summaryrefslogtreecommitdiffstats
path: root/src/utils
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/utils/addftinfo/addftinfo.1.man236
-rw-r--r--src/utils/addftinfo/addftinfo.am35
-rw-r--r--src/utils/addftinfo/addftinfo.cpp237
-rw-r--r--src/utils/addftinfo/guess.cpp489
-rw-r--r--src/utils/addftinfo/guess.h43
-rw-r--r--src/utils/afmtodit/afmtodit.1.man635
-rw-r--r--src/utils/afmtodit/afmtodit.am56
-rw-r--r--src/utils/afmtodit/afmtodit.pl645
-rw-r--r--src/utils/afmtodit/afmtodit.tables6163
-rwxr-xr-xsrc/utils/afmtodit/make-afmtodit-tables139
-rw-r--r--src/utils/grog/grog.1.man628
-rw-r--r--src/utils/grog/grog.am50
-rw-r--r--src/utils/grog/grog.pl721
-rwxr-xr-xsrc/utils/grog/tests/PF-does-not-start-pic-region.sh33
-rwxr-xr-xsrc/utils/grog/tests/avoid-refer-fakeout.sh34
-rw-r--r--src/utils/grog/tests/foo.man146
-rwxr-xr-xsrc/utils/grog/tests/preserve-groff-options.sh30
-rwxr-xr-xsrc/utils/grog/tests/recognize-perl-pod.sh31
-rwxr-xr-xsrc/utils/grog/tests/smoke-test.sh153
-rw-r--r--src/utils/hpftodit/hpftodit.1.man476
-rw-r--r--src/utils/hpftodit/hpftodit.am34
-rw-r--r--src/utils/hpftodit/hpftodit.cpp1465
-rw-r--r--src/utils/hpftodit/hpuni.cpp697
-rw-r--r--src/utils/indxbib/eign133
-rw-r--r--src/utils/indxbib/indxbib.1.man347
-rw-r--r--src/utils/indxbib/indxbib.am57
-rw-r--r--src/utils/indxbib/indxbib.cpp803
-rw-r--r--src/utils/indxbib/signal.c77
-rw-r--r--src/utils/lkbib/lkbib.1.man212
-rw-r--r--src/utils/lkbib/lkbib.am30
-rw-r--r--src/utils/lkbib/lkbib.cpp144
-rw-r--r--src/utils/lookbib/lookbib.1.man166
-rw-r--r--src/utils/lookbib/lookbib.am29
-rw-r--r--src/utils/lookbib/lookbib.cpp146
-rw-r--r--src/utils/pfbtops/pfbtops.1.man129
-rw-r--r--src/utils/pfbtops/pfbtops.am32
-rw-r--r--src/utils/pfbtops/pfbtops.c243
-rw-r--r--src/utils/tfmtodit/tfmtodit.1.man415
-rw-r--r--src/utils/tfmtodit/tfmtodit.am29
-rw-r--r--src/utils/tfmtodit/tfmtodit.cpp889
-rw-r--r--src/utils/xtotroff/xtotroff.1.man237
-rw-r--r--src/utils/xtotroff/xtotroff.am41
-rw-r--r--src/utils/xtotroff/xtotroff.c368
43 files changed, 17703 insertions, 0 deletions
diff --git a/src/utils/addftinfo/addftinfo.1.man b/src/utils/addftinfo/addftinfo.1.man
new file mode 100644
index 0000000..a719136
--- /dev/null
+++ b/src/utils/addftinfo/addftinfo.1.man
@@ -0,0 +1,236 @@
+.TH addftinfo @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+addftinfo \- add font metrics to
+.I troff
+fonts for use with
+.I groff
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_addftinfo_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY addftinfo
+.RB [ \-asc\-height\~\c
+.IR n ]
+.RB [ \-body\-depth\~\c
+.IR n ]
+.RB [ \-body\-height\~\c
+.IR n ]
+.RB [ \-cap\-height\~\c
+.IR n ]
+.RB [ \-comma\-depth\~\c
+.IR n ]
+.RB [ \-desc\-depth\~\c
+.IR n ]
+.RB [ \-fig\-height\~\c
+.IR n ]
+.RB [ \-x\-height\~\c
+.IR n ]
+.I resolution
+.I unit-width
+.I font
+.YS
+.
+.
+.SY addftinfo
+.B \-\-help
+.YS
+.
+.
+.SY addftinfo
+.B \-v
+.
+.SY addftinfo
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I addftinfo
+reads an
+.RI AT&T \~troff
+font description file
+.IR font ,
+adds additional font metric information required by
+.\" We need the "GNU" below because the @g@ prefix might be empty.
+.RI GNU \~@g@troff (@MAN1EXT@),
+and writes the combined result to the standard output.
+.
+The information added is derived from the font's existing parameters and
+assumptions about traditional
+.I troff
+names for characters.
+.
+Among the font metrics added are the heights and depths of characters
+(how far each extends vertically above and below the baseline).
+.
+The
+.I resolution
+and
+.I unit-width
+arguments should be the same as the corresponding parameters in the
+.I DESC
+file.
+.
+.I font
+is the name of the file describing the font;
+if
+.I font
+ends with
+.RB \[lq] I \[rq],
+the font is assumed to be oblique
+(or italic).
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.P
+All other options change parameters that are used to derive the heights
+and depths.
+.
+Like the existing quantities in the font description file,
+each
+.RI value\~ n
+is in
+.I "scaled points,"
+.RI inches/ resolution
+for a font whose type size is
+.IR unit-width ;
+see
+.MR groff_font @MAN5EXT@ .
+.
+.
+.TP
+.BI \-asc\-height \~n
+height of characters with ascenders,
+such as \[lq]b\[rq],
+\[lq]d\[rq],
+or \[lq]l\[rq]
+.
+.
+.TP
+.BI \-body\-depth \~n
+depth of characters such as parentheses
+.
+.
+.TP
+.BI \-body\-height \~n
+height of characters such as parentheses
+.
+.
+.TP
+.BI \-cap\-height \~n
+height of uppercase letters such as \[lq]A\[rq]
+.
+.
+.TP
+.BI \-comma\-depth \~n
+depth of a comma
+.
+.
+.TP
+.BI \-desc\-depth \~n
+depth of characters with descenders,
+such as \[lq]p\[rq],
+\[lq]q\[rq],
+or \[lq]y\[rq]
+.
+.
+.TP
+.B \-fig\-height
+height of figures (numerals)
+.
+.
+.TP
+.BI \-x\-height \~n
+height of lowercase letters without ascenders such as \[lq]x\[rq]
+.
+.
+.P
+.I addftinfo
+makes no attempt to use the specified parameters to infer unspecified
+parameters.
+.
+If a parameter is not specified,
+the default will be used.
+.
+The defaults are chosen to produce reasonable values for a Times font.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR groff_font @MAN5EXT@ ,
+.MR groff @MAN1EXT@ ,
+.MR groff_char @MAN7EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_addftinfo_1_man_C]
+.do rr *groff_addftinfo_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/addftinfo/addftinfo.am b/src/utils/addftinfo/addftinfo.am
new file mode 100644
index 0000000..dd51372
--- /dev/null
+++ b/src/utils/addftinfo/addftinfo.am
@@ -0,0 +1,35 @@
+# Automake rules for 'src utils addftinfo'
+#
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# 'groff' is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# 'groff' is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+#
+########################################################################
+
+bin_PROGRAMS += addftinfo
+man1_MANS += src/utils/addftinfo/addftinfo.1
+EXTRA_DIST += src/utils/addftinfo/addftinfo.1.man
+addftinfo_LDADD = libgroff.a lib/libgnu.a
+addftinfo_SOURCES = \
+ src/utils/addftinfo/addftinfo.cpp \
+ src/utils/addftinfo/guess.cpp \
+ src/utils/addftinfo/guess.h
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/addftinfo/addftinfo.cpp b/src/utils/addftinfo/addftinfo.cpp
new file mode 100644
index 0000000..6f4facf
--- /dev/null
+++ b/src/utils/addftinfo/addftinfo.cpp
@@ -0,0 +1,237 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "guess.h"
+
+extern "C" const char *Version_string;
+
+static void usage(FILE *stream);
+static void usage();
+static void usage(const char *problem);
+static void version();
+static void convert_font(const font_params &, FILE *, FILE *);
+
+typedef int font_params::*param_t;
+
+static struct {
+ const char *name;
+ param_t par;
+} param_table[] = {
+ { "asc-height", &font_params::asc_height },
+ { "body-depth", &font_params::body_depth },
+ { "body-height", &font_params::body_height },
+ { "cap-height", &font_params::cap_height },
+ { "comma-depth", &font_params::comma_depth },
+ { "desc-depth", &font_params::desc_depth },
+ { "fig-height", &font_params::fig_height },
+ { "x-height", &font_params::x_height },
+};
+
+// These are all in thousandths of an em.
+// These values are correct for PostScript Times Roman.
+
+#define DEFAULT_X_HEIGHT 448
+#define DEFAULT_FIG_HEIGHT 676
+#define DEFAULT_ASC_HEIGHT 682
+#define DEFAULT_BODY_HEIGHT 676
+#define DEFAULT_CAP_HEIGHT 662
+#define DEFAULT_COMMA_DEPTH 143
+#define DEFAULT_DESC_DEPTH 217
+#define DEFAULT_BODY_DEPTH 177
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-v") || !strcmp(argv[i],"--version"))
+ version();
+ if (!strcmp(argv[i],"--help")) {
+ usage(stdout);
+ exit(0);
+ }
+ }
+ if (argc < 4)
+ usage("insufficient arguments");
+ /* The next couple of usage() calls cannot provide a meaningful
+ diagnostic because we don't know whether sscanf() failed on a
+ required parameter or an option. A refactor could fix this. */
+ int resolution;
+ if (sscanf(argv[argc-3], "%d", &resolution) != 1)
+ usage();
+ if (resolution <= 0)
+ fatal("resolution must be positive");
+ int unitwidth;
+ if (sscanf(argv[argc-2], "%d", &unitwidth) != 1)
+ usage();
+ if (unitwidth <= 0)
+ fatal("unit width must be positive");
+ font_params param;
+ const char *font = argv[argc-1];
+ param.italic = (font[0] != '\0' && strchr(font, '\0')[-1] == 'I');
+ param.em = (resolution*unitwidth)/72;
+ param.x_height = DEFAULT_X_HEIGHT;
+ param.fig_height = DEFAULT_FIG_HEIGHT;
+ param.asc_height = DEFAULT_ASC_HEIGHT;
+ param.body_height = DEFAULT_BODY_HEIGHT;
+ param.cap_height = DEFAULT_CAP_HEIGHT;
+ param.comma_depth = DEFAULT_COMMA_DEPTH;
+ param.desc_depth = DEFAULT_DESC_DEPTH;
+ param.body_depth = DEFAULT_BODY_DEPTH;
+ for (i = 1; i < argc && argv[i][0] == '-'; i++) {
+ if (argv[i][1] == '-' && argv[i][2] == '\0') {
+ i++;
+ break;
+ }
+ if (i + 1 >= argc)
+ usage("option requires argument");
+ size_t j;
+ for (j = 0;; j++) {
+ if (j >= sizeof(param_table)/sizeof(param_table[0]))
+ fatal("parameter '%1' not recognized", argv[i] + 1);
+ if (strcmp(param_table[j].name, argv[i] + 1) == 0)
+ break;
+ }
+ if (sscanf(argv[i+1], "%d", &(param.*(param_table[j].par))) != 1)
+ fatal("invalid option argument '%1'", argv[i+1]);
+ i++;
+ }
+ if (argc - i != 3)
+ usage("insufficient arguments");
+ errno = 0;
+ FILE *infp = fopen(font, "r");
+ if (infp == 0)
+ fatal("can't open '%1': %2", font, strerror(errno));
+ convert_font(param, infp, stdout);
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s", program_name);
+ size_t len = sizeof(param_table)/sizeof(param_table[0]);
+ for (size_t i = 0; i < len; i++)
+ fprintf(stream, " [-%s n]", param_table[i].name);
+ fputs(" resolution unit-width font\n", stream);
+ fprintf(stream, "usage: %s {-v | --version}\n"
+ "usage: %s --help\n", program_name, program_name);
+}
+
+static void usage()
+{
+ usage(stderr);
+ exit(1);
+}
+
+static void usage(const char *problem)
+{
+ error("%1", problem);
+ usage();
+}
+
+static void version()
+{
+ printf("GNU addftinfo (groff) version %s\n", Version_string);
+ exit(0);
+}
+
+static int get_line(FILE *fp, string *p)
+{
+ int c;
+ p->clear();
+ while ((c = getc(fp)) != EOF) {
+ *p += char(c);
+ if (c == '\n')
+ break;
+ }
+ return p->length() > 0;
+}
+
+static void convert_font(const font_params &param, FILE *infp,
+ FILE *outfp)
+{
+ string s;
+ while (get_line(infp, &s)) {
+ put_string(s, outfp);
+ if (s.length() >= 8
+ && strncmp(&s[0], "charset", 7))
+ break;
+ }
+ while (get_line(infp, &s)) {
+ s += '\0';
+ string name;
+ const char *p = s.contents();
+ while (csspace(*p))
+ p++;
+ while (*p != '\0' && !csspace(*p))
+ name += *p++;
+ while (csspace(*p))
+ p++;
+ for (const char *q = s.contents(); q < p; q++)
+ putc(*q, outfp);
+ char *next;
+ char_metric metric;
+ metric.width = (int)strtol(p, &next, 10);
+ if (next != p) {
+ printf("%d", metric.width);
+ p = next;
+ metric.type = (int)strtol(p, &next, 10);
+ if (next != p) {
+ name += '\0';
+ guess(name.contents(), param, &metric);
+ if (metric.sk == 0) {
+ if (metric.left_ic == 0) {
+ if (metric.ic == 0) {
+ if (metric.depth == 0) {
+ if (metric.height != 0)
+ printf(",%d", metric.height);
+ }
+ else
+ printf(",%d,%d", metric.height, metric.depth);
+ }
+ else
+ printf(",%d,%d,%d", metric.height, metric.depth,
+ metric.ic);
+ }
+ else
+ printf(",%d,%d,%d,%d", metric.height, metric.depth,
+ metric.ic, metric.left_ic);
+ }
+ else
+ printf(",%d,%d,%d,%d,%d", metric.height, metric.depth,
+ metric.ic, metric.left_ic, metric.sk);
+ }
+ }
+ fputs(p, outfp);
+ }
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/addftinfo/guess.cpp b/src/utils/addftinfo/guess.cpp
new file mode 100644
index 0000000..08bbe05
--- /dev/null
+++ b/src/utils/addftinfo/guess.cpp
@@ -0,0 +1,489 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "guess.h"
+
+void guess(const char *s, const font_params &param, char_metric *metric)
+{
+ int &height = metric->height;
+ int &depth = metric->depth;
+
+ metric->ic = 0;
+ metric->left_ic = 0;
+ metric->sk = 0;
+ height = 0;
+ depth = 0;
+ if (s[0] == '\0' || (s[1] != '\0' && s[2] != '\0'))
+ goto do_default;
+#define HASH(c1, c2) (((unsigned char)(c1) << 8) | (unsigned char)(c2))
+ switch (HASH(s[0], s[1])) {
+ default:
+ do_default:
+ if (metric->type & 01)
+ depth = param.desc_depth;
+ if (metric->type & 02)
+ height = param.asc_height;
+ else
+ height = param.x_height;
+ break;
+ case HASH('\\', '|'):
+ case HASH('\\', '^'):
+ case HASH('\\', '&'):
+ // these have zero height and depth
+ break;
+ case HASH('f', 0):
+ height = param.asc_height;
+ if (param.italic)
+ depth = param.desc_depth;
+ break;
+ case HASH('a', 0):
+ case HASH('c', 0):
+ case HASH('e', 0):
+ case HASH('m', 0):
+ case HASH('n', 0):
+ case HASH('o', 0):
+ case HASH('r', 0):
+ case HASH('s', 0):
+ case HASH('u', 0):
+ case HASH('v', 0):
+ case HASH('w', 0):
+ case HASH('x', 0):
+ case HASH('z', 0):
+ height = param.x_height;
+ break;
+ case HASH('i', 0):
+ height = param.x_height;
+ break;
+ case HASH('b', 0):
+ case HASH('d', 0):
+ case HASH('h', 0):
+ case HASH('k', 0):
+ case HASH('l', 0):
+ case HASH('F', 'i'):
+ case HASH('F', 'l'):
+ case HASH('f', 'f'):
+ case HASH('f', 'i'):
+ case HASH('f', 'l'):
+ height = param.asc_height;
+ break;
+ case HASH('t', 0):
+ height = param.asc_height;
+ break;
+ case HASH('g', 0):
+ case HASH('p', 0):
+ case HASH('q', 0):
+ case HASH('y', 0):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('j', 0):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('A', 0):
+ case HASH('B', 0):
+ case HASH('C', 0):
+ case HASH('D', 0):
+ case HASH('E', 0):
+ case HASH('F', 0):
+ case HASH('G', 0):
+ case HASH('H', 0):
+ case HASH('I', 0):
+ case HASH('J', 0):
+ case HASH('K', 0):
+ case HASH('L', 0):
+ case HASH('M', 0):
+ case HASH('N', 0):
+ case HASH('O', 0):
+ case HASH('P', 0):
+ case HASH('Q', 0):
+ case HASH('R', 0):
+ case HASH('S', 0):
+ case HASH('T', 0):
+ case HASH('U', 0):
+ case HASH('V', 0):
+ case HASH('W', 0):
+ case HASH('X', 0):
+ case HASH('Y', 0):
+ case HASH('Z', 0):
+ height = param.cap_height;
+ break;
+ case HASH('*', 'A'):
+ case HASH('*', 'B'):
+ case HASH('*', 'C'):
+ case HASH('*', 'D'):
+ case HASH('*', 'E'):
+ case HASH('*', 'F'):
+ case HASH('*', 'G'):
+ case HASH('*', 'H'):
+ case HASH('*', 'I'):
+ case HASH('*', 'K'):
+ case HASH('*', 'L'):
+ case HASH('*', 'M'):
+ case HASH('*', 'N'):
+ case HASH('*', 'O'):
+ case HASH('*', 'P'):
+ case HASH('*', 'Q'):
+ case HASH('*', 'R'):
+ case HASH('*', 'S'):
+ case HASH('*', 'T'):
+ case HASH('*', 'U'):
+ case HASH('*', 'W'):
+ case HASH('*', 'X'):
+ case HASH('*', 'Y'):
+ case HASH('*', 'Z'):
+ height = param.cap_height;
+ break;
+ case HASH('0', 0):
+ case HASH('1', 0):
+ case HASH('2', 0):
+ case HASH('3', 0):
+ case HASH('4', 0):
+ case HASH('5', 0):
+ case HASH('6', 0):
+ case HASH('7', 0):
+ case HASH('8', 0):
+ case HASH('9', 0):
+ case HASH('1', '2'):
+ case HASH('1', '4'):
+ case HASH('3', '4'):
+ height = param.fig_height;
+ break;
+ case HASH('(', 0):
+ case HASH(')', 0):
+ case HASH('[', 0):
+ case HASH(']', 0):
+ case HASH('{', 0):
+ case HASH('}', 0):
+ height = param.body_height;
+ depth = param.body_depth;
+ break;
+ case HASH('i', 's'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+ case HASH('*', 'a'):
+ case HASH('*', 'e'):
+ case HASH('*', 'i'):
+ case HASH('*', 'k'):
+ case HASH('*', 'n'):
+ case HASH('*', 'o'):
+ case HASH('*', 'p'):
+ case HASH('*', 's'):
+ case HASH('*', 't'):
+ case HASH('*', 'u'):
+ case HASH('*', 'w'):
+ height = param.x_height;
+ break;
+ case HASH('*', 'd'):
+ case HASH('*', 'l'):
+ height = param.asc_height;
+ break;
+ case HASH('*', 'g'):
+ case HASH('*', 'h'):
+ case HASH('*', 'm'):
+ case HASH('*', 'r'):
+ case HASH('*', 'x'):
+ case HASH('*', 'y'):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('*', 'b'):
+ case HASH('*', 'c'):
+ case HASH('*', 'f'):
+ case HASH('*', 'q'):
+ case HASH('*', 'z'):
+ height = param.asc_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('t', 's'):
+ height = param.x_height;
+ depth = param.desc_depth;
+ break;
+ case HASH('!', 0):
+ case HASH('?', 0):
+ case HASH('"', 0):
+ case HASH('#', 0):
+ case HASH('$', 0):
+ case HASH('%', 0):
+ case HASH('&', 0):
+ case HASH('*', 0):
+ case HASH('+', 0):
+ height = param.asc_height;
+ break;
+ case HASH('`', 0):
+ case HASH('\'', 0):
+ height = param.asc_height;
+ break;
+ case HASH('~', 0):
+ case HASH('^', 0):
+ case HASH('a', 'a'):
+ case HASH('g', 'a'):
+ height = param.asc_height;
+ break;
+ case HASH('r', 'u'):
+ case HASH('.', 0):
+ break;
+ case HASH(',', 0):
+ depth = param.comma_depth;
+ break;
+ case HASH('m', 'i'):
+ case HASH('-', 0):
+ case HASH('h', 'y'):
+ case HASH('e', 'm'):
+ height = param.x_height;
+ break;
+ case HASH(':', 0):
+ height = param.x_height;
+ break;
+ case HASH(';', 0):
+ height = param.x_height;
+ depth = param.comma_depth;
+ break;
+ case HASH('=', 0):
+ case HASH('e', 'q'):
+ height = param.x_height;
+ break;
+ case HASH('<', 0):
+ case HASH('>', 0):
+ case HASH('>', '='):
+ case HASH('<', '='):
+ case HASH('@', 0):
+ case HASH('/', 0):
+ case HASH('|', 0):
+ case HASH('\\', 0):
+ height = param.asc_height;
+ break;
+ case HASH('_', 0):
+ case HASH('u', 'l'):
+ case HASH('\\', '_'):
+ depth = param.em/4;
+ break;
+ case HASH('r', 'n'):
+ height = (param.em*3)/4;
+ break;
+ case HASH('s', 'r'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+ case HASH('b', 'u'):
+ case HASH('s', 'q'):
+ case HASH('d', 'e'):
+ case HASH('d', 'g'):
+ case HASH('f', 'm'):
+ case HASH('c', 't'):
+ case HASH('r', 'g'):
+ case HASH('c', 'o'):
+ case HASH('p', 'l'):
+ case HASH('*', '*'):
+ case HASH('s', 'c'):
+ case HASH('s', 'l'):
+ case HASH('=', '='):
+ case HASH('~', '='):
+ case HASH('a', 'p'):
+ case HASH('!', '='):
+ case HASH('-', '>'):
+ case HASH('<', '-'):
+ case HASH('u', 'a'):
+ case HASH('d', 'a'):
+ case HASH('m', 'u'):
+ case HASH('d', 'i'):
+ case HASH('+', '-'):
+ case HASH('c', 'u'):
+ case HASH('c', 'a'):
+ case HASH('s', 'b'):
+ case HASH('s', 'p'):
+ case HASH('i', 'b'):
+ case HASH('i', 'p'):
+ case HASH('i', 'f'):
+ case HASH('p', 'd'):
+ case HASH('g', 'r'):
+ case HASH('n', 'o'):
+ case HASH('p', 't'):
+ case HASH('e', 's'):
+ case HASH('m', 'o'):
+ case HASH('b', 'r'):
+ case HASH('d', 'd'):
+ case HASH('r', 'h'):
+ case HASH('l', 'h'):
+ case HASH('o', 'r'):
+ case HASH('c', 'i'):
+ height = param.asc_height;
+ break;
+ case HASH('l', 't'):
+ case HASH('l', 'b'):
+ case HASH('r', 't'):
+ case HASH('r', 'b'):
+ case HASH('l', 'k'):
+ case HASH('r', 'k'):
+ case HASH('b', 'v'):
+ case HASH('l', 'f'):
+ case HASH('r', 'f'):
+ case HASH('l', 'c'):
+ case HASH('r', 'c'):
+ height = (param.em*3)/4;
+ depth = param.em/4;
+ break;
+#if 0
+ case HASH('%', '0'):
+ case HASH('-', '+'):
+ case HASH('-', 'D'):
+ case HASH('-', 'd'):
+ case HASH('-', 'd'):
+ case HASH('-', 'h'):
+ case HASH('.', 'i'):
+ case HASH('.', 'j'):
+ case HASH('/', 'L'):
+ case HASH('/', 'O'):
+ case HASH('/', 'l'):
+ case HASH('/', 'o'):
+ case HASH('=', '~'):
+ case HASH('A', 'E'):
+ case HASH('A', 'h'):
+ case HASH('A', 'N'):
+ case HASH('C', 's'):
+ case HASH('D', 'o'):
+ case HASH('F', 'c'):
+ case HASH('F', 'o'):
+ case HASH('I', 'J'):
+ case HASH('I', 'm'):
+ case HASH('O', 'E'):
+ case HASH('O', 'f'):
+ case HASH('O', 'K'):
+ case HASH('O', 'm'):
+ case HASH('O', 'R'):
+ case HASH('P', 'o'):
+ case HASH('R', 'e'):
+ case HASH('S', '1'):
+ case HASH('S', '2'):
+ case HASH('S', '3'):
+ case HASH('T', 'P'):
+ case HASH('T', 'p'):
+ case HASH('Y', 'e'):
+ case HASH('\\', '-'):
+ case HASH('a', '"'):
+ case HASH('a', '-'):
+ case HASH('a', '.'):
+ case HASH('a', '^'):
+ case HASH('a', 'b'):
+ case HASH('a', 'c'):
+ case HASH('a', 'd'):
+ case HASH('a', 'e'):
+ case HASH('a', 'h'):
+ case HASH('a', 'o'):
+ case HASH('a', 't'):
+ case HASH('a', '~'):
+ case HASH('b', 'a'):
+ case HASH('b', 'b'):
+ case HASH('b', 's'):
+ case HASH('c', '*'):
+ case HASH('c', '+'):
+ case HASH('f', '/'):
+ case HASH('f', 'a'):
+ case HASH('f', 'c'):
+ case HASH('f', 'o'):
+ case HASH('h', 'a'):
+ case HASH('h', 'o'):
+ case HASH('i', 'j'):
+ case HASH('l', 'A'):
+ case HASH('l', 'B'):
+ case HASH('l', 'C'):
+ case HASH('m', 'd'):
+ case HASH('n', 'c'):
+ case HASH('n', 'e'):
+ case HASH('n', 'm'):
+ case HASH('o', 'A'):
+ case HASH('o', 'a'):
+ case HASH('o', 'e'):
+ case HASH('o', 'q'):
+ case HASH('p', 'l'):
+ case HASH('p', 'p'):
+ case HASH('p', 's'):
+ case HASH('r', '!'):
+ case HASH('r', '?'):
+ case HASH('r', 'A'):
+ case HASH('r', 'B'):
+ case HASH('r', 'C'):
+ case HASH('r', 's'):
+ case HASH('s', 'h'):
+ case HASH('s', 's'):
+ case HASH('t', 'e'):
+ case HASH('t', 'f'):
+ case HASH('t', 'i'):
+ case HASH('t', 'm'):
+ case HASH('~', '~'):
+ case HASH('v', 'S'):
+ case HASH('v', 'Z'):
+ case HASH('v', 's'):
+ case HASH('v', 'z'):
+ case HASH('^', 'A'):
+ case HASH('^', 'E'):
+ case HASH('^', 'I'):
+ case HASH('^', 'O'):
+ case HASH('^', 'U'):
+ case HASH('^', 'a'):
+ case HASH('^', 'e'):
+ case HASH('^', 'i'):
+ case HASH('^', 'o'):
+ case HASH('^', 'u'):
+ case HASH('`', 'A'):
+ case HASH('`', 'E'):
+ case HASH('`', 'I'):
+ case HASH('`', 'O'):
+ case HASH('`', 'U'):
+ case HASH('`', 'a'):
+ case HASH('`', 'e'):
+ case HASH('`', 'i'):
+ case HASH('`', 'o'):
+ case HASH('`', 'u'):
+ case HASH('~', 'A'):
+ case HASH('~', 'N'):
+ case HASH('~', 'O'):
+ case HASH('~', 'a'):
+ case HASH('~', 'n'):
+ case HASH('~', 'o'):
+ case HASH('\'', 'A'):
+ case HASH('\'', 'C'):
+ case HASH('\'', 'E'):
+ case HASH('\'', 'I'):
+ case HASH('\'', 'O'):
+ case HASH('\'', 'U'):
+ case HASH('\'', 'a'):
+ case HASH('\'', 'c'):
+ case HASH('\'', 'e'):
+ case HASH('\'', 'i'):
+ case HASH('\'', 'o'):
+ case HASH('\'', 'u')
+ case HASH(':', 'A'):
+ case HASH(':', 'E'):
+ case HASH(':', 'I'):
+ case HASH(':', 'O'):
+ case HASH(':', 'U'):
+ case HASH(':', 'Y'):
+ case HASH(':', 'a'):
+ case HASH(':', 'e'):
+ case HASH(':', 'i'):
+ case HASH(':', 'o'):
+ case HASH(':', 'u'):
+ case HASH(':', 'y'):
+ case HASH(',', 'C'):
+ case HASH(',', 'c'):
+#endif
+ }
+}
diff --git a/src/utils/addftinfo/guess.h b/src/utils/addftinfo/guess.h
new file mode 100644
index 0000000..d763fe0
--- /dev/null
+++ b/src/utils/addftinfo/guess.h
@@ -0,0 +1,43 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+struct font_params {
+ int italic;
+ int em;
+ int x_height;
+ int fig_height;
+ int cap_height;
+ int asc_height;
+ int body_height;
+ int comma_depth;
+ int desc_depth;
+ int body_depth;
+};
+
+struct char_metric {
+ int width;
+ int type;
+ int height;
+ int depth;
+ int ic;
+ int left_ic;
+ int sk;
+};
+
+void guess(const char *s, const font_params &param, char_metric *metric);
diff --git a/src/utils/afmtodit/afmtodit.1.man b/src/utils/afmtodit/afmtodit.1.man
new file mode 100644
index 0000000..7b0a39f
--- /dev/null
+++ b/src/utils/afmtodit/afmtodit.1.man
@@ -0,0 +1,635 @@
+.TH afmtodit @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+afmtodit \- adapt Adobe Font Metrics files for
+.I groff
+PostScript and PDF output
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_afmtodit_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY afmtodit
+.RB [ \-ckmnsx ]
+.RB [ \-a\~\c
+.IR slant ]
+.RB [ \-d\~\c
+.IR device-description-file ]
+.RB [ \-e\~\c
+.IR encoding-file ]
+.RB [ \-f\~\c
+.IR internal-name ]
+.RB [ \-i\~\c
+.IR italic-correction-factor ]
+.RB [ \-o\~\c
+.IR output-file ]
+.RB [ \-w\~\c
+.IR space-width ]
+.I afm-file
+.I map-file
+.I font-description-file
+.YS
+.
+.
+.SY afmtodit
+.B \-\-help
+.YS
+.
+.
+.SY afmtodit
+.B \-v
+.
+.SY afmtodit
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I \%afmtodit
+adapts an
+Adobe Font Metric
+file,
+.IR afm-file ,
+for use with the
+.B ps
+and
+.B pdf
+output devices of
+.MR @g@troff @MAN1EXT@ .
+.
+.I map-file
+associates a
+.I groff
+ordinary or special character name with a PostScript glyph name.
+.
+Output is written in
+.MR groff_font @MAN5EXT@
+format to
+.I font-description-file,
+a file named for the intended
+.I groff
+font name
+(but see the
+.B \-o
+option).
+.
+.
+.LP
+.I map-file
+should contain a sequence of lines of the form
+.
+.RS
+.EX
+.I ps-glyph groff-char
+.EE
+.RE
+.
+where
+.I ps-glyph
+is the PostScript glyph name and
+.I groff-char
+is a
+.I groff
+ordinary
+(if of unit length)
+or special
+(if longer)
+character identifier.
+.
+The same
+.I ps-glyph
+can occur multiple times in the file;
+each
+.I groff-char
+must occur at most once.
+.
+Lines starting with \[lq]#\[rq] and blank lines are ignored.
+.
+If the file isn't found in the current directory,
+it is sought in the
+.I devps/generate
+subdirectory of the default font directory.
+.
+.
+.LP
+If a PostScript glyph is not mentioned in
+.IR map-file ,
+and a
+.I groff
+character name can't be deduced using the Adobe Glyph List
+(AGL,
+built into
+.IR afmtodit ),
+then
+.I \%afmtodit
+puts the PostScript glyph into the
+.I groff
+font description file as an unnamed glyph which can only be accessed
+by the \[lq]\eN\[rq] escape sequence in a
+.I roff
+document.
+.
+In particular,
+this is true for glyph variants named in the form
+.RI \[lq] foo . bar \[rq];
+all glyph names containing one or more periods are mapped to unnamed
+entities.
+.
+Unless
+.B \-e
+is specified,
+the encoding defined in the AFM file
+(i.e.,
+entries
+with non-negative codes)
+is used.
+.
+Refer to section \[lq]Using Symbols\[rq] in
+.IR "Groff: The GNU Implementation of troff" ,
+the
+.I groff
+Texinfo manual,
+or
+.MR groff_char @MAN7EXT@ ,
+which describe how
+.I groff
+character identifiers are constructed.
+.
+.
+.LP
+Glyphs not encoded in the AFM file
+(i.e.,
+entries indexed as \[lq]\-1\[rq])
+are still available in
+.IR groff ;
+they get glyph index values greater than 255
+(or greater than the biggest code used in the AFM file in the unlikely
+case that it is greater than 255)
+in the
+.I groff
+font description file.
+.
+Unencoded glyph indices don't have a specific order;
+it is best to access them only via special character identifiers.
+.
+.
+.P
+If the font file proper
+(not just its metrics)
+is available,
+listing it in the files
+.I @FONTDIR@/\:\%devps/\:\%download
+and
+.I @FONTDIR@/\:\%devpdf/\:\%download
+enables it to be embedded in the output produced by
+.MR grops @MAN1EXT@
+and
+.MR gropdf @MAN1EXT@ ,
+respectively.
+.
+.
+.P
+If the
+.B \-i
+option is used,
+.I \%afmtodit
+automatically generates an italic correction,
+a left italic correction,
+and a subscript correction for each glyph
+(the significance of these is explained in
+.MR groff_font @MAN5EXT@ );
+they can be specified for individual glyphs by
+adding to the
+.I afm-file
+lines of the form:
+.
+.RS
+.EX
+.RI italicCorrection \~ps-glyph\~n
+.RI leftItalicCorrection \~ps-glyph\~n
+.RI subscriptCorrection \~ps-glyph\~n
+.EE
+.RE
+.
+where
+.I ps-glyph
+is the PostScript glyph name,
+and
+.I n
+is the desired value of the corresponding parameter in thousandths of an
+em.
+.
+Such parameters are normally needed only for italic
+(or oblique)
+fonts.
+.
+.
+.P
+The
+.B \-s
+option should be given if the font is \[lq]special\[rq],
+meaning that
+.I groff
+should search it whenever a glyph is not found in the current font.
+.
+In that case,
+.I font-description-file
+should be listed as an argument to the
+.B fonts
+directive in the output device's
+.I DESC
+file;
+if it is not special,
+there is no need to do so,
+since
+.MR @g@troff @MAN1EXT@
+will automatically mount it when it is first used.
+.
+.
+.br
+.ne 7v
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \%\-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-a\~ slant
+Use
+.I slant
+as the slant (\[lq]angle\[rq]) parameter in the font description file;
+this is used by
+.I groff
+in the positioning of accents.
+.
+By default
+.I \%afmtodit
+uses the negative of the
+.B \%ItalicAngle
+specified in the AFM file;
+with true italic fonts it is sometimes desirable to use a slant that is
+less than this.
+.
+If you find that an italic font places accents over base glyphs
+too far to the right,
+use
+.B \-a
+to give it a smaller slant.
+.
+.
+.TP
+.B \-c
+Include comments in the font description file identifying the PostScript
+font.
+.
+.
+.TP
+.BI \-d\~ device-description-file
+The device description file is
+.I desc-file
+rather than the default
+.IR DESC .
+.
+If not found in the current directory,
+the
+.I devps
+subdirectory of the default font directory is searched
+(this is true for both the default device description file and a file
+given with option
+.BR \-d ).
+.
+.
+.TP
+.BI \-e\~ encoding-file
+The PostScript font should be reencoded to use the encoding described
+in
+.IR enc-file .
+.
+The format of
+.I enc-file
+is described in
+.MR grops @MAN1EXT@ .
+.
+If not found in the current directory,
+the
+.I devps
+subdirectory of the default font directory is searched.
+.
+.
+.TP
+.BI \-f\~ internal-name
+The internal name of the
+.I groff
+font is set to
+.IR name .
+.
+.
+.TP
+.BI \-i\~ italic-correction-factor
+Generate an italic correction for each glyph so that its width plus its
+italic correction is equal to
+.I italic-correction-factor
+thousandths of an em
+plus the amount by which the right edge of the glyph's bounding box is
+to the right of its origin.
+.
+If this would result in a negative italic correction,
+use a zero italic correction instead.
+.
+.
+.IP
+Also generate a subscript correction equal to the
+product of the tangent of the slant of the font and
+four fifths of the x-height of the font.
+.
+If this would result in a subscript correction greater than the italic
+correction,
+use a subscript correction equal to the italic correction instead.
+.
+.
+.IP
+Also generate a left italic correction for each glyph equal to
+.I italic-correction-factor
+thousandths of an em
+plus the amount by which the left edge of the glyph's bounding box is to
+the left of its origin.
+.
+The left italic correction may be negative unless option
+.B \-m
+is given.
+.
+.
+.IP
+This option is normally needed only with italic
+(or oblique)
+fonts.
+.
+The font description files distributed with
+.I groff
+were created using an option of
+.B \-i50
+for italic fonts.
+.
+.
+.TP
+.BI \-o\~ output-file
+Write to
+.I output-file
+instead of
+.I font-description-file.
+.
+.
+.TP
+.B \-k
+Omit any kerning data from the
+.I groff
+font;
+use only for monospaced (constant-width) fonts.
+.
+.
+.TP
+.B \-m
+Prevent negative left italic correction values.
+.
+Font description files for roman styles distributed with
+.I groff
+were created with
+.RB \[lq] \-i0\~\-m \[rq]
+to improve spacing with
+.MR @g@eqn @MAN1EXT@ .
+.
+.
+.TP
+.B \-n
+Don't output a
+.B ligatures
+command for this font;
+use with monospaced (constant-width) fonts.
+.
+.
+.TP
+.B \-s
+Add the
+.B special
+directive to the font description file.
+.
+.
+.TP
+.BI \-w\~ space-width
+Use
+.I space-width
+as the with of inter-word spaces.
+.
+.
+.TP
+.B \-x
+Don't use the built-in Adobe Glyph List.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.I @FONTDIR@/\:\%devps/\:DESC
+describes the
+.B ps
+output device.
+.
+.
+.TP
+.IR @FONTDIR@/\:\%devps/ F
+describes the font known
+.RI as\~ F
+on device
+.BR ps .
+.
+.
+.TP
+.I @FONTDIR@/\:\%devps/\:\%download
+lists fonts available for embedding within the PostScript document
+(or download to the device).
+.
+.
+.TP
+.I @FONTDIR@/\:\%devps/\:\%generate/\:\%dingbats.map
+.TQ
+.I @FONTDIR@/\:\%devps/\:\%generate/\:\%dingbats\-reversed.map
+.TQ
+.I @FONTDIR@/\:\%devps/\:\%generate/\:\%slanted\-symbol.map
+.TQ
+.I @FONTDIR@/\:\%devps/\:\%generate/\:\%symbol.map
+.TQ
+.I @FONTDIR@/\:\%devps/\:\%generate/\:\%text.map
+map names in the Adobe Glyph List to
+.I groff
+special character identifiers for Zapf Dingbats
+.RB ( ZD ),
+reversed Zapf Dingbats
+.RB ( ZDR ),
+slanted symbol
+.RB ( SS ),
+symbol
+.RB ( S ),
+and text fonts,
+respectively.
+.
+These
+.IR map-file s
+are used to produce the font description files provided with
+.I groff
+for the
+.I \%grops
+output driver.
+.
+.
+.\" ====================================================================
+.SH Diagnostics
+.\" ====================================================================
+.
+.TP
+.RI "AGL name \[aq]" x "\[aq] already mapped to groff name \[aq]" y\c
+.RI "\[aq]; ignoring AGL name \[aq]uni" XXXX \[aq]
+You can disregard these if they're in the form shown,
+where the ignored AGL name contains four hexadecimal digits
+.IR XXXX .
+.
+The Adobe Glyph List (AGL) has its own names for glyphs;
+they are often
+different from
+.IR groff 's
+special character names.
+.
+.I \%afmtodit
+is constructing a mapping from
+.I groff
+special character names to AGL names;
+this can be a one-to-one or many-to-one mapping,
+but one-to-many will not work,
+so
+.I \%afmtodit
+discards the excess mappings.
+.
+For example,
+if
+.I x
+is
+.BR *D ,
+.I y
+is
+.BR \%Delta ,
+and
+.I z
+is
+.BR uni0394 ,
+.I \%afmtodit
+is telling you that the
+.I groff
+font description that it is writing cannot map the
+.I groff
+special character
+.B \[rs][*D]
+to AGL glyphs
+.B \%Delta
+and
+.B uni0394
+at the same time.
+.
+.
+.IP
+If you get a message like this but are unhappy with which mapping is
+ignored,
+a remedy is to craft an alternative
+.I map-file
+and re-run
+.I \%afmtodit
+using it.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.IR "Groff: The GNU Implementation of troff" ,
+by Trent A.\& Fisher and Werner Lemberg,
+is the primary
+.I groff
+manual.
+.
+Section \[lq]Using Symbols\[rq] may be of particular note.
+.
+You can browse it interactively with \[lq]info \[aq](groff)Using
+\%Symbols\[aq]\[rq].
+.
+.
+.LP
+.MR groff @MAN1EXT@ ,
+.MR gropdf @MAN1EXT@ ,
+.MR grops @MAN1EXT@ ,
+.MR groff_font @MAN5EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_afmtodit_1_man_C]
+.do rr *groff_afmtodit_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/afmtodit/afmtodit.am b/src/utils/afmtodit/afmtodit.am
new file mode 100644
index 0000000..fda095d
--- /dev/null
+++ b/src/utils/afmtodit/afmtodit.am
@@ -0,0 +1,56 @@
+# Automake rules for 'src utils afmtodit'
+#
+# Copyright (C) 2013-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+########################################################################
+
+afmtodit_srcdir = $(top_srcdir)/src/utils/afmtodit
+
+bin_SCRIPTS += afmtodit
+man1_MANS += src/utils/afmtodit/afmtodit.1
+EXTRA_DIST += \
+ src/utils/afmtodit/afmtodit.1.man \
+ src/utils/afmtodit/afmtodit.pl \
+ src/utils/afmtodit/afmtodit.tables \
+ src/utils/afmtodit/make-afmtodit-tables
+
+afmtodit: $(afmtodit_srcdir)/afmtodit.pl $(afmtodit_srcdir)/afmtodit.tables
+ $(AM_V_GEN)if test -n "$(PERL)"; then \
+ sed -e "s|[@]PERL[@]|$(PERL)|" \
+ -e "s|[@]VERSION[@]|$(VERSION)|" \
+ -e "s|[@]FONTDIR[@]|$(fontdir)|" \
+ -e "/[@]afmtodit.tables[@]/ r $(afmtodit_srcdir)/afmtodit.tables" \
+ -e "/[@]afmtodit.tables[@]/ d" \
+ $(afmtodit_srcdir)/afmtodit.pl \
+ >afmtodit; \
+ else \
+ sed -e "s|[@]VERSION[@]|$(VERSION)|" \
+ -e "s|[@]FONTDIR[@]|$(fontdir)|" \
+ -e "/[@]afmtodit.tables[@]/ r $(afmtodit_srcdir)/afmtodit.tables" \
+ -e "/[@]afmtodit.tables[@]/ d" \
+ $(afmtodit_srcdir)/afmtodit.pl \
+ >afmtodit; \
+ fi \
+ && chmod +x afmtodit
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/afmtodit/afmtodit.pl b/src/utils/afmtodit/afmtodit.pl
new file mode 100644
index 0000000..c6b67cc
--- /dev/null
+++ b/src/utils/afmtodit/afmtodit.pl
@@ -0,0 +1,645 @@
+#!@PERL@
+# Copyright (C) 1989-2020 Free Software Foundation, Inc.
+# Written by James Clark (jjc@jclark.com)
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+use warnings;
+use strict;
+
+@afmtodit.tables@
+
+my $prog = $0;
+my $groff_sys_fontdir = "@FONTDIR@";
+my $want_help;
+my $space_width = 0;
+
+our ($opt_a, $opt_c, $opt_d, $opt_e, $opt_f, $opt_i, $opt_k,
+ $opt_m, $opt_n, $opt_o, $opt_s, $opt_v, $opt_x);
+
+use Getopt::Long qw(:config gnu_getopt);
+GetOptions( "a=s", "c", "d=s", "e=s", "f=s", "i=s", "k", "m", "n",
+ "o=s", "s", "v", "w=i" => \$space_width, "x", "version" => \$opt_v,
+ "help" => \$want_help
+);
+
+my $afmtodit_version = "GNU afmtodit (groff) version @VERSION@";
+
+if ($opt_v) {
+ print "$afmtodit_version\n";
+ exit 0;
+}
+
+sub croak {
+ my $msg = shift;
+ print STDERR "$prog: error: $msg";
+ exit(1);
+}
+
+sub usage {
+ my $stream = *STDOUT;
+ my $had_error = shift;
+ $stream = *STDERR if $had_error;
+ print $stream "usage: $prog [-ckmnsx] [-a slant]" .
+ " [-d device-description-file] [-e encoding-file]" .
+ " [-f internal-name] [-i italic-correction-factor]" .
+ " [-o output-file] [-w space-width] afm-file map-file" .
+ " font-description-file\n" .
+ "usage: $prog {-v | --version}\n" .
+ "usage: $prog --help\n";
+ unless ($had_error) {
+ print $stream "\n" .
+"Adapt an Adobe Font Metric file, afm-file, for use with the 'ps'\n" .
+"and 'pdf' output devices of groff(1). See the afmtodit(1) manual " .
+"page.\n";
+ }
+ my $status = 0;
+ $status = 2 if ($had_error);
+ exit($status);
+}
+
+&usage(0) if ($want_help);
+
+if ($#ARGV != 2) {
+ print STDERR "$prog: usage error: insufficient arguments\n";
+ &usage(1);
+}
+
+my $afm = $ARGV[0];
+my $map = $ARGV[1];
+my $fontfile = $ARGV[2];
+my $outfile = $opt_o || $fontfile;
+my $desc = $opt_d || "DESC";
+my $sys_map = $groff_sys_fontdir . "/devps/generate/" . $map;
+my $sys_desc = $groff_sys_fontdir . "/devps/" . $desc;
+
+# read the afm file
+
+my $psname;
+my ($notice, $version, $fullname, $familyname, @comments);
+my $italic_angle = 0;
+my (@kern1, @kern2, @kernx);
+my (%italic_correction, %left_italic_correction);
+my %subscript_correction;
+# my %ligs
+my %ligatures;
+my (@encoding, %in_encoding);
+my (%width, %height, %depth);
+my (%left_side_bearing, %right_side_bearing);
+
+open(AFM, $afm) || croak("unable to open '$ARGV[0]': $!\n");
+
+while (<AFM>) {
+ chomp;
+ s/\x0D$//;
+ my @field = split(' ');
+ next if $#field < 0;
+ if ($field[0] eq "FontName") {
+ $psname = $field[1];
+ if($opt_f) {
+ $psname = $opt_f;
+ }
+ }
+ elsif($field[0] eq "Notice") {
+ $notice = $_;
+ }
+ elsif($field[0] eq "Version") {
+ $version = $_;
+ }
+ elsif($field[0] eq "FullName") {
+ $fullname = $_;
+ }
+ elsif($field[0] eq "FamilyName") {
+ $familyname = $_;
+ }
+ elsif($field[0] eq "Comment") {
+ push(@comments, $_);
+ }
+ elsif($field[0] eq "ItalicAngle") {
+ $italic_angle = -$field[1];
+ }
+ elsif ($field[0] eq "KPX") {
+ if ($#field == 3) {
+ push(@kern1, $field[1]);
+ push(@kern2, $field[2]);
+ push(@kernx, $field[3]);
+ }
+ }
+ elsif ($field[0] eq "italicCorrection") {
+ $italic_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "leftItalicCorrection") {
+ $left_italic_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "subscriptCorrection") {
+ $subscript_correction{$field[1]} = $field[2];
+ }
+ elsif ($field[0] eq "StartCharMetrics") {
+ while (<AFM>) {
+ @field = split(' ');
+ next if $#field < 0;
+ last if ($field[0] eq "EndCharMetrics");
+ if ($field[0] eq "C") {
+ my $w;
+ my $wx = 0;
+ my $n = "";
+# %ligs = ();
+ my $lly = 0;
+ my $ury = 0;
+ my $llx = 0;
+ my $urx = 0;
+ my $c = $field[1];
+ my $i = 2;
+ while ($i <= $#field) {
+ if ($field[$i] eq "WX") {
+ $w = $field[$i + 1];
+ $i += 2;
+ }
+ elsif ($field[$i] eq "N") {
+ $n = $field[$i + 1];
+ $i += 2;
+ }
+ elsif ($field[$i] eq "B") {
+ $llx = $field[$i + 1];
+ $lly = $field[$i + 2];
+ $urx = $field[$i + 3];
+ $ury = $field[$i + 4];
+ $i += 5;
+ }
+# elsif ($field[$i] eq "L") {
+# $ligs{$field[$i + 2]} = $field[$i + 1];
+# $i += 3;
+# }
+ else {
+ while ($i <= $#field && $field[$i] ne ";") {
+ $i++;
+ }
+ $i++;
+ }
+ }
+ if (!$opt_e && $c != -1) {
+ $encoding[$c] = $n;
+ $in_encoding{$n} = 1;
+ }
+ $width{$n} = $w;
+ $height{$n} = $ury;
+ $depth{$n} = -$lly;
+ $left_side_bearing{$n} = -$llx;
+ $right_side_bearing{$n} = $urx - $w;
+# foreach my $lig (sort keys %ligs) {
+# $ligatures{$lig} = $n . " " . $ligs{$lig};
+# }
+ }
+ }
+ }
+}
+close(AFM);
+
+# read the DESC file
+
+my ($sizescale, $resolution, $unitwidth);
+$sizescale = 1;
+
+open(DESC, $desc) || open(DESC, $sys_desc) ||
+ croak("unable to open '$desc' or '$sys_desc': $!\n");
+while (<DESC>) {
+ next if /^#/;
+ chop;
+ my @field = split(' ');
+ next if $#field < 0;
+ last if $field[0] eq "charset";
+ if ($field[0] eq "res") {
+ $resolution = $field[1];
+ }
+ elsif ($field[0] eq "unitwidth") {
+ $unitwidth = $field[1];
+ }
+ elsif ($field[0] eq "sizescale") {
+ $sizescale = $field[1];
+ }
+}
+close(DESC);
+
+if ($opt_e) {
+ # read the encoding file
+
+ my $sys_opt_e = $groff_sys_fontdir . "/devps/" . $opt_e;
+ open(ENCODING, $opt_e) || open(ENCODING, $sys_opt_e) ||
+ croak("unable to open '$opt_e' or '$sys_opt_e': $!\n");
+ while (<ENCODING>) {
+ next if /^#/;
+ chop;
+ my @field = split(' ');
+ next if $#field < 0;
+ if ($#field == 1) {
+ if ($field[1] >= 0 && defined $width{$field[0]}) {
+ $encoding[$field[1]] = $field[0];
+ $in_encoding{$field[0]} = 1;
+ }
+ }
+ }
+ close(ENCODING);
+}
+
+# read the map file
+
+my (%nmap, %map);
+
+open(MAP, $map) || open(MAP, $sys_map) ||
+ croak("unable to open '$map' or '$sys_map': $!\n");
+while (<MAP>) {
+ next if /^#/;
+ chop;
+ my @field = split(' ');
+ next if $#field < 0;
+ if ($#field == 1) {
+ if ($field[1] eq "space") {
+ # The PostScript character "space" is automatically mapped
+ # to the groff character "space"; this is for grops.
+ warn "$prog: you are not allowed to map to " .
+ "the groff character 'space'";
+ }
+ elsif ($field[0] eq "space") {
+ warn "$prog: you are not allowed to map " .
+ "the PostScript character 'space'";
+ }
+ else {
+ $nmap{$field[0]} += 0;
+ $map{$field[0], $nmap{$field[0]}} = $field[1];
+ $nmap{$field[0]} += 1;
+
+ # There is more than one way to make a PS glyph name;
+ # let us try Unicode names with both 'uni' and 'u' prefixes.
+ my $utmp = $AGL_to_unicode{$field[0]};
+ if (defined $utmp && $utmp =~ /^[0-9A-F]{4}$/) {
+ foreach my $unicodepsname ("uni" . $utmp, "u" . $utmp) {
+ $nmap{$unicodepsname} += 0;
+ $map{$unicodepsname, $nmap{$unicodepsname}} = $field[1];
+ $nmap{$unicodepsname} += 1;
+ }
+ }
+ }
+ }
+}
+close(MAP);
+
+$italic_angle = $opt_a if $opt_a;
+
+
+if (!$opt_x) {
+ my %mapped;
+ my $i = ($#encoding > 256) ? ($#encoding + 1) : 256;
+ foreach my $ch (sort keys %width) {
+ # add unencoded characters
+ if (!$in_encoding{$ch}) {
+ $encoding[$i] = $ch;
+ $i++;
+ }
+ if ($nmap{$ch}) {
+ for (my $j = 0; $j < $nmap{$ch}; $j++) {
+ if (defined $mapped{$map{$ch, $j}}) {
+ print STDERR "$prog: AGL name"
+ . " '$mapped{$map{$ch, $j}}' already mapped to"
+ . " groff name '$map{$ch, $j}'; ignoring AGL"
+ . " name '$ch'\n";
+ }
+ else {
+ $mapped{$map{$ch, $j}} = $ch;
+ }
+ }
+ }
+ else {
+ my $u = ""; # the resulting groff glyph name
+ my $ucomp = ""; # Unicode string before decomposition
+ my $utmp = ""; # temporary value
+ my $component = "";
+ my $nv = 0;
+
+ # Step 1:
+ # Drop all characters from the glyph name starting with the
+ # first occurrence of a period (U+002E FULL STOP), if any.
+ # ?? We avoid mapping of glyphs with periods, since they are
+ # likely to be variant glyphs, leading to a 'many ps glyphs --
+ # one groff glyph' conflict.
+ #
+ # If multiple glyphs in the font represent the same character
+ # in the Unicode standard, as do 'A' and 'A.swash', for example,
+ # they can be differentiated by using the same base name with
+ # different suffixes. This suffix (the part of glyph name that
+ # follows the first period) does not participate in the
+ # computation of a character sequence. It can be used by font
+ # designers to indicate some characteristics of the glyph. The
+ # suffix may contain periods or any other permitted characters.
+ # Small cap A, for example, could be named 'uni0041.sc' or
+ # 'A.sc'.
+
+ next if $ch =~ /\./;
+
+ # Step 2:
+ # Split the remaining string into a sequence of components,
+ # using the underscore character (U+005F LOW LINE) as the
+ # delimiter.
+
+ while ($ch =~ /([^_]+)/g) {
+ $component = $1;
+
+ # Step 3:
+ # Map each component to a character string according to the
+ # procedure below:
+ #
+ # * If the component is in the Adobe Glyph List, then map
+ # it to the corresponding character in that list.
+
+ $utmp = $AGL_to_unicode{$component};
+ if ($utmp) {
+ $utmp = "U+" . $utmp;
+ }
+
+ # * Otherwise, if the component is of the form 'uni'
+ # (U+0075 U+006E U+0069) followed by a sequence of
+ # uppercase hexadecimal digits (0 .. 9, A .. F, i.e.,
+ # U+0030 .. U+0039, U+0041 .. U+0046), the length of
+ # that sequence is a multiple of four, and each group of
+ # four digits represents a number in the set {0x0000 ..
+ # 0xD7FF, 0xE000 .. 0xFFFF}, then interpret each such
+ # number as a Unicode scalar value and map the component
+ # to the string made of those scalar values.
+
+ elsif ($component =~ /^uni([0-9A-F]{4})+$/) {
+ while ($component =~ /([0-9A-F]{4})/g) {
+ $nv = hex("0x" . $1);
+ if ($nv <= 0xD7FF || $nv >= 0xE000) {
+ $utmp .= "U+" . $1;
+ }
+ else {
+ $utmp = "";
+ last;
+ }
+ }
+ }
+
+ # * Otherwise, if the component is of the form 'u' (U+0075)
+ # followed by a sequence of four to six uppercase
+ # hexadecimal digits {0 .. 9, A .. F} (U+0030 .. U+0039,
+ # U+0041 .. U+0046), and those digits represent a number
+ # in {0x0000 .. 0xD7FF, 0xE000 .. 0x10FFFF}, then
+ # interpret this number as a Unicode scalar value and map
+ # the component to the string made of this scalar value.
+
+ elsif ($component =~ /^u([0-9A-F]{4,6})$/) {
+ $nv = hex("0x" . $1);
+ if ($nv <= 0xD7FF || ($nv >= 0xE000 && $nv <= 0x10FFFF)) {
+ $utmp = "U+" . $1;
+ }
+ }
+
+ # Finally, concatenate those strings; the result is the
+ # character string to which the glyph name is mapped.
+
+ $ucomp .= $utmp if $utmp;
+ }
+
+ # Unicode decomposition
+ while ($ucomp =~ /([0-9A-F]{4,6})/g) {
+ $component = $1;
+ $utmp = $unicode_decomposed{$component};
+ $u .= "_" . ($utmp ? $utmp : $component);
+ }
+ $u =~ s/^_/u/;
+ if ($u) {
+ if (defined $mapped{$u}) {
+ warn "$prog: both $mapped{$u} and $ch map to $u";
+ }
+ else {
+ $mapped{$u} = $ch;
+ }
+ $nmap{$ch} += 1;
+ $map{$ch, "0"} = $u;
+ }
+ }
+ }
+}
+
+# Check explicitly for groff's standard ligatures -- many afm files don't
+# have proper 'L' entries.
+
+my %default_ligatures = (
+ "fi", "f i",
+ "fl", "f l",
+ "ff", "f f",
+ "ffi", "ff i",
+ "ffl", "ff l",
+);
+
+foreach my $lig (sort keys %default_ligatures) {
+ if (defined $width{$lig} && !defined $ligatures{$lig}) {
+ $ligatures{$lig} = $default_ligatures{$lig};
+ }
+}
+
+# print it all out
+
+open(FONT, ">$outfile") ||
+ croak("unable to open '$outfile' for writing: $!\n");
+select(FONT);
+
+print("# This file was generated with $afmtodit_version.\n");
+print("#\n");
+print("# $fullname\n") if defined $fullname;
+print("# $version\n") if defined $version;
+print("# $familyname\n") if defined $familyname;
+
+if ($opt_c) {
+ print("#\n");
+ if (defined $notice || @comments) {
+ print("# The original AFM file contains the following comments:\n");
+ print("#\n");
+ print("# $notice\n") if defined $notice;
+ foreach my $comment (@comments) {
+ print("# $comment\n");
+ }
+ }
+ else {
+ print("# The original AFM file contains no comments.\n");
+ }
+}
+
+print("\n");
+
+my $name = $fontfile;
+$name =~ s@.*/@@;
+
+my $sw = 0;
+$sw = conv($width{"space"}) if defined $width{"space"};
+$sw = $space_width if ($space_width);
+
+print("name $name\n");
+print("internalname $psname\n") if $psname;
+print("special\n") if $opt_s;
+printf("slant %g\n", $italic_angle) if $italic_angle != 0;
+printf("spacewidth %d\n", $sw) if $sw;
+
+if ($opt_e) {
+ my $e = $opt_e;
+ $e =~ s@.*/@@;
+ print("encoding $e\n");
+}
+
+if (!$opt_n && %ligatures) {
+ print("ligatures");
+ foreach my $lig (sort keys %ligatures) {
+ print(" $lig");
+ }
+ print(" 0\n");
+}
+
+if (!$opt_k && $#kern1 >= 0) {
+ print("\n");
+ print("kernpairs\n");
+
+ for (my $i = 0; $i <= $#kern1; $i++) {
+ my $c1 = $kern1[$i];
+ my $c2 = $kern2[$i];
+ if (defined $nmap{$c1} && $nmap{$c1} != 0
+ && defined $nmap{$c2} && $nmap{$c2} != 0) {
+ for (my $j = 0; $j < $nmap{$c1}; $j++) {
+ for (my $k = 0; $k < $nmap{$c2}; $k++) {
+ if ($kernx[$i] != 0) {
+ printf("%s %s %d\n",
+ $map{$c1, $j},
+ $map{$c2, $k},
+ conv($kernx[$i]));
+ }
+ }
+ }
+ }
+ }
+}
+
+my ($asc_boundary, $desc_boundary, $xheight, $slant);
+
+# characters not shorter than asc_boundary are considered to have ascenders
+
+$asc_boundary = 0;
+$asc_boundary = $height{"t"} if defined $height{"t"};
+$asc_boundary -= 1;
+
+# likewise for descenders
+
+$desc_boundary = 0;
+$desc_boundary = $depth{"g"} if defined $depth{"g"};
+$desc_boundary = $depth{"j"} if defined $depth{"g"} && $depth{"j"} < $desc_boundary;
+$desc_boundary = $depth{"p"} if defined $depth{"p"} && $depth{"p"} < $desc_boundary;
+$desc_boundary = $depth{"q"} if defined $depth{"q"} && $depth{"q"} < $desc_boundary;
+$desc_boundary = $depth{"y"} if defined $depth{"y"} && $depth{"y"} < $desc_boundary;
+$desc_boundary -= 1;
+
+if (defined $height{"x"}) {
+ $xheight = $height{"x"};
+}
+elsif (defined $height{"alpha"}) {
+ $xheight = $height{"alpha"};
+}
+else {
+ $xheight = 450;
+}
+
+$italic_angle = $italic_angle*3.14159265358979323846/180.0;
+$slant = sin($italic_angle)/cos($italic_angle);
+$slant = 0 if $slant < 0;
+
+print("\n");
+print("charset\n");
+for (my $i = 0; $i <= $#encoding; $i++) {
+ my $ch = $encoding[$i];
+ if (defined $ch && $ch ne "" && $ch ne "space") {
+ $map{$ch, "0"} = "---" if !defined $nmap{$ch} || $nmap{$ch} == 0;
+ my $type = 0;
+ my $h = $height{$ch};
+ $h = 0 if $h < 0;
+ my $d = $depth{$ch};
+ $d = 0 if $d < 0;
+ $type = 1 if $d >= $desc_boundary;
+ $type += 2 if $h >= $asc_boundary;
+ printf("%s\t%d", $map{$ch, "0"}, conv($width{$ch}));
+ my $italic_correction = 0;
+ my $left_math_fit = 0;
+ my $subscript_correction = 0;
+ if (defined $opt_i) {
+ $italic_correction = $right_side_bearing{$ch} + $opt_i;
+ $italic_correction = 0 if $italic_correction < 0;
+ $subscript_correction = $slant * $xheight * .8;
+ $subscript_correction = $italic_correction if
+ $subscript_correction > $italic_correction;
+ $left_math_fit = $left_side_bearing{$ch} + $opt_i;
+ if (defined $opt_m) {
+ $left_math_fit = 0 if $left_math_fit < 0;
+ }
+ }
+ if (defined $italic_correction{$ch}) {
+ $italic_correction = $italic_correction{$ch};
+ }
+ if (defined $left_italic_correction{$ch}) {
+ $left_math_fit = $left_italic_correction{$ch};
+ }
+ if (defined $subscript_correction{$ch}) {
+ $subscript_correction = $subscript_correction{$ch};
+ }
+ if ($subscript_correction != 0) {
+ printf(",%d,%d", conv($h), conv($d));
+ printf(",%d,%d,%d", conv($italic_correction),
+ conv($left_math_fit),
+ conv($subscript_correction));
+ }
+ elsif ($left_math_fit != 0) {
+ printf(",%d,%d", conv($h), conv($d));
+ printf(",%d,%d", conv($italic_correction),
+ conv($left_math_fit));
+ }
+ elsif ($italic_correction != 0) {
+ printf(",%d,%d", conv($h), conv($d));
+ printf(",%d", conv($italic_correction));
+ }
+ elsif ($d != 0) {
+ printf(",%d,%d", conv($h), conv($d));
+ }
+ else {
+ # always put the height in to stop groff guessing
+ printf(",%d", conv($h));
+ }
+ printf("\t%d", $type);
+ printf("\t%d\t%s\n", $i, $ch);
+ if (defined $nmap{$ch}) {
+ for (my $j = 1; $j < $nmap{$ch}; $j++) {
+ printf("%s\t\"\n", $map{$ch, $j});
+ }
+ }
+ }
+ if (defined $ch && $ch eq "space" && defined $width{"space"}) {
+ printf("space\t%d\t0\t%d\tspace\n", conv($width{"space"}), $i);
+ }
+}
+
+sub conv {
+ $_[0]*$unitwidth*$resolution/(72*1000*$sizescale) +
+ ($_[0] < 0 ? -.5 : .5);
+}
+
+# Local Variables:
+# fill-column: 72
+# mode: CPerl
+# End:
+# vim: set cindent noexpandtab shiftwidth=2 softtabstop=2 textwidth=72:
diff --git a/src/utils/afmtodit/afmtodit.tables b/src/utils/afmtodit/afmtodit.tables
new file mode 100644
index 0000000..16e3647
--- /dev/null
+++ b/src/utils/afmtodit/afmtodit.tables
@@ -0,0 +1,6163 @@
+# This table was algorithmically derived from the file 'UnicodeData.txt'
+# for Unicode 15.0.0, available from unicode.org,
+# on 2022-10-09.
+my %unicode_decomposed = (
+ "00C0", "0041_0300",
+ "00C1", "0041_0301",
+ "00C2", "0041_0302",
+ "00C3", "0041_0303",
+ "00C4", "0041_0308",
+ "00C5", "0041_030A",
+ "00C7", "0043_0327",
+ "00C8", "0045_0300",
+ "00C9", "0045_0301",
+ "00CA", "0045_0302",
+ "00CB", "0045_0308",
+ "00CC", "0049_0300",
+ "00CD", "0049_0301",
+ "00CE", "0049_0302",
+ "00CF", "0049_0308",
+ "00D1", "004E_0303",
+ "00D2", "004F_0300",
+ "00D3", "004F_0301",
+ "00D4", "004F_0302",
+ "00D5", "004F_0303",
+ "00D6", "004F_0308",
+ "00D9", "0055_0300",
+ "00DA", "0055_0301",
+ "00DB", "0055_0302",
+ "00DC", "0055_0308",
+ "00DD", "0059_0301",
+ "00E0", "0061_0300",
+ "00E1", "0061_0301",
+ "00E2", "0061_0302",
+ "00E3", "0061_0303",
+ "00E4", "0061_0308",
+ "00E5", "0061_030A",
+ "00E7", "0063_0327",
+ "00E8", "0065_0300",
+ "00E9", "0065_0301",
+ "00EA", "0065_0302",
+ "00EB", "0065_0308",
+ "00EC", "0069_0300",
+ "00ED", "0069_0301",
+ "00EE", "0069_0302",
+ "00EF", "0069_0308",
+ "00F1", "006E_0303",
+ "00F2", "006F_0300",
+ "00F3", "006F_0301",
+ "00F4", "006F_0302",
+ "00F5", "006F_0303",
+ "00F6", "006F_0308",
+ "00F9", "0075_0300",
+ "00FA", "0075_0301",
+ "00FB", "0075_0302",
+ "00FC", "0075_0308",
+ "00FD", "0079_0301",
+ "00FF", "0079_0308",
+ "0100", "0041_0304",
+ "0101", "0061_0304",
+ "0102", "0041_0306",
+ "0103", "0061_0306",
+ "0104", "0041_0328",
+ "0105", "0061_0328",
+ "0106", "0043_0301",
+ "0107", "0063_0301",
+ "0108", "0043_0302",
+ "0109", "0063_0302",
+ "010A", "0043_0307",
+ "010B", "0063_0307",
+ "010C", "0043_030C",
+ "010D", "0063_030C",
+ "010E", "0044_030C",
+ "010F", "0064_030C",
+ "0112", "0045_0304",
+ "0113", "0065_0304",
+ "0114", "0045_0306",
+ "0115", "0065_0306",
+ "0116", "0045_0307",
+ "0117", "0065_0307",
+ "0118", "0045_0328",
+ "0119", "0065_0328",
+ "011A", "0045_030C",
+ "011B", "0065_030C",
+ "011C", "0047_0302",
+ "011D", "0067_0302",
+ "011E", "0047_0306",
+ "011F", "0067_0306",
+ "0120", "0047_0307",
+ "0121", "0067_0307",
+ "0122", "0047_0327",
+ "0123", "0067_0327",
+ "0124", "0048_0302",
+ "0125", "0068_0302",
+ "0128", "0049_0303",
+ "0129", "0069_0303",
+ "012A", "0049_0304",
+ "012B", "0069_0304",
+ "012C", "0049_0306",
+ "012D", "0069_0306",
+ "012E", "0049_0328",
+ "012F", "0069_0328",
+ "0130", "0049_0307",
+ "0134", "004A_0302",
+ "0135", "006A_0302",
+ "0136", "004B_0327",
+ "0137", "006B_0327",
+ "0139", "004C_0301",
+ "013A", "006C_0301",
+ "013B", "004C_0327",
+ "013C", "006C_0327",
+ "013D", "004C_030C",
+ "013E", "006C_030C",
+ "0143", "004E_0301",
+ "0144", "006E_0301",
+ "0145", "004E_0327",
+ "0146", "006E_0327",
+ "0147", "004E_030C",
+ "0148", "006E_030C",
+ "014C", "004F_0304",
+ "014D", "006F_0304",
+ "014E", "004F_0306",
+ "014F", "006F_0306",
+ "0150", "004F_030B",
+ "0151", "006F_030B",
+ "0154", "0052_0301",
+ "0155", "0072_0301",
+ "0156", "0052_0327",
+ "0157", "0072_0327",
+ "0158", "0052_030C",
+ "0159", "0072_030C",
+ "015A", "0053_0301",
+ "015B", "0073_0301",
+ "015C", "0053_0302",
+ "015D", "0073_0302",
+ "015E", "0053_0327",
+ "015F", "0073_0327",
+ "0160", "0053_030C",
+ "0161", "0073_030C",
+ "0162", "0054_0327",
+ "0163", "0074_0327",
+ "0164", "0054_030C",
+ "0165", "0074_030C",
+ "0168", "0055_0303",
+ "0169", "0075_0303",
+ "016A", "0055_0304",
+ "016B", "0075_0304",
+ "016C", "0055_0306",
+ "016D", "0075_0306",
+ "016E", "0055_030A",
+ "016F", "0075_030A",
+ "0170", "0055_030B",
+ "0171", "0075_030B",
+ "0172", "0055_0328",
+ "0173", "0075_0328",
+ "0174", "0057_0302",
+ "0175", "0077_0302",
+ "0176", "0059_0302",
+ "0177", "0079_0302",
+ "0178", "0059_0308",
+ "0179", "005A_0301",
+ "017A", "007A_0301",
+ "017B", "005A_0307",
+ "017C", "007A_0307",
+ "017D", "005A_030C",
+ "017E", "007A_030C",
+ "01A0", "004F_031B",
+ "01A1", "006F_031B",
+ "01AF", "0055_031B",
+ "01B0", "0075_031B",
+ "01CD", "0041_030C",
+ "01CE", "0061_030C",
+ "01CF", "0049_030C",
+ "01D0", "0069_030C",
+ "01D1", "004F_030C",
+ "01D2", "006F_030C",
+ "01D3", "0055_030C",
+ "01D4", "0075_030C",
+ "01D5", "0055_0308_0304",
+ "01D6", "0075_0308_0304",
+ "01D7", "0055_0308_0301",
+ "01D8", "0075_0308_0301",
+ "01D9", "0055_0308_030C",
+ "01DA", "0075_0308_030C",
+ "01DB", "0055_0308_0300",
+ "01DC", "0075_0308_0300",
+ "01DE", "0041_0308_0304",
+ "01DF", "0061_0308_0304",
+ "01E0", "0041_0307_0304",
+ "01E1", "0061_0307_0304",
+ "01E2", "00C6_0304",
+ "01E3", "00E6_0304",
+ "01E6", "0047_030C",
+ "01E7", "0067_030C",
+ "01E8", "004B_030C",
+ "01E9", "006B_030C",
+ "01EA", "004F_0328",
+ "01EB", "006F_0328",
+ "01EC", "004F_0328_0304",
+ "01ED", "006F_0328_0304",
+ "01EE", "01B7_030C",
+ "01EF", "0292_030C",
+ "01F0", "006A_030C",
+ "01F4", "0047_0301",
+ "01F5", "0067_0301",
+ "01F8", "004E_0300",
+ "01F9", "006E_0300",
+ "01FA", "0041_030A_0301",
+ "01FB", "0061_030A_0301",
+ "01FC", "00C6_0301",
+ "01FD", "00E6_0301",
+ "01FE", "00D8_0301",
+ "01FF", "00F8_0301",
+ "0200", "0041_030F",
+ "0201", "0061_030F",
+ "0202", "0041_0311",
+ "0203", "0061_0311",
+ "0204", "0045_030F",
+ "0205", "0065_030F",
+ "0206", "0045_0311",
+ "0207", "0065_0311",
+ "0208", "0049_030F",
+ "0209", "0069_030F",
+ "020A", "0049_0311",
+ "020B", "0069_0311",
+ "020C", "004F_030F",
+ "020D", "006F_030F",
+ "020E", "004F_0311",
+ "020F", "006F_0311",
+ "0210", "0052_030F",
+ "0211", "0072_030F",
+ "0212", "0052_0311",
+ "0213", "0072_0311",
+ "0214", "0055_030F",
+ "0215", "0075_030F",
+ "0216", "0055_0311",
+ "0217", "0075_0311",
+ "0218", "0053_0326",
+ "0219", "0073_0326",
+ "021A", "0054_0326",
+ "021B", "0074_0326",
+ "021E", "0048_030C",
+ "021F", "0068_030C",
+ "0226", "0041_0307",
+ "0227", "0061_0307",
+ "0228", "0045_0327",
+ "0229", "0065_0327",
+ "022A", "004F_0308_0304",
+ "022B", "006F_0308_0304",
+ "022C", "004F_0303_0304",
+ "022D", "006F_0303_0304",
+ "022E", "004F_0307",
+ "022F", "006F_0307",
+ "0230", "004F_0307_0304",
+ "0231", "006F_0307_0304",
+ "0232", "0059_0304",
+ "0233", "0079_0304",
+ "0340", "0300",
+ "0341", "0301",
+ "0343", "0313",
+ "0344", "0308_0301",
+ "0374", "02B9",
+ "037E", "003B",
+ "0385", "00A8_0301",
+ "0386", "0391_0301",
+ "0387", "00B7",
+ "0388", "0395_0301",
+ "0389", "0397_0301",
+ "038A", "0399_0301",
+ "038C", "039F_0301",
+ "038E", "03A5_0301",
+ "038F", "03A9_0301",
+ "0390", "03B9_0308_0301",
+ "03AA", "0399_0308",
+ "03AB", "03A5_0308",
+ "03AC", "03B1_0301",
+ "03AD", "03B5_0301",
+ "03AE", "03B7_0301",
+ "03AF", "03B9_0301",
+ "03B0", "03C5_0308_0301",
+ "03CA", "03B9_0308",
+ "03CB", "03C5_0308",
+ "03CC", "03BF_0301",
+ "03CD", "03C5_0301",
+ "03CE", "03C9_0301",
+ "03D3", "03D2_0301",
+ "03D4", "03D2_0308",
+ "0400", "0415_0300",
+ "0401", "0415_0308",
+ "0403", "0413_0301",
+ "0407", "0406_0308",
+ "040C", "041A_0301",
+ "040D", "0418_0300",
+ "040E", "0423_0306",
+ "0419", "0418_0306",
+ "0439", "0438_0306",
+ "0450", "0435_0300",
+ "0451", "0435_0308",
+ "0453", "0433_0301",
+ "0457", "0456_0308",
+ "045C", "043A_0301",
+ "045D", "0438_0300",
+ "045E", "0443_0306",
+ "0476", "0474_030F",
+ "0477", "0475_030F",
+ "04C1", "0416_0306",
+ "04C2", "0436_0306",
+ "04D0", "0410_0306",
+ "04D1", "0430_0306",
+ "04D2", "0410_0308",
+ "04D3", "0430_0308",
+ "04D6", "0415_0306",
+ "04D7", "0435_0306",
+ "04DA", "04D8_0308",
+ "04DB", "04D9_0308",
+ "04DC", "0416_0308",
+ "04DD", "0436_0308",
+ "04DE", "0417_0308",
+ "04DF", "0437_0308",
+ "04E2", "0418_0304",
+ "04E3", "0438_0304",
+ "04E4", "0418_0308",
+ "04E5", "0438_0308",
+ "04E6", "041E_0308",
+ "04E7", "043E_0308",
+ "04EA", "04E8_0308",
+ "04EB", "04E9_0308",
+ "04EC", "042D_0308",
+ "04ED", "044D_0308",
+ "04EE", "0423_0304",
+ "04EF", "0443_0304",
+ "04F0", "0423_0308",
+ "04F1", "0443_0308",
+ "04F2", "0423_030B",
+ "04F3", "0443_030B",
+ "04F4", "0427_0308",
+ "04F5", "0447_0308",
+ "04F8", "042B_0308",
+ "04F9", "044B_0308",
+ "0622", "0627_0653",
+ "0623", "0627_0654",
+ "0624", "0648_0654",
+ "0625", "0627_0655",
+ "0626", "064A_0654",
+ "06C0", "06D5_0654",
+ "06C2", "06C1_0654",
+ "06D3", "06D2_0654",
+ "0929", "0928_093C",
+ "0931", "0930_093C",
+ "0934", "0933_093C",
+ "0958", "0915_093C",
+ "0959", "0916_093C",
+ "095A", "0917_093C",
+ "095B", "091C_093C",
+ "095C", "0921_093C",
+ "095D", "0922_093C",
+ "095E", "092B_093C",
+ "095F", "092F_093C",
+ "09CB", "09C7_09BE",
+ "09CC", "09C7_09D7",
+ "09DC", "09A1_09BC",
+ "09DD", "09A2_09BC",
+ "09DF", "09AF_09BC",
+ "0A33", "0A32_0A3C",
+ "0A36", "0A38_0A3C",
+ "0A59", "0A16_0A3C",
+ "0A5A", "0A17_0A3C",
+ "0A5B", "0A1C_0A3C",
+ "0A5E", "0A2B_0A3C",
+ "0B48", "0B47_0B56",
+ "0B4B", "0B47_0B3E",
+ "0B4C", "0B47_0B57",
+ "0B5C", "0B21_0B3C",
+ "0B5D", "0B22_0B3C",
+ "0B94", "0B92_0BD7",
+ "0BCA", "0BC6_0BBE",
+ "0BCB", "0BC7_0BBE",
+ "0BCC", "0BC6_0BD7",
+ "0C48", "0C46_0C56",
+ "0CC0", "0CBF_0CD5",
+ "0CC7", "0CC6_0CD5",
+ "0CC8", "0CC6_0CD6",
+ "0CCA", "0CC6_0CC2",
+ "0CCB", "0CC6_0CC2_0CD5",
+ "0D4A", "0D46_0D3E",
+ "0D4B", "0D47_0D3E",
+ "0D4C", "0D46_0D57",
+ "0DDA", "0DD9_0DCA",
+ "0DDC", "0DD9_0DCF",
+ "0DDD", "0DD9_0DCF_0DCA",
+ "0DDE", "0DD9_0DDF",
+ "0F43", "0F42_0FB7",
+ "0F4D", "0F4C_0FB7",
+ "0F52", "0F51_0FB7",
+ "0F57", "0F56_0FB7",
+ "0F5C", "0F5B_0FB7",
+ "0F69", "0F40_0FB5",
+ "0F73", "0F71_0F72",
+ "0F75", "0F71_0F74",
+ "0F76", "0FB2_0F80",
+ "0F78", "0FB3_0F80",
+ "0F81", "0F71_0F80",
+ "0F93", "0F92_0FB7",
+ "0F9D", "0F9C_0FB7",
+ "0FA2", "0FA1_0FB7",
+ "0FA7", "0FA6_0FB7",
+ "0FAC", "0FAB_0FB7",
+ "0FB9", "0F90_0FB5",
+ "1026", "1025_102E",
+ "1B06", "1B05_1B35",
+ "1B08", "1B07_1B35",
+ "1B0A", "1B09_1B35",
+ "1B0C", "1B0B_1B35",
+ "1B0E", "1B0D_1B35",
+ "1B12", "1B11_1B35",
+ "1B3B", "1B3A_1B35",
+ "1B3D", "1B3C_1B35",
+ "1B40", "1B3E_1B35",
+ "1B41", "1B3F_1B35",
+ "1B43", "1B42_1B35",
+ "1E00", "0041_0325",
+ "1E01", "0061_0325",
+ "1E02", "0042_0307",
+ "1E03", "0062_0307",
+ "1E04", "0042_0323",
+ "1E05", "0062_0323",
+ "1E06", "0042_0331",
+ "1E07", "0062_0331",
+ "1E08", "0043_0327_0301",
+ "1E09", "0063_0327_0301",
+ "1E0A", "0044_0307",
+ "1E0B", "0064_0307",
+ "1E0C", "0044_0323",
+ "1E0D", "0064_0323",
+ "1E0E", "0044_0331",
+ "1E0F", "0064_0331",
+ "1E10", "0044_0327",
+ "1E11", "0064_0327",
+ "1E12", "0044_032D",
+ "1E13", "0064_032D",
+ "1E14", "0045_0304_0300",
+ "1E15", "0065_0304_0300",
+ "1E16", "0045_0304_0301",
+ "1E17", "0065_0304_0301",
+ "1E18", "0045_032D",
+ "1E19", "0065_032D",
+ "1E1A", "0045_0330",
+ "1E1B", "0065_0330",
+ "1E1C", "0045_0327_0306",
+ "1E1D", "0065_0327_0306",
+ "1E1E", "0046_0307",
+ "1E1F", "0066_0307",
+ "1E20", "0047_0304",
+ "1E21", "0067_0304",
+ "1E22", "0048_0307",
+ "1E23", "0068_0307",
+ "1E24", "0048_0323",
+ "1E25", "0068_0323",
+ "1E26", "0048_0308",
+ "1E27", "0068_0308",
+ "1E28", "0048_0327",
+ "1E29", "0068_0327",
+ "1E2A", "0048_032E",
+ "1E2B", "0068_032E",
+ "1E2C", "0049_0330",
+ "1E2D", "0069_0330",
+ "1E2E", "0049_0308_0301",
+ "1E2F", "0069_0308_0301",
+ "1E30", "004B_0301",
+ "1E31", "006B_0301",
+ "1E32", "004B_0323",
+ "1E33", "006B_0323",
+ "1E34", "004B_0331",
+ "1E35", "006B_0331",
+ "1E36", "004C_0323",
+ "1E37", "006C_0323",
+ "1E38", "004C_0323_0304",
+ "1E39", "006C_0323_0304",
+ "1E3A", "004C_0331",
+ "1E3B", "006C_0331",
+ "1E3C", "004C_032D",
+ "1E3D", "006C_032D",
+ "1E3E", "004D_0301",
+ "1E3F", "006D_0301",
+ "1E40", "004D_0307",
+ "1E41", "006D_0307",
+ "1E42", "004D_0323",
+ "1E43", "006D_0323",
+ "1E44", "004E_0307",
+ "1E45", "006E_0307",
+ "1E46", "004E_0323",
+ "1E47", "006E_0323",
+ "1E48", "004E_0331",
+ "1E49", "006E_0331",
+ "1E4A", "004E_032D",
+ "1E4B", "006E_032D",
+ "1E4C", "004F_0303_0301",
+ "1E4D", "006F_0303_0301",
+ "1E4E", "004F_0303_0308",
+ "1E4F", "006F_0303_0308",
+ "1E50", "004F_0304_0300",
+ "1E51", "006F_0304_0300",
+ "1E52", "004F_0304_0301",
+ "1E53", "006F_0304_0301",
+ "1E54", "0050_0301",
+ "1E55", "0070_0301",
+ "1E56", "0050_0307",
+ "1E57", "0070_0307",
+ "1E58", "0052_0307",
+ "1E59", "0072_0307",
+ "1E5A", "0052_0323",
+ "1E5B", "0072_0323",
+ "1E5C", "0052_0323_0304",
+ "1E5D", "0072_0323_0304",
+ "1E5E", "0052_0331",
+ "1E5F", "0072_0331",
+ "1E60", "0053_0307",
+ "1E61", "0073_0307",
+ "1E62", "0053_0323",
+ "1E63", "0073_0323",
+ "1E64", "0053_0301_0307",
+ "1E65", "0073_0301_0307",
+ "1E66", "0053_030C_0307",
+ "1E67", "0073_030C_0307",
+ "1E68", "0053_0323_0307",
+ "1E69", "0073_0323_0307",
+ "1E6A", "0054_0307",
+ "1E6B", "0074_0307",
+ "1E6C", "0054_0323",
+ "1E6D", "0074_0323",
+ "1E6E", "0054_0331",
+ "1E6F", "0074_0331",
+ "1E70", "0054_032D",
+ "1E71", "0074_032D",
+ "1E72", "0055_0324",
+ "1E73", "0075_0324",
+ "1E74", "0055_0330",
+ "1E75", "0075_0330",
+ "1E76", "0055_032D",
+ "1E77", "0075_032D",
+ "1E78", "0055_0303_0301",
+ "1E79", "0075_0303_0301",
+ "1E7A", "0055_0304_0308",
+ "1E7B", "0075_0304_0308",
+ "1E7C", "0056_0303",
+ "1E7D", "0076_0303",
+ "1E7E", "0056_0323",
+ "1E7F", "0076_0323",
+ "1E80", "0057_0300",
+ "1E81", "0077_0300",
+ "1E82", "0057_0301",
+ "1E83", "0077_0301",
+ "1E84", "0057_0308",
+ "1E85", "0077_0308",
+ "1E86", "0057_0307",
+ "1E87", "0077_0307",
+ "1E88", "0057_0323",
+ "1E89", "0077_0323",
+ "1E8A", "0058_0307",
+ "1E8B", "0078_0307",
+ "1E8C", "0058_0308",
+ "1E8D", "0078_0308",
+ "1E8E", "0059_0307",
+ "1E8F", "0079_0307",
+ "1E90", "005A_0302",
+ "1E91", "007A_0302",
+ "1E92", "005A_0323",
+ "1E93", "007A_0323",
+ "1E94", "005A_0331",
+ "1E95", "007A_0331",
+ "1E96", "0068_0331",
+ "1E97", "0074_0308",
+ "1E98", "0077_030A",
+ "1E99", "0079_030A",
+ "1E9B", "017F_0307",
+ "1EA0", "0041_0323",
+ "1EA1", "0061_0323",
+ "1EA2", "0041_0309",
+ "1EA3", "0061_0309",
+ "1EA4", "0041_0302_0301",
+ "1EA5", "0061_0302_0301",
+ "1EA6", "0041_0302_0300",
+ "1EA7", "0061_0302_0300",
+ "1EA8", "0041_0302_0309",
+ "1EA9", "0061_0302_0309",
+ "1EAA", "0041_0302_0303",
+ "1EAB", "0061_0302_0303",
+ "1EAC", "0041_0323_0302",
+ "1EAD", "0061_0323_0302",
+ "1EAE", "0041_0306_0301",
+ "1EAF", "0061_0306_0301",
+ "1EB0", "0041_0306_0300",
+ "1EB1", "0061_0306_0300",
+ "1EB2", "0041_0306_0309",
+ "1EB3", "0061_0306_0309",
+ "1EB4", "0041_0306_0303",
+ "1EB5", "0061_0306_0303",
+ "1EB6", "0041_0323_0306",
+ "1EB7", "0061_0323_0306",
+ "1EB8", "0045_0323",
+ "1EB9", "0065_0323",
+ "1EBA", "0045_0309",
+ "1EBB", "0065_0309",
+ "1EBC", "0045_0303",
+ "1EBD", "0065_0303",
+ "1EBE", "0045_0302_0301",
+ "1EBF", "0065_0302_0301",
+ "1EC0", "0045_0302_0300",
+ "1EC1", "0065_0302_0300",
+ "1EC2", "0045_0302_0309",
+ "1EC3", "0065_0302_0309",
+ "1EC4", "0045_0302_0303",
+ "1EC5", "0065_0302_0303",
+ "1EC6", "0045_0323_0302",
+ "1EC7", "0065_0323_0302",
+ "1EC8", "0049_0309",
+ "1EC9", "0069_0309",
+ "1ECA", "0049_0323",
+ "1ECB", "0069_0323",
+ "1ECC", "004F_0323",
+ "1ECD", "006F_0323",
+ "1ECE", "004F_0309",
+ "1ECF", "006F_0309",
+ "1ED0", "004F_0302_0301",
+ "1ED1", "006F_0302_0301",
+ "1ED2", "004F_0302_0300",
+ "1ED3", "006F_0302_0300",
+ "1ED4", "004F_0302_0309",
+ "1ED5", "006F_0302_0309",
+ "1ED6", "004F_0302_0303",
+ "1ED7", "006F_0302_0303",
+ "1ED8", "004F_0323_0302",
+ "1ED9", "006F_0323_0302",
+ "1EDA", "004F_031B_0301",
+ "1EDB", "006F_031B_0301",
+ "1EDC", "004F_031B_0300",
+ "1EDD", "006F_031B_0300",
+ "1EDE", "004F_031B_0309",
+ "1EDF", "006F_031B_0309",
+ "1EE0", "004F_031B_0303",
+ "1EE1", "006F_031B_0303",
+ "1EE2", "004F_031B_0323",
+ "1EE3", "006F_031B_0323",
+ "1EE4", "0055_0323",
+ "1EE5", "0075_0323",
+ "1EE6", "0055_0309",
+ "1EE7", "0075_0309",
+ "1EE8", "0055_031B_0301",
+ "1EE9", "0075_031B_0301",
+ "1EEA", "0055_031B_0300",
+ "1EEB", "0075_031B_0300",
+ "1EEC", "0055_031B_0309",
+ "1EED", "0075_031B_0309",
+ "1EEE", "0055_031B_0303",
+ "1EEF", "0075_031B_0303",
+ "1EF0", "0055_031B_0323",
+ "1EF1", "0075_031B_0323",
+ "1EF2", "0059_0300",
+ "1EF3", "0079_0300",
+ "1EF4", "0059_0323",
+ "1EF5", "0079_0323",
+ "1EF6", "0059_0309",
+ "1EF7", "0079_0309",
+ "1EF8", "0059_0303",
+ "1EF9", "0079_0303",
+ "1F00", "03B1_0313",
+ "1F01", "03B1_0314",
+ "1F02", "03B1_0313_0300",
+ "1F03", "03B1_0314_0300",
+ "1F04", "03B1_0313_0301",
+ "1F05", "03B1_0314_0301",
+ "1F06", "03B1_0313_0342",
+ "1F07", "03B1_0314_0342",
+ "1F08", "0391_0313",
+ "1F09", "0391_0314",
+ "1F0A", "0391_0313_0300",
+ "1F0B", "0391_0314_0300",
+ "1F0C", "0391_0313_0301",
+ "1F0D", "0391_0314_0301",
+ "1F0E", "0391_0313_0342",
+ "1F0F", "0391_0314_0342",
+ "1F10", "03B5_0313",
+ "1F11", "03B5_0314",
+ "1F12", "03B5_0313_0300",
+ "1F13", "03B5_0314_0300",
+ "1F14", "03B5_0313_0301",
+ "1F15", "03B5_0314_0301",
+ "1F18", "0395_0313",
+ "1F19", "0395_0314",
+ "1F1A", "0395_0313_0300",
+ "1F1B", "0395_0314_0300",
+ "1F1C", "0395_0313_0301",
+ "1F1D", "0395_0314_0301",
+ "1F20", "03B7_0313",
+ "1F21", "03B7_0314",
+ "1F22", "03B7_0313_0300",
+ "1F23", "03B7_0314_0300",
+ "1F24", "03B7_0313_0301",
+ "1F25", "03B7_0314_0301",
+ "1F26", "03B7_0313_0342",
+ "1F27", "03B7_0314_0342",
+ "1F28", "0397_0313",
+ "1F29", "0397_0314",
+ "1F2A", "0397_0313_0300",
+ "1F2B", "0397_0314_0300",
+ "1F2C", "0397_0313_0301",
+ "1F2D", "0397_0314_0301",
+ "1F2E", "0397_0313_0342",
+ "1F2F", "0397_0314_0342",
+ "1F30", "03B9_0313",
+ "1F31", "03B9_0314",
+ "1F32", "03B9_0313_0300",
+ "1F33", "03B9_0314_0300",
+ "1F34", "03B9_0313_0301",
+ "1F35", "03B9_0314_0301",
+ "1F36", "03B9_0313_0342",
+ "1F37", "03B9_0314_0342",
+ "1F38", "0399_0313",
+ "1F39", "0399_0314",
+ "1F3A", "0399_0313_0300",
+ "1F3B", "0399_0314_0300",
+ "1F3C", "0399_0313_0301",
+ "1F3D", "0399_0314_0301",
+ "1F3E", "0399_0313_0342",
+ "1F3F", "0399_0314_0342",
+ "1F40", "03BF_0313",
+ "1F41", "03BF_0314",
+ "1F42", "03BF_0313_0300",
+ "1F43", "03BF_0314_0300",
+ "1F44", "03BF_0313_0301",
+ "1F45", "03BF_0314_0301",
+ "1F48", "039F_0313",
+ "1F49", "039F_0314",
+ "1F4A", "039F_0313_0300",
+ "1F4B", "039F_0314_0300",
+ "1F4C", "039F_0313_0301",
+ "1F4D", "039F_0314_0301",
+ "1F50", "03C5_0313",
+ "1F51", "03C5_0314",
+ "1F52", "03C5_0313_0300",
+ "1F53", "03C5_0314_0300",
+ "1F54", "03C5_0313_0301",
+ "1F55", "03C5_0314_0301",
+ "1F56", "03C5_0313_0342",
+ "1F57", "03C5_0314_0342",
+ "1F59", "03A5_0314",
+ "1F5B", "03A5_0314_0300",
+ "1F5D", "03A5_0314_0301",
+ "1F5F", "03A5_0314_0342",
+ "1F60", "03C9_0313",
+ "1F61", "03C9_0314",
+ "1F62", "03C9_0313_0300",
+ "1F63", "03C9_0314_0300",
+ "1F64", "03C9_0313_0301",
+ "1F65", "03C9_0314_0301",
+ "1F66", "03C9_0313_0342",
+ "1F67", "03C9_0314_0342",
+ "1F68", "03A9_0313",
+ "1F69", "03A9_0314",
+ "1F6A", "03A9_0313_0300",
+ "1F6B", "03A9_0314_0300",
+ "1F6C", "03A9_0313_0301",
+ "1F6D", "03A9_0314_0301",
+ "1F6E", "03A9_0313_0342",
+ "1F6F", "03A9_0314_0342",
+ "1F70", "03B1_0300",
+ "1F71", "03B1_0301",
+ "1F72", "03B5_0300",
+ "1F73", "03B5_0301",
+ "1F74", "03B7_0300",
+ "1F75", "03B7_0301",
+ "1F76", "03B9_0300",
+ "1F77", "03B9_0301",
+ "1F78", "03BF_0300",
+ "1F79", "03BF_0301",
+ "1F7A", "03C5_0300",
+ "1F7B", "03C5_0301",
+ "1F7C", "03C9_0300",
+ "1F7D", "03C9_0301",
+ "1F80", "03B1_0313_0345",
+ "1F81", "03B1_0314_0345",
+ "1F82", "03B1_0313_0300_0345",
+ "1F83", "03B1_0314_0300_0345",
+ "1F84", "03B1_0313_0301_0345",
+ "1F85", "03B1_0314_0301_0345",
+ "1F86", "03B1_0313_0342_0345",
+ "1F87", "03B1_0314_0342_0345",
+ "1F88", "0391_0313_0345",
+ "1F89", "0391_0314_0345",
+ "1F8A", "0391_0313_0300_0345",
+ "1F8B", "0391_0314_0300_0345",
+ "1F8C", "0391_0313_0301_0345",
+ "1F8D", "0391_0314_0301_0345",
+ "1F8E", "0391_0313_0342_0345",
+ "1F8F", "0391_0314_0342_0345",
+ "1F90", "03B7_0313_0345",
+ "1F91", "03B7_0314_0345",
+ "1F92", "03B7_0313_0300_0345",
+ "1F93", "03B7_0314_0300_0345",
+ "1F94", "03B7_0313_0301_0345",
+ "1F95", "03B7_0314_0301_0345",
+ "1F96", "03B7_0313_0342_0345",
+ "1F97", "03B7_0314_0342_0345",
+ "1F98", "0397_0313_0345",
+ "1F99", "0397_0314_0345",
+ "1F9A", "0397_0313_0300_0345",
+ "1F9B", "0397_0314_0300_0345",
+ "1F9C", "0397_0313_0301_0345",
+ "1F9D", "0397_0314_0301_0345",
+ "1F9E", "0397_0313_0342_0345",
+ "1F9F", "0397_0314_0342_0345",
+ "1FA0", "03C9_0313_0345",
+ "1FA1", "03C9_0314_0345",
+ "1FA2", "03C9_0313_0300_0345",
+ "1FA3", "03C9_0314_0300_0345",
+ "1FA4", "03C9_0313_0301_0345",
+ "1FA5", "03C9_0314_0301_0345",
+ "1FA6", "03C9_0313_0342_0345",
+ "1FA7", "03C9_0314_0342_0345",
+ "1FA8", "03A9_0313_0345",
+ "1FA9", "03A9_0314_0345",
+ "1FAA", "03A9_0313_0300_0345",
+ "1FAB", "03A9_0314_0300_0345",
+ "1FAC", "03A9_0313_0301_0345",
+ "1FAD", "03A9_0314_0301_0345",
+ "1FAE", "03A9_0313_0342_0345",
+ "1FAF", "03A9_0314_0342_0345",
+ "1FB0", "03B1_0306",
+ "1FB1", "03B1_0304",
+ "1FB2", "03B1_0300_0345",
+ "1FB3", "03B1_0345",
+ "1FB4", "03B1_0301_0345",
+ "1FB6", "03B1_0342",
+ "1FB7", "03B1_0342_0345",
+ "1FB8", "0391_0306",
+ "1FB9", "0391_0304",
+ "1FBA", "0391_0300",
+ "1FBB", "0391_0301",
+ "1FBC", "0391_0345",
+ "1FBE", "03B9",
+ "1FC1", "00A8_0342",
+ "1FC2", "03B7_0300_0345",
+ "1FC3", "03B7_0345",
+ "1FC4", "03B7_0301_0345",
+ "1FC6", "03B7_0342",
+ "1FC7", "03B7_0342_0345",
+ "1FC8", "0395_0300",
+ "1FC9", "0395_0301",
+ "1FCA", "0397_0300",
+ "1FCB", "0397_0301",
+ "1FCC", "0397_0345",
+ "1FCD", "1FBF_0300",
+ "1FCE", "1FBF_0301",
+ "1FCF", "1FBF_0342",
+ "1FD0", "03B9_0306",
+ "1FD1", "03B9_0304",
+ "1FD2", "03B9_0308_0300",
+ "1FD3", "03B9_0308_0301",
+ "1FD6", "03B9_0342",
+ "1FD7", "03B9_0308_0342",
+ "1FD8", "0399_0306",
+ "1FD9", "0399_0304",
+ "1FDA", "0399_0300",
+ "1FDB", "0399_0301",
+ "1FDD", "1FFE_0300",
+ "1FDE", "1FFE_0301",
+ "1FDF", "1FFE_0342",
+ "1FE0", "03C5_0306",
+ "1FE1", "03C5_0304",
+ "1FE2", "03C5_0308_0300",
+ "1FE3", "03C5_0308_0301",
+ "1FE4", "03C1_0313",
+ "1FE5", "03C1_0314",
+ "1FE6", "03C5_0342",
+ "1FE7", "03C5_0308_0342",
+ "1FE8", "03A5_0306",
+ "1FE9", "03A5_0304",
+ "1FEA", "03A5_0300",
+ "1FEB", "03A5_0301",
+ "1FEC", "03A1_0314",
+ "1FED", "00A8_0300",
+ "1FEE", "00A8_0301",
+ "1FEF", "0060",
+ "1FF2", "03C9_0300_0345",
+ "1FF3", "03C9_0345",
+ "1FF4", "03C9_0301_0345",
+ "1FF6", "03C9_0342",
+ "1FF7", "03C9_0342_0345",
+ "1FF8", "039F_0300",
+ "1FF9", "039F_0301",
+ "1FFA", "03A9_0300",
+ "1FFB", "03A9_0301",
+ "1FFC", "03A9_0345",
+ "1FFD", "00B4",
+ "2000", "2002",
+ "2001", "2003",
+ "2126", "03A9",
+ "212A", "004B",
+ "212B", "0041_030A",
+ "219A", "2190_0338",
+ "219B", "2192_0338",
+ "21AE", "2194_0338",
+ "21CD", "21D0_0338",
+ "21CE", "21D4_0338",
+ "21CF", "21D2_0338",
+ "2204", "2203_0338",
+ "2209", "2208_0338",
+ "220C", "220B_0338",
+ "2224", "2223_0338",
+ "2226", "2225_0338",
+ "2241", "223C_0338",
+ "2244", "2243_0338",
+ "2247", "2245_0338",
+ "2249", "2248_0338",
+ "2260", "003D_0338",
+ "2262", "2261_0338",
+ "226D", "224D_0338",
+ "226E", "003C_0338",
+ "226F", "003E_0338",
+ "2270", "2264_0338",
+ "2271", "2265_0338",
+ "2274", "2272_0338",
+ "2275", "2273_0338",
+ "2278", "2276_0338",
+ "2279", "2277_0338",
+ "2280", "227A_0338",
+ "2281", "227B_0338",
+ "2284", "2282_0338",
+ "2285", "2283_0338",
+ "2288", "2286_0338",
+ "2289", "2287_0338",
+ "22AC", "22A2_0338",
+ "22AD", "22A8_0338",
+ "22AE", "22A9_0338",
+ "22AF", "22AB_0338",
+ "22E0", "227C_0338",
+ "22E1", "227D_0338",
+ "22E2", "2291_0338",
+ "22E3", "2292_0338",
+ "22EA", "22B2_0338",
+ "22EB", "22B3_0338",
+ "22EC", "22B4_0338",
+ "22ED", "22B5_0338",
+ "2329", "3008",
+ "232A", "3009",
+ "2ADC", "2ADD_0338",
+ "304C", "304B_3099",
+ "304E", "304D_3099",
+ "3050", "304F_3099",
+ "3052", "3051_3099",
+ "3054", "3053_3099",
+ "3056", "3055_3099",
+ "3058", "3057_3099",
+ "305A", "3059_3099",
+ "305C", "305B_3099",
+ "305E", "305D_3099",
+ "3060", "305F_3099",
+ "3062", "3061_3099",
+ "3065", "3064_3099",
+ "3067", "3066_3099",
+ "3069", "3068_3099",
+ "3070", "306F_3099",
+ "3071", "306F_309A",
+ "3073", "3072_3099",
+ "3074", "3072_309A",
+ "3076", "3075_3099",
+ "3077", "3075_309A",
+ "3079", "3078_3099",
+ "307A", "3078_309A",
+ "307C", "307B_3099",
+ "307D", "307B_309A",
+ "3094", "3046_3099",
+ "309E", "309D_3099",
+ "30AC", "30AB_3099",
+ "30AE", "30AD_3099",
+ "30B0", "30AF_3099",
+ "30B2", "30B1_3099",
+ "30B4", "30B3_3099",
+ "30B6", "30B5_3099",
+ "30B8", "30B7_3099",
+ "30BA", "30B9_3099",
+ "30BC", "30BB_3099",
+ "30BE", "30BD_3099",
+ "30C0", "30BF_3099",
+ "30C2", "30C1_3099",
+ "30C5", "30C4_3099",
+ "30C7", "30C6_3099",
+ "30C9", "30C8_3099",
+ "30D0", "30CF_3099",
+ "30D1", "30CF_309A",
+ "30D3", "30D2_3099",
+ "30D4", "30D2_309A",
+ "30D6", "30D5_3099",
+ "30D7", "30D5_309A",
+ "30D9", "30D8_3099",
+ "30DA", "30D8_309A",
+ "30DC", "30DB_3099",
+ "30DD", "30DB_309A",
+ "30F4", "30A6_3099",
+ "30F7", "30EF_3099",
+ "30F8", "30F0_3099",
+ "30F9", "30F1_3099",
+ "30FA", "30F2_3099",
+ "30FE", "30FD_3099",
+ "F900", "8C48",
+ "F901", "66F4",
+ "F902", "8ECA",
+ "F903", "8CC8",
+ "F904", "6ED1",
+ "F905", "4E32",
+ "F906", "53E5",
+ "F907", "9F9C",
+ "F908", "9F9C",
+ "F909", "5951",
+ "F90A", "91D1",
+ "F90B", "5587",
+ "F90C", "5948",
+ "F90D", "61F6",
+ "F90E", "7669",
+ "F90F", "7F85",
+ "F910", "863F",
+ "F911", "87BA",
+ "F912", "88F8",
+ "F913", "908F",
+ "F914", "6A02",
+ "F915", "6D1B",
+ "F916", "70D9",
+ "F917", "73DE",
+ "F918", "843D",
+ "F919", "916A",
+ "F91A", "99F1",
+ "F91B", "4E82",
+ "F91C", "5375",
+ "F91D", "6B04",
+ "F91E", "721B",
+ "F91F", "862D",
+ "F920", "9E1E",
+ "F921", "5D50",
+ "F922", "6FEB",
+ "F923", "85CD",
+ "F924", "8964",
+ "F925", "62C9",
+ "F926", "81D8",
+ "F927", "881F",
+ "F928", "5ECA",
+ "F929", "6717",
+ "F92A", "6D6A",
+ "F92B", "72FC",
+ "F92C", "90CE",
+ "F92D", "4F86",
+ "F92E", "51B7",
+ "F92F", "52DE",
+ "F930", "64C4",
+ "F931", "6AD3",
+ "F932", "7210",
+ "F933", "76E7",
+ "F934", "8001",
+ "F935", "8606",
+ "F936", "865C",
+ "F937", "8DEF",
+ "F938", "9732",
+ "F939", "9B6F",
+ "F93A", "9DFA",
+ "F93B", "788C",
+ "F93C", "797F",
+ "F93D", "7DA0",
+ "F93E", "83C9",
+ "F93F", "9304",
+ "F940", "9E7F",
+ "F941", "8AD6",
+ "F942", "58DF",
+ "F943", "5F04",
+ "F944", "7C60",
+ "F945", "807E",
+ "F946", "7262",
+ "F947", "78CA",
+ "F948", "8CC2",
+ "F949", "96F7",
+ "F94A", "58D8",
+ "F94B", "5C62",
+ "F94C", "6A13",
+ "F94D", "6DDA",
+ "F94E", "6F0F",
+ "F94F", "7D2F",
+ "F950", "7E37",
+ "F951", "964B",
+ "F952", "52D2",
+ "F953", "808B",
+ "F954", "51DC",
+ "F955", "51CC",
+ "F956", "7A1C",
+ "F957", "7DBE",
+ "F958", "83F1",
+ "F959", "9675",
+ "F95A", "8B80",
+ "F95B", "62CF",
+ "F95C", "6A02",
+ "F95D", "8AFE",
+ "F95E", "4E39",
+ "F95F", "5BE7",
+ "F960", "6012",
+ "F961", "7387",
+ "F962", "7570",
+ "F963", "5317",
+ "F964", "78FB",
+ "F965", "4FBF",
+ "F966", "5FA9",
+ "F967", "4E0D",
+ "F968", "6CCC",
+ "F969", "6578",
+ "F96A", "7D22",
+ "F96B", "53C3",
+ "F96C", "585E",
+ "F96D", "7701",
+ "F96E", "8449",
+ "F96F", "8AAA",
+ "F970", "6BBA",
+ "F971", "8FB0",
+ "F972", "6C88",
+ "F973", "62FE",
+ "F974", "82E5",
+ "F975", "63A0",
+ "F976", "7565",
+ "F977", "4EAE",
+ "F978", "5169",
+ "F979", "51C9",
+ "F97A", "6881",
+ "F97B", "7CE7",
+ "F97C", "826F",
+ "F97D", "8AD2",
+ "F97E", "91CF",
+ "F97F", "52F5",
+ "F980", "5442",
+ "F981", "5973",
+ "F982", "5EEC",
+ "F983", "65C5",
+ "F984", "6FFE",
+ "F985", "792A",
+ "F986", "95AD",
+ "F987", "9A6A",
+ "F988", "9E97",
+ "F989", "9ECE",
+ "F98A", "529B",
+ "F98B", "66C6",
+ "F98C", "6B77",
+ "F98D", "8F62",
+ "F98E", "5E74",
+ "F98F", "6190",
+ "F990", "6200",
+ "F991", "649A",
+ "F992", "6F23",
+ "F993", "7149",
+ "F994", "7489",
+ "F995", "79CA",
+ "F996", "7DF4",
+ "F997", "806F",
+ "F998", "8F26",
+ "F999", "84EE",
+ "F99A", "9023",
+ "F99B", "934A",
+ "F99C", "5217",
+ "F99D", "52A3",
+ "F99E", "54BD",
+ "F99F", "70C8",
+ "F9A0", "88C2",
+ "F9A1", "8AAA",
+ "F9A2", "5EC9",
+ "F9A3", "5FF5",
+ "F9A4", "637B",
+ "F9A5", "6BAE",
+ "F9A6", "7C3E",
+ "F9A7", "7375",
+ "F9A8", "4EE4",
+ "F9A9", "56F9",
+ "F9AA", "5BE7",
+ "F9AB", "5DBA",
+ "F9AC", "601C",
+ "F9AD", "73B2",
+ "F9AE", "7469",
+ "F9AF", "7F9A",
+ "F9B0", "8046",
+ "F9B1", "9234",
+ "F9B2", "96F6",
+ "F9B3", "9748",
+ "F9B4", "9818",
+ "F9B5", "4F8B",
+ "F9B6", "79AE",
+ "F9B7", "91B4",
+ "F9B8", "96B8",
+ "F9B9", "60E1",
+ "F9BA", "4E86",
+ "F9BB", "50DA",
+ "F9BC", "5BEE",
+ "F9BD", "5C3F",
+ "F9BE", "6599",
+ "F9BF", "6A02",
+ "F9C0", "71CE",
+ "F9C1", "7642",
+ "F9C2", "84FC",
+ "F9C3", "907C",
+ "F9C4", "9F8D",
+ "F9C5", "6688",
+ "F9C6", "962E",
+ "F9C7", "5289",
+ "F9C8", "677B",
+ "F9C9", "67F3",
+ "F9CA", "6D41",
+ "F9CB", "6E9C",
+ "F9CC", "7409",
+ "F9CD", "7559",
+ "F9CE", "786B",
+ "F9CF", "7D10",
+ "F9D0", "985E",
+ "F9D1", "516D",
+ "F9D2", "622E",
+ "F9D3", "9678",
+ "F9D4", "502B",
+ "F9D5", "5D19",
+ "F9D6", "6DEA",
+ "F9D7", "8F2A",
+ "F9D8", "5F8B",
+ "F9D9", "6144",
+ "F9DA", "6817",
+ "F9DB", "7387",
+ "F9DC", "9686",
+ "F9DD", "5229",
+ "F9DE", "540F",
+ "F9DF", "5C65",
+ "F9E0", "6613",
+ "F9E1", "674E",
+ "F9E2", "68A8",
+ "F9E3", "6CE5",
+ "F9E4", "7406",
+ "F9E5", "75E2",
+ "F9E6", "7F79",
+ "F9E7", "88CF",
+ "F9E8", "88E1",
+ "F9E9", "91CC",
+ "F9EA", "96E2",
+ "F9EB", "533F",
+ "F9EC", "6EBA",
+ "F9ED", "541D",
+ "F9EE", "71D0",
+ "F9EF", "7498",
+ "F9F0", "85FA",
+ "F9F1", "96A3",
+ "F9F2", "9C57",
+ "F9F3", "9E9F",
+ "F9F4", "6797",
+ "F9F5", "6DCB",
+ "F9F6", "81E8",
+ "F9F7", "7ACB",
+ "F9F8", "7B20",
+ "F9F9", "7C92",
+ "F9FA", "72C0",
+ "F9FB", "7099",
+ "F9FC", "8B58",
+ "F9FD", "4EC0",
+ "F9FE", "8336",
+ "F9FF", "523A",
+ "FA00", "5207",
+ "FA01", "5EA6",
+ "FA02", "62D3",
+ "FA03", "7CD6",
+ "FA04", "5B85",
+ "FA05", "6D1E",
+ "FA06", "66B4",
+ "FA07", "8F3B",
+ "FA08", "884C",
+ "FA09", "964D",
+ "FA0A", "898B",
+ "FA0B", "5ED3",
+ "FA0C", "5140",
+ "FA0D", "55C0",
+ "FA10", "585A",
+ "FA12", "6674",
+ "FA15", "51DE",
+ "FA16", "732A",
+ "FA17", "76CA",
+ "FA18", "793C",
+ "FA19", "795E",
+ "FA1A", "7965",
+ "FA1B", "798F",
+ "FA1C", "9756",
+ "FA1D", "7CBE",
+ "FA1E", "7FBD",
+ "FA20", "8612",
+ "FA22", "8AF8",
+ "FA25", "9038",
+ "FA26", "90FD",
+ "FA2A", "98EF",
+ "FA2B", "98FC",
+ "FA2C", "9928",
+ "FA2D", "9DB4",
+ "FA2E", "90DE",
+ "FA2F", "96B7",
+ "FA30", "4FAE",
+ "FA31", "50E7",
+ "FA32", "514D",
+ "FA33", "52C9",
+ "FA34", "52E4",
+ "FA35", "5351",
+ "FA36", "559D",
+ "FA37", "5606",
+ "FA38", "5668",
+ "FA39", "5840",
+ "FA3A", "58A8",
+ "FA3B", "5C64",
+ "FA3C", "5C6E",
+ "FA3D", "6094",
+ "FA3E", "6168",
+ "FA3F", "618E",
+ "FA40", "61F2",
+ "FA41", "654F",
+ "FA42", "65E2",
+ "FA43", "6691",
+ "FA44", "6885",
+ "FA45", "6D77",
+ "FA46", "6E1A",
+ "FA47", "6F22",
+ "FA48", "716E",
+ "FA49", "722B",
+ "FA4A", "7422",
+ "FA4B", "7891",
+ "FA4C", "793E",
+ "FA4D", "7949",
+ "FA4E", "7948",
+ "FA4F", "7950",
+ "FA50", "7956",
+ "FA51", "795D",
+ "FA52", "798D",
+ "FA53", "798E",
+ "FA54", "7A40",
+ "FA55", "7A81",
+ "FA56", "7BC0",
+ "FA57", "7DF4",
+ "FA58", "7E09",
+ "FA59", "7E41",
+ "FA5A", "7F72",
+ "FA5B", "8005",
+ "FA5C", "81ED",
+ "FA5D", "8279",
+ "FA5E", "8279",
+ "FA5F", "8457",
+ "FA60", "8910",
+ "FA61", "8996",
+ "FA62", "8B01",
+ "FA63", "8B39",
+ "FA64", "8CD3",
+ "FA65", "8D08",
+ "FA66", "8FB6",
+ "FA67", "9038",
+ "FA68", "96E3",
+ "FA69", "97FF",
+ "FA6A", "983B",
+ "FA6B", "6075",
+ "FA6C", "242EE",
+ "FA6D", "8218",
+ "FA70", "4E26",
+ "FA71", "51B5",
+ "FA72", "5168",
+ "FA73", "4F80",
+ "FA74", "5145",
+ "FA75", "5180",
+ "FA76", "52C7",
+ "FA77", "52FA",
+ "FA78", "559D",
+ "FA79", "5555",
+ "FA7A", "5599",
+ "FA7B", "55E2",
+ "FA7C", "585A",
+ "FA7D", "58B3",
+ "FA7E", "5944",
+ "FA7F", "5954",
+ "FA80", "5A62",
+ "FA81", "5B28",
+ "FA82", "5ED2",
+ "FA83", "5ED9",
+ "FA84", "5F69",
+ "FA85", "5FAD",
+ "FA86", "60D8",
+ "FA87", "614E",
+ "FA88", "6108",
+ "FA89", "618E",
+ "FA8A", "6160",
+ "FA8B", "61F2",
+ "FA8C", "6234",
+ "FA8D", "63C4",
+ "FA8E", "641C",
+ "FA8F", "6452",
+ "FA90", "6556",
+ "FA91", "6674",
+ "FA92", "6717",
+ "FA93", "671B",
+ "FA94", "6756",
+ "FA95", "6B79",
+ "FA96", "6BBA",
+ "FA97", "6D41",
+ "FA98", "6EDB",
+ "FA99", "6ECB",
+ "FA9A", "6F22",
+ "FA9B", "701E",
+ "FA9C", "716E",
+ "FA9D", "77A7",
+ "FA9E", "7235",
+ "FA9F", "72AF",
+ "FAA0", "732A",
+ "FAA1", "7471",
+ "FAA2", "7506",
+ "FAA3", "753B",
+ "FAA4", "761D",
+ "FAA5", "761F",
+ "FAA6", "76CA",
+ "FAA7", "76DB",
+ "FAA8", "76F4",
+ "FAA9", "774A",
+ "FAAA", "7740",
+ "FAAB", "78CC",
+ "FAAC", "7AB1",
+ "FAAD", "7BC0",
+ "FAAE", "7C7B",
+ "FAAF", "7D5B",
+ "FAB0", "7DF4",
+ "FAB1", "7F3E",
+ "FAB2", "8005",
+ "FAB3", "8352",
+ "FAB4", "83EF",
+ "FAB5", "8779",
+ "FAB6", "8941",
+ "FAB7", "8986",
+ "FAB8", "8996",
+ "FAB9", "8ABF",
+ "FABA", "8AF8",
+ "FABB", "8ACB",
+ "FABC", "8B01",
+ "FABD", "8AFE",
+ "FABE", "8AED",
+ "FABF", "8B39",
+ "FAC0", "8B8A",
+ "FAC1", "8D08",
+ "FAC2", "8F38",
+ "FAC3", "9072",
+ "FAC4", "9199",
+ "FAC5", "9276",
+ "FAC6", "967C",
+ "FAC7", "96E3",
+ "FAC8", "9756",
+ "FAC9", "97DB",
+ "FACA", "97FF",
+ "FACB", "980B",
+ "FACC", "983B",
+ "FACD", "9B12",
+ "FACE", "9F9C",
+ "FACF", "2284A",
+ "FAD0", "22844",
+ "FAD1", "233D5",
+ "FAD2", "3B9D",
+ "FAD3", "4018",
+ "FAD4", "4039",
+ "FAD5", "25249",
+ "FAD6", "25CD0",
+ "FAD7", "27ED3",
+ "FAD8", "9F43",
+ "FAD9", "9F8E",
+ "FB1D", "05D9_05B4",
+ "FB1F", "05F2_05B7",
+ "FB2A", "05E9_05C1",
+ "FB2B", "05E9_05C2",
+ "FB2C", "05E9_05BC_05C1",
+ "FB2D", "05E9_05BC_05C2",
+ "FB2E", "05D0_05B7",
+ "FB2F", "05D0_05B8",
+ "FB30", "05D0_05BC",
+ "FB31", "05D1_05BC",
+ "FB32", "05D2_05BC",
+ "FB33", "05D3_05BC",
+ "FB34", "05D4_05BC",
+ "FB35", "05D5_05BC",
+ "FB36", "05D6_05BC",
+ "FB38", "05D8_05BC",
+ "FB39", "05D9_05BC",
+ "FB3A", "05DA_05BC",
+ "FB3B", "05DB_05BC",
+ "FB3C", "05DC_05BC",
+ "FB3E", "05DE_05BC",
+ "FB40", "05E0_05BC",
+ "FB41", "05E1_05BC",
+ "FB43", "05E3_05BC",
+ "FB44", "05E4_05BC",
+ "FB46", "05E6_05BC",
+ "FB47", "05E7_05BC",
+ "FB48", "05E8_05BC",
+ "FB49", "05E9_05BC",
+ "FB4A", "05EA_05BC",
+ "FB4B", "05D5_05B9",
+ "FB4C", "05D1_05BF",
+ "FB4D", "05DB_05BF",
+ "FB4E", "05E4_05BF",
+ "1109A", "11099_110BA",
+ "1109C", "1109B_110BA",
+ "110AB", "110A5_110BA",
+ "1112E", "11131_11127",
+ "1112F", "11132_11127",
+ "1134B", "11347_1133E",
+ "1134C", "11347_11357",
+ "114BB", "114B9_114BA",
+ "114BC", "114B9_114B0",
+ "114BE", "114B9_114BD",
+ "115BA", "115B8_115AF",
+ "115BB", "115B9_115AF",
+ "11938", "11935_11930",
+ "1D15E", "1D157_1D165",
+ "1D15F", "1D158_1D165",
+ "1D160", "1D158_1D165_1D16E",
+ "1D161", "1D158_1D165_1D16F",
+ "1D162", "1D158_1D165_1D170",
+ "1D163", "1D158_1D165_1D171",
+ "1D164", "1D158_1D165_1D172",
+ "1D1BB", "1D1B9_1D165",
+ "1D1BC", "1D1BA_1D165",
+ "1D1BD", "1D1B9_1D165_1D16E",
+ "1D1BE", "1D1BA_1D165_1D16E",
+ "1D1BF", "1D1B9_1D165_1D16F",
+ "1D1C0", "1D1BA_1D165_1D16F",
+ "2F800", "4E3D",
+ "2F801", "4E38",
+ "2F802", "4E41",
+ "2F803", "20122",
+ "2F804", "4F60",
+ "2F805", "4FAE",
+ "2F806", "4FBB",
+ "2F807", "5002",
+ "2F808", "507A",
+ "2F809", "5099",
+ "2F80A", "50E7",
+ "2F80B", "50CF",
+ "2F80C", "349E",
+ "2F80D", "2063A",
+ "2F80E", "514D",
+ "2F80F", "5154",
+ "2F810", "5164",
+ "2F811", "5177",
+ "2F812", "2051C",
+ "2F813", "34B9",
+ "2F814", "5167",
+ "2F815", "518D",
+ "2F816", "2054B",
+ "2F817", "5197",
+ "2F818", "51A4",
+ "2F819", "4ECC",
+ "2F81A", "51AC",
+ "2F81B", "51B5",
+ "2F81C", "291DF",
+ "2F81D", "51F5",
+ "2F81E", "5203",
+ "2F81F", "34DF",
+ "2F820", "523B",
+ "2F821", "5246",
+ "2F822", "5272",
+ "2F823", "5277",
+ "2F824", "3515",
+ "2F825", "52C7",
+ "2F826", "52C9",
+ "2F827", "52E4",
+ "2F828", "52FA",
+ "2F829", "5305",
+ "2F82A", "5306",
+ "2F82B", "5317",
+ "2F82C", "5349",
+ "2F82D", "5351",
+ "2F82E", "535A",
+ "2F82F", "5373",
+ "2F830", "537D",
+ "2F831", "537F",
+ "2F832", "537F",
+ "2F833", "537F",
+ "2F834", "20A2C",
+ "2F835", "7070",
+ "2F836", "53CA",
+ "2F837", "53DF",
+ "2F838", "20B63",
+ "2F839", "53EB",
+ "2F83A", "53F1",
+ "2F83B", "5406",
+ "2F83C", "549E",
+ "2F83D", "5438",
+ "2F83E", "5448",
+ "2F83F", "5468",
+ "2F840", "54A2",
+ "2F841", "54F6",
+ "2F842", "5510",
+ "2F843", "5553",
+ "2F844", "5563",
+ "2F845", "5584",
+ "2F846", "5584",
+ "2F847", "5599",
+ "2F848", "55AB",
+ "2F849", "55B3",
+ "2F84A", "55C2",
+ "2F84B", "5716",
+ "2F84C", "5606",
+ "2F84D", "5717",
+ "2F84E", "5651",
+ "2F84F", "5674",
+ "2F850", "5207",
+ "2F851", "58EE",
+ "2F852", "57CE",
+ "2F853", "57F4",
+ "2F854", "580D",
+ "2F855", "578B",
+ "2F856", "5832",
+ "2F857", "5831",
+ "2F858", "58AC",
+ "2F859", "214E4",
+ "2F85A", "58F2",
+ "2F85B", "58F7",
+ "2F85C", "5906",
+ "2F85D", "591A",
+ "2F85E", "5922",
+ "2F85F", "5962",
+ "2F860", "216A8",
+ "2F861", "216EA",
+ "2F862", "59EC",
+ "2F863", "5A1B",
+ "2F864", "5A27",
+ "2F865", "59D8",
+ "2F866", "5A66",
+ "2F867", "36EE",
+ "2F868", "36FC",
+ "2F869", "5B08",
+ "2F86A", "5B3E",
+ "2F86B", "5B3E",
+ "2F86C", "219C8",
+ "2F86D", "5BC3",
+ "2F86E", "5BD8",
+ "2F86F", "5BE7",
+ "2F870", "5BF3",
+ "2F871", "21B18",
+ "2F872", "5BFF",
+ "2F873", "5C06",
+ "2F874", "5F53",
+ "2F875", "5C22",
+ "2F876", "3781",
+ "2F877", "5C60",
+ "2F878", "5C6E",
+ "2F879", "5CC0",
+ "2F87A", "5C8D",
+ "2F87B", "21DE4",
+ "2F87C", "5D43",
+ "2F87D", "21DE6",
+ "2F87E", "5D6E",
+ "2F87F", "5D6B",
+ "2F880", "5D7C",
+ "2F881", "5DE1",
+ "2F882", "5DE2",
+ "2F883", "382F",
+ "2F884", "5DFD",
+ "2F885", "5E28",
+ "2F886", "5E3D",
+ "2F887", "5E69",
+ "2F888", "3862",
+ "2F889", "22183",
+ "2F88A", "387C",
+ "2F88B", "5EB0",
+ "2F88C", "5EB3",
+ "2F88D", "5EB6",
+ "2F88E", "5ECA",
+ "2F88F", "2A392",
+ "2F890", "5EFE",
+ "2F891", "22331",
+ "2F892", "22331",
+ "2F893", "8201",
+ "2F894", "5F22",
+ "2F895", "5F22",
+ "2F896", "38C7",
+ "2F897", "232B8",
+ "2F898", "261DA",
+ "2F899", "5F62",
+ "2F89A", "5F6B",
+ "2F89B", "38E3",
+ "2F89C", "5F9A",
+ "2F89D", "5FCD",
+ "2F89E", "5FD7",
+ "2F89F", "5FF9",
+ "2F8A0", "6081",
+ "2F8A1", "393A",
+ "2F8A2", "391C",
+ "2F8A3", "6094",
+ "2F8A4", "226D4",
+ "2F8A5", "60C7",
+ "2F8A6", "6148",
+ "2F8A7", "614C",
+ "2F8A8", "614E",
+ "2F8A9", "614C",
+ "2F8AA", "617A",
+ "2F8AB", "618E",
+ "2F8AC", "61B2",
+ "2F8AD", "61A4",
+ "2F8AE", "61AF",
+ "2F8AF", "61DE",
+ "2F8B0", "61F2",
+ "2F8B1", "61F6",
+ "2F8B2", "6210",
+ "2F8B3", "621B",
+ "2F8B4", "625D",
+ "2F8B5", "62B1",
+ "2F8B6", "62D4",
+ "2F8B7", "6350",
+ "2F8B8", "22B0C",
+ "2F8B9", "633D",
+ "2F8BA", "62FC",
+ "2F8BB", "6368",
+ "2F8BC", "6383",
+ "2F8BD", "63E4",
+ "2F8BE", "22BF1",
+ "2F8BF", "6422",
+ "2F8C0", "63C5",
+ "2F8C1", "63A9",
+ "2F8C2", "3A2E",
+ "2F8C3", "6469",
+ "2F8C4", "647E",
+ "2F8C5", "649D",
+ "2F8C6", "6477",
+ "2F8C7", "3A6C",
+ "2F8C8", "654F",
+ "2F8C9", "656C",
+ "2F8CA", "2300A",
+ "2F8CB", "65E3",
+ "2F8CC", "66F8",
+ "2F8CD", "6649",
+ "2F8CE", "3B19",
+ "2F8CF", "6691",
+ "2F8D0", "3B08",
+ "2F8D1", "3AE4",
+ "2F8D2", "5192",
+ "2F8D3", "5195",
+ "2F8D4", "6700",
+ "2F8D5", "669C",
+ "2F8D6", "80AD",
+ "2F8D7", "43D9",
+ "2F8D8", "6717",
+ "2F8D9", "671B",
+ "2F8DA", "6721",
+ "2F8DB", "675E",
+ "2F8DC", "6753",
+ "2F8DD", "233C3",
+ "2F8DE", "3B49",
+ "2F8DF", "67FA",
+ "2F8E0", "6785",
+ "2F8E1", "6852",
+ "2F8E2", "6885",
+ "2F8E3", "2346D",
+ "2F8E4", "688E",
+ "2F8E5", "681F",
+ "2F8E6", "6914",
+ "2F8E7", "3B9D",
+ "2F8E8", "6942",
+ "2F8E9", "69A3",
+ "2F8EA", "69EA",
+ "2F8EB", "6AA8",
+ "2F8EC", "236A3",
+ "2F8ED", "6ADB",
+ "2F8EE", "3C18",
+ "2F8EF", "6B21",
+ "2F8F0", "238A7",
+ "2F8F1", "6B54",
+ "2F8F2", "3C4E",
+ "2F8F3", "6B72",
+ "2F8F4", "6B9F",
+ "2F8F5", "6BBA",
+ "2F8F6", "6BBB",
+ "2F8F7", "23A8D",
+ "2F8F8", "21D0B",
+ "2F8F9", "23AFA",
+ "2F8FA", "6C4E",
+ "2F8FB", "23CBC",
+ "2F8FC", "6CBF",
+ "2F8FD", "6CCD",
+ "2F8FE", "6C67",
+ "2F8FF", "6D16",
+ "2F900", "6D3E",
+ "2F901", "6D77",
+ "2F902", "6D41",
+ "2F903", "6D69",
+ "2F904", "6D78",
+ "2F905", "6D85",
+ "2F906", "23D1E",
+ "2F907", "6D34",
+ "2F908", "6E2F",
+ "2F909", "6E6E",
+ "2F90A", "3D33",
+ "2F90B", "6ECB",
+ "2F90C", "6EC7",
+ "2F90D", "23ED1",
+ "2F90E", "6DF9",
+ "2F90F", "6F6E",
+ "2F910", "23F5E",
+ "2F911", "23F8E",
+ "2F912", "6FC6",
+ "2F913", "7039",
+ "2F914", "701E",
+ "2F915", "701B",
+ "2F916", "3D96",
+ "2F917", "704A",
+ "2F918", "707D",
+ "2F919", "7077",
+ "2F91A", "70AD",
+ "2F91B", "20525",
+ "2F91C", "7145",
+ "2F91D", "24263",
+ "2F91E", "719C",
+ "2F91F", "243AB",
+ "2F920", "7228",
+ "2F921", "7235",
+ "2F922", "7250",
+ "2F923", "24608",
+ "2F924", "7280",
+ "2F925", "7295",
+ "2F926", "24735",
+ "2F927", "24814",
+ "2F928", "737A",
+ "2F929", "738B",
+ "2F92A", "3EAC",
+ "2F92B", "73A5",
+ "2F92C", "3EB8",
+ "2F92D", "3EB8",
+ "2F92E", "7447",
+ "2F92F", "745C",
+ "2F930", "7471",
+ "2F931", "7485",
+ "2F932", "74CA",
+ "2F933", "3F1B",
+ "2F934", "7524",
+ "2F935", "24C36",
+ "2F936", "753E",
+ "2F937", "24C92",
+ "2F938", "7570",
+ "2F939", "2219F",
+ "2F93A", "7610",
+ "2F93B", "24FA1",
+ "2F93C", "24FB8",
+ "2F93D", "25044",
+ "2F93E", "3FFC",
+ "2F93F", "4008",
+ "2F940", "76F4",
+ "2F941", "250F3",
+ "2F942", "250F2",
+ "2F943", "25119",
+ "2F944", "25133",
+ "2F945", "771E",
+ "2F946", "771F",
+ "2F947", "771F",
+ "2F948", "774A",
+ "2F949", "4039",
+ "2F94A", "778B",
+ "2F94B", "4046",
+ "2F94C", "4096",
+ "2F94D", "2541D",
+ "2F94E", "784E",
+ "2F94F", "788C",
+ "2F950", "78CC",
+ "2F951", "40E3",
+ "2F952", "25626",
+ "2F953", "7956",
+ "2F954", "2569A",
+ "2F955", "256C5",
+ "2F956", "798F",
+ "2F957", "79EB",
+ "2F958", "412F",
+ "2F959", "7A40",
+ "2F95A", "7A4A",
+ "2F95B", "7A4F",
+ "2F95C", "2597C",
+ "2F95D", "25AA7",
+ "2F95E", "25AA7",
+ "2F95F", "7AEE",
+ "2F960", "4202",
+ "2F961", "25BAB",
+ "2F962", "7BC6",
+ "2F963", "7BC9",
+ "2F964", "4227",
+ "2F965", "25C80",
+ "2F966", "7CD2",
+ "2F967", "42A0",
+ "2F968", "7CE8",
+ "2F969", "7CE3",
+ "2F96A", "7D00",
+ "2F96B", "25F86",
+ "2F96C", "7D63",
+ "2F96D", "4301",
+ "2F96E", "7DC7",
+ "2F96F", "7E02",
+ "2F970", "7E45",
+ "2F971", "4334",
+ "2F972", "26228",
+ "2F973", "26247",
+ "2F974", "4359",
+ "2F975", "262D9",
+ "2F976", "7F7A",
+ "2F977", "2633E",
+ "2F978", "7F95",
+ "2F979", "7FFA",
+ "2F97A", "8005",
+ "2F97B", "264DA",
+ "2F97C", "26523",
+ "2F97D", "8060",
+ "2F97E", "265A8",
+ "2F97F", "8070",
+ "2F980", "2335F",
+ "2F981", "43D5",
+ "2F982", "80B2",
+ "2F983", "8103",
+ "2F984", "440B",
+ "2F985", "813E",
+ "2F986", "5AB5",
+ "2F987", "267A7",
+ "2F988", "267B5",
+ "2F989", "23393",
+ "2F98A", "2339C",
+ "2F98B", "8201",
+ "2F98C", "8204",
+ "2F98D", "8F9E",
+ "2F98E", "446B",
+ "2F98F", "8291",
+ "2F990", "828B",
+ "2F991", "829D",
+ "2F992", "52B3",
+ "2F993", "82B1",
+ "2F994", "82B3",
+ "2F995", "82BD",
+ "2F996", "82E6",
+ "2F997", "26B3C",
+ "2F998", "82E5",
+ "2F999", "831D",
+ "2F99A", "8363",
+ "2F99B", "83AD",
+ "2F99C", "8323",
+ "2F99D", "83BD",
+ "2F99E", "83E7",
+ "2F99F", "8457",
+ "2F9A0", "8353",
+ "2F9A1", "83CA",
+ "2F9A2", "83CC",
+ "2F9A3", "83DC",
+ "2F9A4", "26C36",
+ "2F9A5", "26D6B",
+ "2F9A6", "26CD5",
+ "2F9A7", "452B",
+ "2F9A8", "84F1",
+ "2F9A9", "84F3",
+ "2F9AA", "8516",
+ "2F9AB", "273CA",
+ "2F9AC", "8564",
+ "2F9AD", "26F2C",
+ "2F9AE", "455D",
+ "2F9AF", "4561",
+ "2F9B0", "26FB1",
+ "2F9B1", "270D2",
+ "2F9B2", "456B",
+ "2F9B3", "8650",
+ "2F9B4", "865C",
+ "2F9B5", "8667",
+ "2F9B6", "8669",
+ "2F9B7", "86A9",
+ "2F9B8", "8688",
+ "2F9B9", "870E",
+ "2F9BA", "86E2",
+ "2F9BB", "8779",
+ "2F9BC", "8728",
+ "2F9BD", "876B",
+ "2F9BE", "8786",
+ "2F9BF", "45D7",
+ "2F9C0", "87E1",
+ "2F9C1", "8801",
+ "2F9C2", "45F9",
+ "2F9C3", "8860",
+ "2F9C4", "8863",
+ "2F9C5", "27667",
+ "2F9C6", "88D7",
+ "2F9C7", "88DE",
+ "2F9C8", "4635",
+ "2F9C9", "88FA",
+ "2F9CA", "34BB",
+ "2F9CB", "278AE",
+ "2F9CC", "27966",
+ "2F9CD", "46BE",
+ "2F9CE", "46C7",
+ "2F9CF", "8AA0",
+ "2F9D0", "8AED",
+ "2F9D1", "8B8A",
+ "2F9D2", "8C55",
+ "2F9D3", "27CA8",
+ "2F9D4", "8CAB",
+ "2F9D5", "8CC1",
+ "2F9D6", "8D1B",
+ "2F9D7", "8D77",
+ "2F9D8", "27F2F",
+ "2F9D9", "20804",
+ "2F9DA", "8DCB",
+ "2F9DB", "8DBC",
+ "2F9DC", "8DF0",
+ "2F9DD", "208DE",
+ "2F9DE", "8ED4",
+ "2F9DF", "8F38",
+ "2F9E0", "285D2",
+ "2F9E1", "285ED",
+ "2F9E2", "9094",
+ "2F9E3", "90F1",
+ "2F9E4", "9111",
+ "2F9E5", "2872E",
+ "2F9E6", "911B",
+ "2F9E7", "9238",
+ "2F9E8", "92D7",
+ "2F9E9", "92D8",
+ "2F9EA", "927C",
+ "2F9EB", "93F9",
+ "2F9EC", "9415",
+ "2F9ED", "28BFA",
+ "2F9EE", "958B",
+ "2F9EF", "4995",
+ "2F9F0", "95B7",
+ "2F9F1", "28D77",
+ "2F9F2", "49E6",
+ "2F9F3", "96C3",
+ "2F9F4", "5DB2",
+ "2F9F5", "9723",
+ "2F9F6", "29145",
+ "2F9F7", "2921A",
+ "2F9F8", "4A6E",
+ "2F9F9", "4A76",
+ "2F9FA", "97E0",
+ "2F9FB", "2940A",
+ "2F9FC", "4AB2",
+ "2F9FD", "29496",
+ "2F9FE", "980B",
+ "2F9FF", "980B",
+ "2FA00", "9829",
+ "2FA01", "295B6",
+ "2FA02", "98E2",
+ "2FA03", "4B33",
+ "2FA04", "9929",
+ "2FA05", "99A7",
+ "2FA06", "99C2",
+ "2FA07", "99FE",
+ "2FA08", "4BCE",
+ "2FA09", "29B30",
+ "2FA0A", "9B12",
+ "2FA0B", "9C40",
+ "2FA0C", "9CFD",
+ "2FA0D", "4CCE",
+ "2FA0E", "4CED",
+ "2FA0F", "9D67",
+ "2FA10", "2A0CE",
+ "2FA11", "4CF8",
+ "2FA12", "2A105",
+ "2FA13", "2A20E",
+ "2FA14", "2A291",
+ "2FA15", "9EBB",
+ "2FA16", "4D56",
+ "2FA17", "9EF9",
+ "2FA18", "9EFE",
+ "2FA19", "9F05",
+ "2FA1A", "9F0F",
+ "2FA1B", "9F16",
+ "2FA1C", "9F3B",
+ "2FA1D", "2A600",
+);
+
+# This table was algorithmically derived from the Adobe Glyph List (AGL)
+# file 'glyphlist.txt' from the GitHub Adobe Type Tools agl-aglfn
+# project, on 2022-10-09.
+#
+# See "groff:" comments for altered mappings.
+my %AGL_to_unicode = (
+ "A", "0041",
+ "AE", "00C6",
+ "AEacute", "01FC",
+ "AEmacron", "01E2",
+ "Aacute", "00C1",
+ "Abreve", "0102",
+ "Abreveacute", "1EAE",
+ "Abrevecyrillic", "04D0",
+ "Abrevedotbelow", "1EB6",
+ "Abrevegrave", "1EB0",
+ "Abrevehookabove", "1EB2",
+ "Abrevetilde", "1EB4",
+ "Acaron", "01CD",
+ "Acircle", "24B6",
+ "Acircumflex", "00C2",
+ "Acircumflexacute", "1EA4",
+ "Acircumflexdotbelow", "1EAC",
+ "Acircumflexgrave", "1EA6",
+ "Acircumflexhookabove", "1EA8",
+ "Acircumflextilde", "1EAA",
+ "Acyrillic", "0410",
+ "Adblgrave", "0200",
+ "Adieresis", "00C4",
+ "Adieresiscyrillic", "04D2",
+ "Adieresismacron", "01DE",
+ "Adotbelow", "1EA0",
+ "Adotmacron", "01E0",
+ "Agrave", "00C0",
+ "Ahookabove", "1EA2",
+ "Aiecyrillic", "04D4",
+ "Ainvertedbreve", "0202",
+ "Alpha", "0391",
+ "Alphatonos", "0386",
+ "Amacron", "0100",
+ "Amonospace", "FF21",
+ "Aogonek", "0104",
+ "Aring", "00C5",
+ "Aringacute", "01FA",
+ "Aringbelow", "1E00",
+ "Atilde", "00C3",
+ "Aybarmenian", "0531",
+ "B", "0042",
+ "Bcircle", "24B7",
+ "Bdotaccent", "1E02",
+ "Bdotbelow", "1E04",
+ "Becyrillic", "0411",
+ "Benarmenian", "0532",
+ "Beta", "0392",
+ "Bhook", "0181",
+ "Blinebelow", "1E06",
+ "Bmonospace", "FF22",
+ "Btopbar", "0182",
+ "C", "0043",
+ "Caarmenian", "053E",
+ "Cacute", "0106",
+ "Ccaron", "010C",
+ "Ccedilla", "00C7",
+ "Ccedillaacute", "1E08",
+ "Ccircle", "24B8",
+ "Ccircumflex", "0108",
+ "Cdot", "010A",
+ "Cdotaccent", "010A",
+ "Chaarmenian", "0549",
+ "Cheabkhasiancyrillic", "04BC",
+ "Checyrillic", "0427",
+ "Chedescenderabkhasiancyrillic", "04BE",
+ "Chedescendercyrillic", "04B6",
+ "Chedieresiscyrillic", "04F4",
+ "Cheharmenian", "0543",
+ "Chekhakassiancyrillic", "04CB",
+ "Cheverticalstrokecyrillic", "04B8",
+ "Chi", "03A7",
+ "Chook", "0187",
+ "Cmonospace", "FF23",
+ "Coarmenian", "0551",
+ "D", "0044",
+ "DZ", "01F1",
+ "DZcaron", "01C4",
+ "Daarmenian", "0534",
+ "Dafrican", "0189",
+ "Dcaron", "010E",
+ "Dcedilla", "1E10",
+ "Dcircle", "24B9",
+ "Dcircumflexbelow", "1E12",
+ "Dcroat", "0110",
+ "Ddotaccent", "1E0A",
+ "Ddotbelow", "1E0C",
+ "Decyrillic", "0414",
+ "Deicoptic", "03EE",
+ "Delta", "0394", # groff: not U+2206
+ "Deltagreek", "0394",
+ "Dhook", "018A",
+ "Digammagreek", "03DC",
+ "Djecyrillic", "0402",
+ "Dlinebelow", "1E0E",
+ "Dmonospace", "FF24",
+ "Dslash", "0110",
+ "Dtopbar", "018B",
+ "Dz", "01F2",
+ "Dzcaron", "01C5",
+ "Dzeabkhasiancyrillic", "04E0",
+ "Dzecyrillic", "0405",
+ "Dzhecyrillic", "040F",
+ "E", "0045",
+ "Eacute", "00C9",
+ "Ebreve", "0114",
+ "Ecaron", "011A",
+ "Ecedillabreve", "1E1C",
+ "Echarmenian", "0535",
+ "Ecircle", "24BA",
+ "Ecircumflex", "00CA",
+ "Ecircumflexacute", "1EBE",
+ "Ecircumflexbelow", "1E18",
+ "Ecircumflexdotbelow", "1EC6",
+ "Ecircumflexgrave", "1EC0",
+ "Ecircumflexhookabove", "1EC2",
+ "Ecircumflextilde", "1EC4",
+ "Ecyrillic", "0404",
+ "Edblgrave", "0204",
+ "Edieresis", "00CB",
+ "Edot", "0116",
+ "Edotaccent", "0116",
+ "Edotbelow", "1EB8",
+ "Efcyrillic", "0424",
+ "Egrave", "00C8",
+ "Eharmenian", "0537",
+ "Ehookabove", "1EBA",
+ "Eightroman", "2167",
+ "Einvertedbreve", "0206",
+ "Eiotifiedcyrillic", "0464",
+ "Elcyrillic", "041B",
+ "Elevenroman", "216A",
+ "Emacron", "0112",
+ "Emacronacute", "1E16",
+ "Emacrongrave", "1E14",
+ "Emcyrillic", "041C",
+ "Emonospace", "FF25",
+ "Encyrillic", "041D",
+ "Endescendercyrillic", "04A2",
+ "Eng", "014A",
+ "Enghecyrillic", "04A4",
+ "Enhookcyrillic", "04C7",
+ "Eogonek", "0118",
+ "Eopen", "0190",
+ "Epsilon", "0395",
+ "Epsilontonos", "0388",
+ "Ercyrillic", "0420",
+ "Ereversed", "018E",
+ "Ereversedcyrillic", "042D",
+ "Escyrillic", "0421",
+ "Esdescendercyrillic", "04AA",
+ "Esh", "01A9",
+ "Eta", "0397",
+ "Etarmenian", "0538",
+ "Etatonos", "0389",
+ "Eth", "00D0",
+ "Etilde", "1EBC",
+ "Etildebelow", "1E1A",
+ "Euro", "20AC",
+ "Ezh", "01B7",
+ "Ezhcaron", "01EE",
+ "Ezhreversed", "01B8",
+ "F", "0046",
+ "Fcircle", "24BB",
+ "Fdotaccent", "1E1E",
+ "Feharmenian", "0556",
+ "Feicoptic", "03E4",
+ "Fhook", "0191",
+ "Fitacyrillic", "0472",
+ "Fiveroman", "2164",
+ "Fmonospace", "FF26",
+ "Fourroman", "2163",
+ "G", "0047",
+ "GBsquare", "3387",
+ "Gacute", "01F4",
+ "Gamma", "0393",
+ "Gammaafrican", "0194",
+ "Gangiacoptic", "03EA",
+ "Gbreve", "011E",
+ "Gcaron", "01E6",
+ "Gcedilla", "0122",
+ "Gcircle", "24BC",
+ "Gcircumflex", "011C",
+ "Gcommaaccent", "0122",
+ "Gdot", "0120",
+ "Gdotaccent", "0120",
+ "Gecyrillic", "0413",
+ "Ghadarmenian", "0542",
+ "Ghemiddlehookcyrillic", "0494",
+ "Ghestrokecyrillic", "0492",
+ "Gheupturncyrillic", "0490",
+ "Ghook", "0193",
+ "Gimarmenian", "0533",
+ "Gjecyrillic", "0403",
+ "Gmacron", "1E20",
+ "Gmonospace", "FF27",
+ "Gsmallhook", "029B",
+ "Gstroke", "01E4",
+ "H", "0048",
+ "H18533", "25CF",
+ "H18543", "25AA",
+ "H18551", "25AB",
+ "H22073", "25A1",
+ "HPsquare", "33CB",
+ "Haabkhasiancyrillic", "04A8",
+ "Hadescendercyrillic", "04B2",
+ "Hardsigncyrillic", "042A",
+ "Hbar", "0126",
+ "Hbrevebelow", "1E2A",
+ "Hcedilla", "1E28",
+ "Hcircle", "24BD",
+ "Hcircumflex", "0124",
+ "Hdieresis", "1E26",
+ "Hdotaccent", "1E22",
+ "Hdotbelow", "1E24",
+ "Hmonospace", "FF28",
+ "Hoarmenian", "0540",
+ "Horicoptic", "03E8",
+ "Hzsquare", "3390",
+ "I", "0049",
+ "IAcyrillic", "042F",
+ "IJ", "0132",
+ "IUcyrillic", "042E",
+ "Iacute", "00CD",
+ "Ibreve", "012C",
+ "Icaron", "01CF",
+ "Icircle", "24BE",
+ "Icircumflex", "00CE",
+ "Icyrillic", "0406",
+ "Idblgrave", "0208",
+ "Idieresis", "00CF",
+ "Idieresisacute", "1E2E",
+ "Idieresiscyrillic", "04E4",
+ "Idot", "0130",
+ "Idotaccent", "0130",
+ "Idotbelow", "1ECA",
+ "Iebrevecyrillic", "04D6",
+ "Iecyrillic", "0415",
+ "Ifraktur", "2111",
+ "Igrave", "00CC",
+ "Ihookabove", "1EC8",
+ "Iicyrillic", "0418",
+ "Iinvertedbreve", "020A",
+ "Iishortcyrillic", "0419",
+ "Imacron", "012A",
+ "Imacroncyrillic", "04E2",
+ "Imonospace", "FF29",
+ "Iniarmenian", "053B",
+ "Iocyrillic", "0401",
+ "Iogonek", "012E",
+ "Iota", "0399",
+ "Iotaafrican", "0196",
+ "Iotadieresis", "03AA",
+ "Iotatonos", "038A",
+ "Istroke", "0197",
+ "Itilde", "0128",
+ "Itildebelow", "1E2C",
+ "Izhitsacyrillic", "0474",
+ "Izhitsadblgravecyrillic", "0476",
+ "J", "004A",
+ "Jaarmenian", "0541",
+ "Jcircle", "24BF",
+ "Jcircumflex", "0134",
+ "Jecyrillic", "0408",
+ "Jheharmenian", "054B",
+ "Jmonospace", "FF2A",
+ "K", "004B",
+ "KBsquare", "3385",
+ "KKsquare", "33CD",
+ "Kabashkircyrillic", "04A0",
+ "Kacute", "1E30",
+ "Kacyrillic", "041A",
+ "Kadescendercyrillic", "049A",
+ "Kahookcyrillic", "04C3",
+ "Kappa", "039A",
+ "Kastrokecyrillic", "049E",
+ "Kaverticalstrokecyrillic", "049C",
+ "Kcaron", "01E8",
+ "Kcedilla", "0136",
+ "Kcircle", "24C0",
+ "Kcommaaccent", "0136",
+ "Kdotbelow", "1E32",
+ "Keharmenian", "0554",
+ "Kenarmenian", "053F",
+ "Khacyrillic", "0425",
+ "Kheicoptic", "03E6",
+ "Khook", "0198",
+ "Kjecyrillic", "040C",
+ "Klinebelow", "1E34",
+ "Kmonospace", "FF2B",
+ "Koppacyrillic", "0480",
+ "Koppagreek", "03DE",
+ "Ksicyrillic", "046E",
+ "L", "004C",
+ "LJ", "01C7",
+ "Lacute", "0139",
+ "Lambda", "039B",
+ "Lcaron", "013D",
+ "Lcedilla", "013B",
+ "Lcircle", "24C1",
+ "Lcircumflexbelow", "1E3C",
+ "Lcommaaccent", "013B",
+ "Ldot", "013F",
+ "Ldotaccent", "013F",
+ "Ldotbelow", "1E36",
+ "Ldotbelowmacron", "1E38",
+ "Liwnarmenian", "053C",
+ "Lj", "01C8",
+ "Ljecyrillic", "0409",
+ "Llinebelow", "1E3A",
+ "Lmonospace", "FF2C",
+ "Lslash", "0141",
+ "M", "004D",
+ "MBsquare", "3386",
+ "Macute", "1E3E",
+ "Mcircle", "24C2",
+ "Mdotaccent", "1E40",
+ "Mdotbelow", "1E42",
+ "Menarmenian", "0544",
+ "Mmonospace", "FF2D",
+ "Mturned", "019C",
+ "Mu", "039C",
+ "N", "004E",
+ "NJ", "01CA",
+ "Nacute", "0143",
+ "Ncaron", "0147",
+ "Ncedilla", "0145",
+ "Ncircle", "24C3",
+ "Ncircumflexbelow", "1E4A",
+ "Ncommaaccent", "0145",
+ "Ndotaccent", "1E44",
+ "Ndotbelow", "1E46",
+ "Nhookleft", "019D",
+ "Nineroman", "2168",
+ "Nj", "01CB",
+ "Njecyrillic", "040A",
+ "Nlinebelow", "1E48",
+ "Nmonospace", "FF2E",
+ "Nowarmenian", "0546",
+ "Ntilde", "00D1",
+ "Nu", "039D",
+ "O", "004F",
+ "OE", "0152",
+ "Oacute", "00D3",
+ "Obarredcyrillic", "04E8",
+ "Obarreddieresiscyrillic", "04EA",
+ "Obreve", "014E",
+ "Ocaron", "01D1",
+ "Ocenteredtilde", "019F",
+ "Ocircle", "24C4",
+ "Ocircumflex", "00D4",
+ "Ocircumflexacute", "1ED0",
+ "Ocircumflexdotbelow", "1ED8",
+ "Ocircumflexgrave", "1ED2",
+ "Ocircumflexhookabove", "1ED4",
+ "Ocircumflextilde", "1ED6",
+ "Ocyrillic", "041E",
+ "Odblacute", "0150",
+ "Odblgrave", "020C",
+ "Odieresis", "00D6",
+ "Odieresiscyrillic", "04E6",
+ "Odotbelow", "1ECC",
+ "Ograve", "00D2",
+ "Oharmenian", "0555",
+ "Ohm", "2126",
+ "Ohookabove", "1ECE",
+ "Ohorn", "01A0",
+ "Ohornacute", "1EDA",
+ "Ohorndotbelow", "1EE2",
+ "Ohorngrave", "1EDC",
+ "Ohornhookabove", "1EDE",
+ "Ohorntilde", "1EE0",
+ "Ohungarumlaut", "0150",
+ "Oi", "01A2",
+ "Oinvertedbreve", "020E",
+ "Omacron", "014C",
+ "Omacronacute", "1E52",
+ "Omacrongrave", "1E50",
+ "Omega", "03A9", # groff: not U+2126
+ "Omegacyrillic", "0460",
+ "Omegagreek", "03A9",
+ "Omegaroundcyrillic", "047A",
+ "Omegatitlocyrillic", "047C",
+ "Omegatonos", "038F",
+ "Omicron", "039F",
+ "Omicrontonos", "038C",
+ "Omonospace", "FF2F",
+ "Oneroman", "2160",
+ "Oogonek", "01EA",
+ "Oogonekmacron", "01EC",
+ "Oopen", "0186",
+ "Oslash", "00D8",
+ "Oslashacute", "01FE",
+ "Ostrokeacute", "01FE",
+ "Otcyrillic", "047E",
+ "Otilde", "00D5",
+ "Otildeacute", "1E4C",
+ "Otildedieresis", "1E4E",
+ "P", "0050",
+ "Pacute", "1E54",
+ "Pcircle", "24C5",
+ "Pdotaccent", "1E56",
+ "Pecyrillic", "041F",
+ "Peharmenian", "054A",
+ "Pemiddlehookcyrillic", "04A6",
+ "Phi", "03A6",
+ "Phook", "01A4",
+ "Pi", "03A0",
+ "Piwrarmenian", "0553",
+ "Pmonospace", "FF30",
+ "Psi", "03A8",
+ "Psicyrillic", "0470",
+ "Q", "0051",
+ "Qcircle", "24C6",
+ "Qmonospace", "FF31",
+ "R", "0052",
+ "Raarmenian", "054C",
+ "Racute", "0154",
+ "Rcaron", "0158",
+ "Rcedilla", "0156",
+ "Rcircle", "24C7",
+ "Rcommaaccent", "0156",
+ "Rdblgrave", "0210",
+ "Rdotaccent", "1E58",
+ "Rdotbelow", "1E5A",
+ "Rdotbelowmacron", "1E5C",
+ "Reharmenian", "0550",
+ "Rfraktur", "211C",
+ "Rho", "03A1",
+ "Rinvertedbreve", "0212",
+ "Rlinebelow", "1E5E",
+ "Rmonospace", "FF32",
+ "Rsmallinverted", "0281",
+ "Rsmallinvertedsuperior", "02B6",
+ "S", "0053",
+ "SF010000", "250C",
+ "SF020000", "2514",
+ "SF030000", "2510",
+ "SF040000", "2518",
+ "SF050000", "253C",
+ "SF060000", "252C",
+ "SF070000", "2534",
+ "SF080000", "251C",
+ "SF090000", "2524",
+ "SF100000", "2500",
+ "SF110000", "2502",
+ "SF190000", "2561",
+ "SF200000", "2562",
+ "SF210000", "2556",
+ "SF220000", "2555",
+ "SF230000", "2563",
+ "SF240000", "2551",
+ "SF250000", "2557",
+ "SF260000", "255D",
+ "SF270000", "255C",
+ "SF280000", "255B",
+ "SF360000", "255E",
+ "SF370000", "255F",
+ "SF380000", "255A",
+ "SF390000", "2554",
+ "SF400000", "2569",
+ "SF410000", "2566",
+ "SF420000", "2560",
+ "SF430000", "2550",
+ "SF440000", "256C",
+ "SF450000", "2567",
+ "SF460000", "2568",
+ "SF470000", "2564",
+ "SF480000", "2565",
+ "SF490000", "2559",
+ "SF500000", "2558",
+ "SF510000", "2552",
+ "SF520000", "2553",
+ "SF530000", "256B",
+ "SF540000", "256A",
+ "Sacute", "015A",
+ "Sacutedotaccent", "1E64",
+ "Sampigreek", "03E0",
+ "Scaron", "0160",
+ "Scarondotaccent", "1E66",
+ "Scedilla", "015E",
+ "Schwa", "018F",
+ "Schwacyrillic", "04D8",
+ "Schwadieresiscyrillic", "04DA",
+ "Scircle", "24C8",
+ "Scircumflex", "015C",
+ "Scommaaccent", "0218",
+ "Sdotaccent", "1E60",
+ "Sdotbelow", "1E62",
+ "Sdotbelowdotaccent", "1E68",
+ "Seharmenian", "054D",
+ "Sevenroman", "2166",
+ "Shaarmenian", "0547",
+ "Shacyrillic", "0428",
+ "Shchacyrillic", "0429",
+ "Sheicoptic", "03E2",
+ "Shhacyrillic", "04BA",
+ "Shimacoptic", "03EC",
+ "Sigma", "03A3",
+ "Sixroman", "2165",
+ "Smonospace", "FF33",
+ "Softsigncyrillic", "042C",
+ "Stigmagreek", "03DA",
+ "T", "0054",
+ "Tau", "03A4",
+ "Tbar", "0166",
+ "Tcaron", "0164",
+ "Tcedilla", "0162",
+ "Tcircle", "24C9",
+ "Tcircumflexbelow", "1E70",
+ "Tcommaaccent", "0162",
+ "Tdotaccent", "1E6A",
+ "Tdotbelow", "1E6C",
+ "Tecyrillic", "0422",
+ "Tedescendercyrillic", "04AC",
+ "Tenroman", "2169",
+ "Tetsecyrillic", "04B4",
+ "Theta", "0398",
+ "Thook", "01AC",
+ "Thorn", "00DE",
+ "Threeroman", "2162",
+ "Tiwnarmenian", "054F",
+ "Tlinebelow", "1E6E",
+ "Tmonospace", "FF34",
+ "Toarmenian", "0539",
+ "Tonefive", "01BC",
+ "Tonesix", "0184",
+ "Tonetwo", "01A7",
+ "Tretroflexhook", "01AE",
+ "Tsecyrillic", "0426",
+ "Tshecyrillic", "040B",
+ "Twelveroman", "216B",
+ "Tworoman", "2161",
+ "U", "0055",
+ "Uacute", "00DA",
+ "Ubreve", "016C",
+ "Ucaron", "01D3",
+ "Ucircle", "24CA",
+ "Ucircumflex", "00DB",
+ "Ucircumflexbelow", "1E76",
+ "Ucyrillic", "0423",
+ "Udblacute", "0170",
+ "Udblgrave", "0214",
+ "Udieresis", "00DC",
+ "Udieresisacute", "01D7",
+ "Udieresisbelow", "1E72",
+ "Udieresiscaron", "01D9",
+ "Udieresiscyrillic", "04F0",
+ "Udieresisgrave", "01DB",
+ "Udieresismacron", "01D5",
+ "Udotbelow", "1EE4",
+ "Ugrave", "00D9",
+ "Uhookabove", "1EE6",
+ "Uhorn", "01AF",
+ "Uhornacute", "1EE8",
+ "Uhorndotbelow", "1EF0",
+ "Uhorngrave", "1EEA",
+ "Uhornhookabove", "1EEC",
+ "Uhorntilde", "1EEE",
+ "Uhungarumlaut", "0170",
+ "Uhungarumlautcyrillic", "04F2",
+ "Uinvertedbreve", "0216",
+ "Ukcyrillic", "0478",
+ "Umacron", "016A",
+ "Umacroncyrillic", "04EE",
+ "Umacrondieresis", "1E7A",
+ "Umonospace", "FF35",
+ "Uogonek", "0172",
+ "Upsilon", "03A5",
+ "Upsilon1", "03D2",
+ "Upsilonacutehooksymbolgreek", "03D3",
+ "Upsilonafrican", "01B1",
+ "Upsilondieresis", "03AB",
+ "Upsilondieresishooksymbolgreek", "03D4",
+ "Upsilonhooksymbol", "03D2",
+ "Upsilontonos", "038E",
+ "Uring", "016E",
+ "Ushortcyrillic", "040E",
+ "Ustraightcyrillic", "04AE",
+ "Ustraightstrokecyrillic", "04B0",
+ "Utilde", "0168",
+ "Utildeacute", "1E78",
+ "Utildebelow", "1E74",
+ "V", "0056",
+ "Vcircle", "24CB",
+ "Vdotbelow", "1E7E",
+ "Vecyrillic", "0412",
+ "Vewarmenian", "054E",
+ "Vhook", "01B2",
+ "Vmonospace", "FF36",
+ "Voarmenian", "0548",
+ "Vtilde", "1E7C",
+ "W", "0057",
+ "Wacute", "1E82",
+ "Wcircle", "24CC",
+ "Wcircumflex", "0174",
+ "Wdieresis", "1E84",
+ "Wdotaccent", "1E86",
+ "Wdotbelow", "1E88",
+ "Wgrave", "1E80",
+ "Wmonospace", "FF37",
+ "X", "0058",
+ "Xcircle", "24CD",
+ "Xdieresis", "1E8C",
+ "Xdotaccent", "1E8A",
+ "Xeharmenian", "053D",
+ "Xi", "039E",
+ "Xmonospace", "FF38",
+ "Y", "0059",
+ "Yacute", "00DD",
+ "Yatcyrillic", "0462",
+ "Ycircle", "24CE",
+ "Ycircumflex", "0176",
+ "Ydieresis", "0178",
+ "Ydotaccent", "1E8E",
+ "Ydotbelow", "1EF4",
+ "Yericyrillic", "042B",
+ "Yerudieresiscyrillic", "04F8",
+ "Ygrave", "1EF2",
+ "Yhook", "01B3",
+ "Yhookabove", "1EF6",
+ "Yiarmenian", "0545",
+ "Yicyrillic", "0407",
+ "Yiwnarmenian", "0552",
+ "Ymonospace", "FF39",
+ "Ytilde", "1EF8",
+ "Yusbigcyrillic", "046A",
+ "Yusbigiotifiedcyrillic", "046C",
+ "Yuslittlecyrillic", "0466",
+ "Yuslittleiotifiedcyrillic", "0468",
+ "Z", "005A",
+ "Zaarmenian", "0536",
+ "Zacute", "0179",
+ "Zcaron", "017D",
+ "Zcircle", "24CF",
+ "Zcircumflex", "1E90",
+ "Zdot", "017B",
+ "Zdotaccent", "017B",
+ "Zdotbelow", "1E92",
+ "Zecyrillic", "0417",
+ "Zedescendercyrillic", "0498",
+ "Zedieresiscyrillic", "04DE",
+ "Zeta", "0396",
+ "Zhearmenian", "053A",
+ "Zhebrevecyrillic", "04C1",
+ "Zhecyrillic", "0416",
+ "Zhedescendercyrillic", "0496",
+ "Zhedieresiscyrillic", "04DC",
+ "Zlinebelow", "1E94",
+ "Zmonospace", "FF3A",
+ "Zstroke", "01B5",
+ "a", "0061",
+ "aabengali", "0986",
+ "aacute", "00E1",
+ "aadeva", "0906",
+ "aagujarati", "0A86",
+ "aagurmukhi", "0A06",
+ "aamatragurmukhi", "0A3E",
+ "aarusquare", "3303",
+ "aavowelsignbengali", "09BE",
+ "aavowelsigndeva", "093E",
+ "aavowelsigngujarati", "0ABE",
+ "abbreviationmarkarmenian", "055F",
+ "abbreviationsigndeva", "0970",
+ "abengali", "0985",
+ "abopomofo", "311A",
+ "abreve", "0103",
+ "abreveacute", "1EAF",
+ "abrevecyrillic", "04D1",
+ "abrevedotbelow", "1EB7",
+ "abrevegrave", "1EB1",
+ "abrevehookabove", "1EB3",
+ "abrevetilde", "1EB5",
+ "acaron", "01CE",
+ "acircle", "24D0",
+ "acircumflex", "00E2",
+ "acircumflexacute", "1EA5",
+ "acircumflexdotbelow", "1EAD",
+ "acircumflexgrave", "1EA7",
+ "acircumflexhookabove", "1EA9",
+ "acircumflextilde", "1EAB",
+ "acute", "00B4",
+ "acutebelowcmb", "0317",
+ "acutecmb", "0301",
+ "acutecomb", "0301",
+ "acutedeva", "0954",
+ "acutelowmod", "02CF",
+ "acutetonecmb", "0341",
+ "acyrillic", "0430",
+ "adblgrave", "0201",
+ "addakgurmukhi", "0A71",
+ "adeva", "0905",
+ "adieresis", "00E4",
+ "adieresiscyrillic", "04D3",
+ "adieresismacron", "01DF",
+ "adotbelow", "1EA1",
+ "adotmacron", "01E1",
+ "ae", "00E6",
+ "aeacute", "01FD",
+ "aekorean", "3150",
+ "aemacron", "01E3",
+ "afii00208", "2015",
+ "afii08941", "20A4",
+ "afii10017", "0410",
+ "afii10018", "0411",
+ "afii10019", "0412",
+ "afii10020", "0413",
+ "afii10021", "0414",
+ "afii10022", "0415",
+ "afii10023", "0401",
+ "afii10024", "0416",
+ "afii10025", "0417",
+ "afii10026", "0418",
+ "afii10027", "0419",
+ "afii10028", "041A",
+ "afii10029", "041B",
+ "afii10030", "041C",
+ "afii10031", "041D",
+ "afii10032", "041E",
+ "afii10033", "041F",
+ "afii10034", "0420",
+ "afii10035", "0421",
+ "afii10036", "0422",
+ "afii10037", "0423",
+ "afii10038", "0424",
+ "afii10039", "0425",
+ "afii10040", "0426",
+ "afii10041", "0427",
+ "afii10042", "0428",
+ "afii10043", "0429",
+ "afii10044", "042A",
+ "afii10045", "042B",
+ "afii10046", "042C",
+ "afii10047", "042D",
+ "afii10048", "042E",
+ "afii10049", "042F",
+ "afii10050", "0490",
+ "afii10051", "0402",
+ "afii10052", "0403",
+ "afii10053", "0404",
+ "afii10054", "0405",
+ "afii10055", "0406",
+ "afii10056", "0407",
+ "afii10057", "0408",
+ "afii10058", "0409",
+ "afii10059", "040A",
+ "afii10060", "040B",
+ "afii10061", "040C",
+ "afii10062", "040E",
+ "afii10065", "0430",
+ "afii10066", "0431",
+ "afii10067", "0432",
+ "afii10068", "0433",
+ "afii10069", "0434",
+ "afii10070", "0435",
+ "afii10071", "0451",
+ "afii10072", "0436",
+ "afii10073", "0437",
+ "afii10074", "0438",
+ "afii10075", "0439",
+ "afii10076", "043A",
+ "afii10077", "043B",
+ "afii10078", "043C",
+ "afii10079", "043D",
+ "afii10080", "043E",
+ "afii10081", "043F",
+ "afii10082", "0440",
+ "afii10083", "0441",
+ "afii10084", "0442",
+ "afii10085", "0443",
+ "afii10086", "0444",
+ "afii10087", "0445",
+ "afii10088", "0446",
+ "afii10089", "0447",
+ "afii10090", "0448",
+ "afii10091", "0449",
+ "afii10092", "044A",
+ "afii10093", "044B",
+ "afii10094", "044C",
+ "afii10095", "044D",
+ "afii10096", "044E",
+ "afii10097", "044F",
+ "afii10098", "0491",
+ "afii10099", "0452",
+ "afii10100", "0453",
+ "afii10101", "0454",
+ "afii10102", "0455",
+ "afii10103", "0456",
+ "afii10104", "0457",
+ "afii10105", "0458",
+ "afii10106", "0459",
+ "afii10107", "045A",
+ "afii10108", "045B",
+ "afii10109", "045C",
+ "afii10110", "045E",
+ "afii10145", "040F",
+ "afii10146", "0462",
+ "afii10147", "0472",
+ "afii10148", "0474",
+ "afii10193", "045F",
+ "afii10194", "0463",
+ "afii10195", "0473",
+ "afii10196", "0475",
+ "afii10846", "04D9",
+ "afii299", "200E",
+ "afii300", "200F",
+ "afii301", "200D",
+ "afii57381", "066A",
+ "afii57388", "060C",
+ "afii57392", "0660",
+ "afii57393", "0661",
+ "afii57394", "0662",
+ "afii57395", "0663",
+ "afii57396", "0664",
+ "afii57397", "0665",
+ "afii57398", "0666",
+ "afii57399", "0667",
+ "afii57400", "0668",
+ "afii57401", "0669",
+ "afii57403", "061B",
+ "afii57407", "061F",
+ "afii57409", "0621",
+ "afii57410", "0622",
+ "afii57411", "0623",
+ "afii57412", "0624",
+ "afii57413", "0625",
+ "afii57414", "0626",
+ "afii57415", "0627",
+ "afii57416", "0628",
+ "afii57417", "0629",
+ "afii57418", "062A",
+ "afii57419", "062B",
+ "afii57420", "062C",
+ "afii57421", "062D",
+ "afii57422", "062E",
+ "afii57423", "062F",
+ "afii57424", "0630",
+ "afii57425", "0631",
+ "afii57426", "0632",
+ "afii57427", "0633",
+ "afii57428", "0634",
+ "afii57429", "0635",
+ "afii57430", "0636",
+ "afii57431", "0637",
+ "afii57432", "0638",
+ "afii57433", "0639",
+ "afii57434", "063A",
+ "afii57440", "0640",
+ "afii57441", "0641",
+ "afii57442", "0642",
+ "afii57443", "0643",
+ "afii57444", "0644",
+ "afii57445", "0645",
+ "afii57446", "0646",
+ "afii57448", "0648",
+ "afii57449", "0649",
+ "afii57450", "064A",
+ "afii57451", "064B",
+ "afii57452", "064C",
+ "afii57453", "064D",
+ "afii57454", "064E",
+ "afii57455", "064F",
+ "afii57456", "0650",
+ "afii57457", "0651",
+ "afii57458", "0652",
+ "afii57470", "0647",
+ "afii57505", "06A4",
+ "afii57506", "067E",
+ "afii57507", "0686",
+ "afii57508", "0698",
+ "afii57509", "06AF",
+ "afii57511", "0679",
+ "afii57512", "0688",
+ "afii57513", "0691",
+ "afii57514", "06BA",
+ "afii57519", "06D2",
+ "afii57534", "06D5",
+ "afii57636", "20AA",
+ "afii57645", "05BE",
+ "afii57658", "05C3",
+ "afii57664", "05D0",
+ "afii57665", "05D1",
+ "afii57666", "05D2",
+ "afii57667", "05D3",
+ "afii57668", "05D4",
+ "afii57669", "05D5",
+ "afii57670", "05D6",
+ "afii57671", "05D7",
+ "afii57672", "05D8",
+ "afii57673", "05D9",
+ "afii57674", "05DA",
+ "afii57675", "05DB",
+ "afii57676", "05DC",
+ "afii57677", "05DD",
+ "afii57678", "05DE",
+ "afii57679", "05DF",
+ "afii57680", "05E0",
+ "afii57681", "05E1",
+ "afii57682", "05E2",
+ "afii57683", "05E3",
+ "afii57684", "05E4",
+ "afii57685", "05E5",
+ "afii57686", "05E6",
+ "afii57687", "05E7",
+ "afii57688", "05E8",
+ "afii57689", "05E9",
+ "afii57690", "05EA",
+ "afii57694", "FB2A",
+ "afii57695", "FB2B",
+ "afii57700", "FB4B",
+ "afii57705", "FB1F",
+ "afii57716", "05F0",
+ "afii57717", "05F1",
+ "afii57718", "05F2",
+ "afii57723", "FB35",
+ "afii57793", "05B4",
+ "afii57794", "05B5",
+ "afii57795", "05B6",
+ "afii57796", "05BB",
+ "afii57797", "05B8",
+ "afii57798", "05B7",
+ "afii57799", "05B0",
+ "afii57800", "05B2",
+ "afii57801", "05B1",
+ "afii57802", "05B3",
+ "afii57803", "05C2",
+ "afii57804", "05C1",
+ "afii57806", "05B9",
+ "afii57807", "05BC",
+ "afii57839", "05BD",
+ "afii57841", "05BF",
+ "afii57842", "05C0",
+ "afii57929", "02BC",
+ "afii61248", "2105",
+ "afii61289", "2113",
+ "afii61352", "2116",
+ "afii61573", "202C",
+ "afii61574", "202D",
+ "afii61575", "202E",
+ "afii61664", "200C",
+ "afii63167", "066D",
+ "afii64937", "02BD",
+ "agrave", "00E0",
+ "agujarati", "0A85",
+ "agurmukhi", "0A05",
+ "ahiragana", "3042",
+ "ahookabove", "1EA3",
+ "aibengali", "0990",
+ "aibopomofo", "311E",
+ "aideva", "0910",
+ "aiecyrillic", "04D5",
+ "aigujarati", "0A90",
+ "aigurmukhi", "0A10",
+ "aimatragurmukhi", "0A48",
+ "ainarabic", "0639",
+ "ainfinalarabic", "FECA",
+ "aininitialarabic", "FECB",
+ "ainmedialarabic", "FECC",
+ "ainvertedbreve", "0203",
+ "aivowelsignbengali", "09C8",
+ "aivowelsigndeva", "0948",
+ "aivowelsigngujarati", "0AC8",
+ "akatakana", "30A2",
+ "akatakanahalfwidth", "FF71",
+ "akorean", "314F",
+ "alef", "05D0",
+ "alefarabic", "0627",
+ "alefdageshhebrew", "FB30",
+ "aleffinalarabic", "FE8E",
+ "alefhamzaabovearabic", "0623",
+ "alefhamzaabovefinalarabic", "FE84",
+ "alefhamzabelowarabic", "0625",
+ "alefhamzabelowfinalarabic", "FE88",
+ "alefhebrew", "05D0",
+ "aleflamedhebrew", "FB4F",
+ "alefmaddaabovearabic", "0622",
+ "alefmaddaabovefinalarabic", "FE82",
+ "alefmaksuraarabic", "0649",
+ "alefmaksurafinalarabic", "FEF0",
+ "alefmaksurainitialarabic", "FEF3",
+ "alefmaksuramedialarabic", "FEF4",
+ "alefpatahhebrew", "FB2E",
+ "alefqamatshebrew", "FB2F",
+ "aleph", "2135",
+ "allequal", "224C",
+ "alpha", "03B1",
+ "alphatonos", "03AC",
+ "amacron", "0101",
+ "amonospace", "FF41",
+ "ampersand", "0026",
+ "ampersandmonospace", "FF06",
+ "amsquare", "33C2",
+ "anbopomofo", "3122",
+ "angbopomofo", "3124",
+ "angkhankhuthai", "0E5A",
+ "angle", "2220",
+ "anglebracketleft", "3008",
+ "anglebracketleftvertical", "FE3F",
+ "anglebracketright", "3009",
+ "anglebracketrightvertical", "FE40",
+ "angleleft", "2329",
+ "angleright", "232A",
+ "angstrom", "212B",
+ "anoteleia", "0387",
+ "anudattadeva", "0952",
+ "anusvarabengali", "0982",
+ "anusvaradeva", "0902",
+ "anusvaragujarati", "0A82",
+ "aogonek", "0105",
+ "apaatosquare", "3300",
+ "aparen", "249C",
+ "apostrophearmenian", "055A",
+ "apostrophemod", "02BC",
+ "approaches", "2250",
+ "approxequal", "2248",
+ "approxequalorimage", "2252",
+ "approximatelyequal", "2245",
+ "araeaekorean", "318E",
+ "araeakorean", "318D",
+ "arc", "2312",
+ "arighthalfring", "1E9A",
+ "aring", "00E5",
+ "aringacute", "01FB",
+ "aringbelow", "1E01",
+ "arrowboth", "2194",
+ "arrowdashdown", "21E3",
+ "arrowdashleft", "21E0",
+ "arrowdashright", "21E2",
+ "arrowdashup", "21E1",
+ "arrowdblboth", "21D4",
+ "arrowdbldown", "21D3",
+ "arrowdblleft", "21D0",
+ "arrowdblright", "21D2",
+ "arrowdblup", "21D1",
+ "arrowdown", "2193",
+ "arrowdownleft", "2199",
+ "arrowdownright", "2198",
+ "arrowdownwhite", "21E9",
+ "arrowheaddownmod", "02C5",
+ "arrowheadleftmod", "02C2",
+ "arrowheadrightmod", "02C3",
+ "arrowheadupmod", "02C4",
+ "arrowleft", "2190",
+ "arrowleftdbl", "21D0",
+ "arrowleftdblstroke", "21CD",
+ "arrowleftoverright", "21C6",
+ "arrowleftwhite", "21E6",
+ "arrowright", "2192",
+ "arrowrightdblstroke", "21CF",
+ "arrowrightheavy", "279E",
+ "arrowrightoverleft", "21C4",
+ "arrowrightwhite", "21E8",
+ "arrowtableft", "21E4",
+ "arrowtabright", "21E5",
+ "arrowup", "2191",
+ "arrowupdn", "2195",
+ "arrowupdnbse", "21A8",
+ "arrowupdownbase", "21A8",
+ "arrowupleft", "2196",
+ "arrowupleftofdown", "21C5",
+ "arrowupright", "2197",
+ "arrowupwhite", "21E7",
+ "asciicircum", "005E",
+ "asciicircummonospace", "FF3E",
+ "asciitilde", "007E",
+ "asciitildemonospace", "FF5E",
+ "ascript", "0251",
+ "ascriptturned", "0252",
+ "asmallhiragana", "3041",
+ "asmallkatakana", "30A1",
+ "asmallkatakanahalfwidth", "FF67",
+ "asterisk", "002A",
+ "asteriskaltonearabic", "066D",
+ "asteriskarabic", "066D",
+ "asteriskmath", "2217",
+ "asteriskmonospace", "FF0A",
+ "asterisksmall", "FE61",
+ "asterism", "2042",
+ "asymptoticallyequal", "2243",
+ "at", "0040",
+ "atilde", "00E3",
+ "atmonospace", "FF20",
+ "atsmall", "FE6B",
+ "aturned", "0250",
+ "aubengali", "0994",
+ "aubopomofo", "3120",
+ "audeva", "0914",
+ "augujarati", "0A94",
+ "augurmukhi", "0A14",
+ "aulengthmarkbengali", "09D7",
+ "aumatragurmukhi", "0A4C",
+ "auvowelsignbengali", "09CC",
+ "auvowelsigndeva", "094C",
+ "auvowelsigngujarati", "0ACC",
+ "avagrahadeva", "093D",
+ "aybarmenian", "0561",
+ "ayin", "05E2",
+ "ayinaltonehebrew", "FB20",
+ "ayinhebrew", "05E2",
+ "b", "0062",
+ "babengali", "09AC",
+ "backslash", "005C",
+ "backslashmonospace", "FF3C",
+ "badeva", "092C",
+ "bagujarati", "0AAC",
+ "bagurmukhi", "0A2C",
+ "bahiragana", "3070",
+ "bahtthai", "0E3F",
+ "bakatakana", "30D0",
+ "bar", "007C",
+ "barmonospace", "FF5C",
+ "bbopomofo", "3105",
+ "bcircle", "24D1",
+ "bdotaccent", "1E03",
+ "bdotbelow", "1E05",
+ "beamedsixteenthnotes", "266C",
+ "because", "2235",
+ "becyrillic", "0431",
+ "beharabic", "0628",
+ "behfinalarabic", "FE90",
+ "behinitialarabic", "FE91",
+ "behiragana", "3079",
+ "behmedialarabic", "FE92",
+ "behmeeminitialarabic", "FC9F",
+ "behmeemisolatedarabic", "FC08",
+ "behnoonfinalarabic", "FC6D",
+ "bekatakana", "30D9",
+ "benarmenian", "0562",
+ "bet", "05D1",
+ "beta", "03B2",
+ "betasymbolgreek", "03D0",
+ "betdagesh", "FB31",
+ "betdageshhebrew", "FB31",
+ "bethebrew", "05D1",
+ "betrafehebrew", "FB4C",
+ "bhabengali", "09AD",
+ "bhadeva", "092D",
+ "bhagujarati", "0AAD",
+ "bhagurmukhi", "0A2D",
+ "bhook", "0253",
+ "bihiragana", "3073",
+ "bikatakana", "30D3",
+ "bilabialclick", "0298",
+ "bindigurmukhi", "0A02",
+ "birusquare", "3331",
+ "blackcircle", "25CF",
+ "blackdiamond", "25C6",
+ "blackdownpointingtriangle", "25BC",
+ "blackleftpointingpointer", "25C4",
+ "blackleftpointingtriangle", "25C0",
+ "blacklenticularbracketleft", "3010",
+ "blacklenticularbracketleftvertical", "FE3B",
+ "blacklenticularbracketright", "3011",
+ "blacklenticularbracketrightvertical", "FE3C",
+ "blacklowerlefttriangle", "25E3",
+ "blacklowerrighttriangle", "25E2",
+ "blackrectangle", "25AC",
+ "blackrightpointingpointer", "25BA",
+ "blackrightpointingtriangle", "25B6",
+ "blacksmallsquare", "25AA",
+ "blacksmilingface", "263B",
+ "blacksquare", "25A0",
+ "blackstar", "2605",
+ "blackupperlefttriangle", "25E4",
+ "blackupperrighttriangle", "25E5",
+ "blackuppointingsmalltriangle", "25B4",
+ "blackuppointingtriangle", "25B2",
+ "blank", "2423",
+ "blinebelow", "1E07",
+ "block", "2588",
+ "bmonospace", "FF42",
+ "bobaimaithai", "0E1A",
+ "bohiragana", "307C",
+ "bokatakana", "30DC",
+ "bparen", "249D",
+ "bqsquare", "33C3",
+ "braceleft", "007B",
+ "braceleftmonospace", "FF5B",
+ "braceleftsmall", "FE5B",
+ "braceleftvertical", "FE37",
+ "braceright", "007D",
+ "bracerightmonospace", "FF5D",
+ "bracerightsmall", "FE5C",
+ "bracerightvertical", "FE38",
+ "bracketleft", "005B",
+ "bracketleftmonospace", "FF3B",
+ "bracketright", "005D",
+ "bracketrightmonospace", "FF3D",
+ "breve", "02D8",
+ "brevebelowcmb", "032E",
+ "brevecmb", "0306",
+ "breveinvertedbelowcmb", "032F",
+ "breveinvertedcmb", "0311",
+ "breveinverteddoublecmb", "0361",
+ "bridgebelowcmb", "032A",
+ "bridgeinvertedbelowcmb", "033A",
+ "brokenbar", "00A6",
+ "bstroke", "0180",
+ "btopbar", "0183",
+ "buhiragana", "3076",
+ "bukatakana", "30D6",
+ "bullet", "2022",
+ "bulletinverse", "25D8",
+ "bulletoperator", "2219",
+ "bullseye", "25CE",
+ "c", "0063",
+ "caarmenian", "056E",
+ "cabengali", "099A",
+ "cacute", "0107",
+ "cadeva", "091A",
+ "cagujarati", "0A9A",
+ "cagurmukhi", "0A1A",
+ "calsquare", "3388",
+ "candrabindubengali", "0981",
+ "candrabinducmb", "0310",
+ "candrabindudeva", "0901",
+ "candrabindugujarati", "0A81",
+ "capslock", "21EA",
+ "careof", "2105",
+ "caron", "02C7",
+ "caronbelowcmb", "032C",
+ "caroncmb", "030C",
+ "carriagereturn", "21B5",
+ "cbopomofo", "3118",
+ "ccaron", "010D",
+ "ccedilla", "00E7",
+ "ccedillaacute", "1E09",
+ "ccircle", "24D2",
+ "ccircumflex", "0109",
+ "ccurl", "0255",
+ "cdot", "010B",
+ "cdotaccent", "010B",
+ "cdsquare", "33C5",
+ "cedilla", "00B8",
+ "cedillacmb", "0327",
+ "cent", "00A2",
+ "centigrade", "2103",
+ "centmonospace", "FFE0",
+ "chaarmenian", "0579",
+ "chabengali", "099B",
+ "chadeva", "091B",
+ "chagujarati", "0A9B",
+ "chagurmukhi", "0A1B",
+ "chbopomofo", "3114",
+ "cheabkhasiancyrillic", "04BD",
+ "checkmark", "2713",
+ "checyrillic", "0447",
+ "chedescenderabkhasiancyrillic", "04BF",
+ "chedescendercyrillic", "04B7",
+ "chedieresiscyrillic", "04F5",
+ "cheharmenian", "0573",
+ "chekhakassiancyrillic", "04CC",
+ "cheverticalstrokecyrillic", "04B9",
+ "chi", "03C7",
+ "chieuchacirclekorean", "3277",
+ "chieuchaparenkorean", "3217",
+ "chieuchcirclekorean", "3269",
+ "chieuchkorean", "314A",
+ "chieuchparenkorean", "3209",
+ "chochangthai", "0E0A",
+ "chochanthai", "0E08",
+ "chochingthai", "0E09",
+ "chochoethai", "0E0C",
+ "chook", "0188",
+ "cieucacirclekorean", "3276",
+ "cieucaparenkorean", "3216",
+ "cieuccirclekorean", "3268",
+ "cieuckorean", "3148",
+ "cieucparenkorean", "3208",
+ "cieucuparenkorean", "321C",
+ "circle", "25CB",
+ "circlemultiply", "2297",
+ "circleot", "2299",
+ "circleplus", "2295",
+ "circlepostalmark", "3036",
+ "circlewithlefthalfblack", "25D0",
+ "circlewithrighthalfblack", "25D1",
+ "circumflex", "02C6",
+ "circumflexbelowcmb", "032D",
+ "circumflexcmb", "0302",
+ "clear", "2327",
+ "clickalveolar", "01C2",
+ "clickdental", "01C0",
+ "clicklateral", "01C1",
+ "clickretroflex", "01C3",
+ "club", "2663",
+ "clubsuitblack", "2663",
+ "clubsuitwhite", "2667",
+ "cmcubedsquare", "33A4",
+ "cmonospace", "FF43",
+ "cmsquaredsquare", "33A0",
+ "coarmenian", "0581",
+ "colon", "003A",
+ "colonmonetary", "20A1",
+ "colonmonospace", "FF1A",
+ "colonsign", "20A1",
+ "colonsmall", "FE55",
+ "colontriangularhalfmod", "02D1",
+ "colontriangularmod", "02D0",
+ "comma", "002C",
+ "commaabovecmb", "0313",
+ "commaaboverightcmb", "0315",
+ "commaarabic", "060C",
+ "commaarmenian", "055D",
+ "commamonospace", "FF0C",
+ "commareversedabovecmb", "0314",
+ "commareversedmod", "02BD",
+ "commasmall", "FE50",
+ "commaturnedabovecmb", "0312",
+ "commaturnedmod", "02BB",
+ "compass", "263C",
+ "congruent", "2245",
+ "contourintegral", "222E",
+ "control", "2303",
+ "controlACK", "0006",
+ "controlBEL", "0007",
+ "controlBS", "0008",
+ "controlCAN", "0018",
+ "controlCR", "000D",
+ "controlDC1", "0011",
+ "controlDC2", "0012",
+ "controlDC3", "0013",
+ "controlDC4", "0014",
+ "controlDEL", "007F",
+ "controlDLE", "0010",
+ "controlEM", "0019",
+ "controlENQ", "0005",
+ "controlEOT", "0004",
+ "controlESC", "001B",
+ "controlETB", "0017",
+ "controlETX", "0003",
+ "controlFF", "000C",
+ "controlFS", "001C",
+ "controlGS", "001D",
+ "controlHT", "0009",
+ "controlLF", "000A",
+ "controlNAK", "0015",
+ "controlRS", "001E",
+ "controlSI", "000F",
+ "controlSO", "000E",
+ "controlSOT", "0002",
+ "controlSTX", "0001",
+ "controlSUB", "001A",
+ "controlSYN", "0016",
+ "controlUS", "001F",
+ "controlVT", "000B",
+ "copyright", "00A9",
+ "cornerbracketleft", "300C",
+ "cornerbracketlefthalfwidth", "FF62",
+ "cornerbracketleftvertical", "FE41",
+ "cornerbracketright", "300D",
+ "cornerbracketrighthalfwidth", "FF63",
+ "cornerbracketrightvertical", "FE42",
+ "corporationsquare", "337F",
+ "cosquare", "33C7",
+ "coverkgsquare", "33C6",
+ "cparen", "249E",
+ "cruzeiro", "20A2",
+ "cstretched", "0297",
+ "curlyand", "22CF",
+ "curlyor", "22CE",
+ "currency", "00A4",
+ "d", "0064",
+ "daarmenian", "0564",
+ "dabengali", "09A6",
+ "dadarabic", "0636",
+ "dadeva", "0926",
+ "dadfinalarabic", "FEBE",
+ "dadinitialarabic", "FEBF",
+ "dadmedialarabic", "FEC0",
+ "dagesh", "05BC",
+ "dageshhebrew", "05BC",
+ "dagger", "2020",
+ "daggerdbl", "2021",
+ "dagujarati", "0AA6",
+ "dagurmukhi", "0A26",
+ "dahiragana", "3060",
+ "dakatakana", "30C0",
+ "dalarabic", "062F",
+ "dalet", "05D3",
+ "daletdagesh", "FB33",
+ "daletdageshhebrew", "FB33",
+ "dalethatafpatah", "05D3_05B2",
+ "dalethatafpatahhebrew", "05D3_05B2",
+ "dalethatafsegol", "05D3_05B1",
+ "dalethatafsegolhebrew", "05D3_05B1",
+ "dalethebrew", "05D3",
+ "dalethiriq", "05D3_05B4",
+ "dalethiriqhebrew", "05D3_05B4",
+ "daletholam", "05D3_05B9",
+ "daletholamhebrew", "05D3_05B9",
+ "daletpatah", "05D3_05B7",
+ "daletpatahhebrew", "05D3_05B7",
+ "daletqamats", "05D3_05B8",
+ "daletqamatshebrew", "05D3_05B8",
+ "daletqubuts", "05D3_05BB",
+ "daletqubutshebrew", "05D3_05BB",
+ "daletsegol", "05D3_05B6",
+ "daletsegolhebrew", "05D3_05B6",
+ "daletsheva", "05D3_05B0",
+ "daletshevahebrew", "05D3_05B0",
+ "dalettsere", "05D3_05B5",
+ "dalettserehebrew", "05D3_05B5",
+ "dalfinalarabic", "FEAA",
+ "dammaarabic", "064F",
+ "dammalowarabic", "064F",
+ "dammatanaltonearabic", "064C",
+ "dammatanarabic", "064C",
+ "danda", "0964",
+ "dargahebrew", "05A7",
+ "dargalefthebrew", "05A7",
+ "dasiapneumatacyrilliccmb", "0485",
+ "dblanglebracketleft", "300A",
+ "dblanglebracketleftvertical", "FE3D",
+ "dblanglebracketright", "300B",
+ "dblanglebracketrightvertical", "FE3E",
+ "dblarchinvertedbelowcmb", "032B",
+ "dblarrowleft", "21D4",
+ "dblarrowright", "21D2",
+ "dbldanda", "0965",
+ "dblgravecmb", "030F",
+ "dblintegral", "222C",
+ "dbllowline", "2017",
+ "dbllowlinecmb", "0333",
+ "dbloverlinecmb", "033F",
+ "dblprimemod", "02BA",
+ "dblverticalbar", "2016",
+ "dblverticallineabovecmb", "030E",
+ "dbopomofo", "3109",
+ "dbsquare", "33C8",
+ "dcaron", "010F",
+ "dcedilla", "1E11",
+ "dcircle", "24D3",
+ "dcircumflexbelow", "1E13",
+ "dcroat", "0111",
+ "ddabengali", "09A1",
+ "ddadeva", "0921",
+ "ddagujarati", "0AA1",
+ "ddagurmukhi", "0A21",
+ "ddalarabic", "0688",
+ "ddalfinalarabic", "FB89",
+ "dddhadeva", "095C",
+ "ddhabengali", "09A2",
+ "ddhadeva", "0922",
+ "ddhagujarati", "0AA2",
+ "ddhagurmukhi", "0A22",
+ "ddotaccent", "1E0B",
+ "ddotbelow", "1E0D",
+ "decimalseparatorarabic", "066B",
+ "decimalseparatorpersian", "066B",
+ "decyrillic", "0434",
+ "degree", "00B0",
+ "dehihebrew", "05AD",
+ "dehiragana", "3067",
+ "deicoptic", "03EF",
+ "dekatakana", "30C7",
+ "deleteleft", "232B",
+ "deleteright", "2326",
+ "delta", "03B4",
+ "deltaturned", "018D",
+ "denominatorminusonenumeratorbengali", "09F8",
+ "dezh", "02A4",
+ "dhabengali", "09A7",
+ "dhadeva", "0927",
+ "dhagujarati", "0AA7",
+ "dhagurmukhi", "0A27",
+ "dhook", "0257",
+ "dialytikatonos", "0385",
+ "dialytikatonoscmb", "0344",
+ "diamond", "2666",
+ "diamondsuitwhite", "2662",
+ "dieresis", "00A8",
+ "dieresisbelowcmb", "0324",
+ "dieresiscmb", "0308",
+ "dieresistonos", "0385",
+ "dihiragana", "3062",
+ "dikatakana", "30C2",
+ "dittomark", "3003",
+ "divide", "00F7",
+ "divides", "2223",
+ "divisionslash", "2215",
+ "djecyrillic", "0452",
+ "dkshade", "2593",
+ "dlinebelow", "1E0F",
+ "dlsquare", "3397",
+ "dmacron", "0111",
+ "dmonospace", "FF44",
+ "dnblock", "2584",
+ "dochadathai", "0E0E",
+ "dodekthai", "0E14",
+ "dohiragana", "3069",
+ "dokatakana", "30C9",
+ "dollar", "0024",
+ "dollarmonospace", "FF04",
+ "dollarsmall", "FE69",
+ "dong", "20AB",
+ "dorusquare", "3326",
+ "dotaccent", "02D9",
+ "dotaccentcmb", "0307",
+ "dotbelowcmb", "0323",
+ "dotbelowcomb", "0323",
+ "dotkatakana", "30FB",
+ "dotlessi", "0131",
+ "dotlessjstrokehook", "0284",
+ "dotmath", "22C5",
+ "dottedcircle", "25CC",
+ "doubleyodpatah", "FB1F",
+ "doubleyodpatahhebrew", "FB1F",
+ "downtackbelowcmb", "031E",
+ "downtackmod", "02D5",
+ "dparen", "249F",
+ "dtail", "0256",
+ "dtopbar", "018C",
+ "duhiragana", "3065",
+ "dukatakana", "30C5",
+ "dz", "01F3",
+ "dzaltone", "02A3",
+ "dzcaron", "01C6",
+ "dzcurl", "02A5",
+ "dzeabkhasiancyrillic", "04E1",
+ "dzecyrillic", "0455",
+ "dzhecyrillic", "045F",
+ "e", "0065",
+ "eacute", "00E9",
+ "earth", "2641",
+ "ebengali", "098F",
+ "ebopomofo", "311C",
+ "ebreve", "0115",
+ "ecandradeva", "090D",
+ "ecandragujarati", "0A8D",
+ "ecandravowelsigndeva", "0945",
+ "ecandravowelsigngujarati", "0AC5",
+ "ecaron", "011B",
+ "ecedillabreve", "1E1D",
+ "echarmenian", "0565",
+ "echyiwnarmenian", "0587",
+ "ecircle", "24D4",
+ "ecircumflex", "00EA",
+ "ecircumflexacute", "1EBF",
+ "ecircumflexbelow", "1E19",
+ "ecircumflexdotbelow", "1EC7",
+ "ecircumflexgrave", "1EC1",
+ "ecircumflexhookabove", "1EC3",
+ "ecircumflextilde", "1EC5",
+ "ecyrillic", "0454",
+ "edblgrave", "0205",
+ "edeva", "090F",
+ "edieresis", "00EB",
+ "edot", "0117",
+ "edotaccent", "0117",
+ "edotbelow", "1EB9",
+ "eegurmukhi", "0A0F",
+ "eematragurmukhi", "0A47",
+ "efcyrillic", "0444",
+ "egrave", "00E8",
+ "egujarati", "0A8F",
+ "eharmenian", "0567",
+ "ehbopomofo", "311D",
+ "ehiragana", "3048",
+ "ehookabove", "1EBB",
+ "eibopomofo", "311F",
+ "eight", "0038",
+ "eightarabic", "0668",
+ "eightbengali", "09EE",
+ "eightcircle", "2467",
+ "eightcircleinversesansserif", "2791",
+ "eightdeva", "096E",
+ "eighteencircle", "2471",
+ "eighteenparen", "2485",
+ "eighteenperiod", "2499",
+ "eightgujarati", "0AEE",
+ "eightgurmukhi", "0A6E",
+ "eighthackarabic", "0668",
+ "eighthangzhou", "3028",
+ "eighthnotebeamed", "266B",
+ "eightideographicparen", "3227",
+ "eightinferior", "2088",
+ "eightmonospace", "FF18",
+ "eightparen", "247B",
+ "eightperiod", "248F",
+ "eightpersian", "06F8",
+ "eightroman", "2177",
+ "eightsuperior", "2078",
+ "eightthai", "0E58",
+ "einvertedbreve", "0207",
+ "eiotifiedcyrillic", "0465",
+ "ekatakana", "30A8",
+ "ekatakanahalfwidth", "FF74",
+ "ekonkargurmukhi", "0A74",
+ "ekorean", "3154",
+ "elcyrillic", "043B",
+ "element", "2208",
+ "elevencircle", "246A",
+ "elevenparen", "247E",
+ "elevenperiod", "2492",
+ "elevenroman", "217A",
+ "ellipsis", "2026",
+ "ellipsisvertical", "22EE",
+ "emacron", "0113",
+ "emacronacute", "1E17",
+ "emacrongrave", "1E15",
+ "emcyrillic", "043C",
+ "emdash", "2014",
+ "emdashvertical", "FE31",
+ "emonospace", "FF45",
+ "emphasismarkarmenian", "055B",
+ "emptyset", "2205",
+ "enbopomofo", "3123",
+ "encyrillic", "043D",
+ "endash", "2013",
+ "endashvertical", "FE32",
+ "endescendercyrillic", "04A3",
+ "eng", "014B",
+ "engbopomofo", "3125",
+ "enghecyrillic", "04A5",
+ "enhookcyrillic", "04C8",
+ "enspace", "2002",
+ "eogonek", "0119",
+ "eokorean", "3153",
+ "eopen", "025B",
+ "eopenclosed", "029A",
+ "eopenreversed", "025C",
+ "eopenreversedclosed", "025E",
+ "eopenreversedhook", "025D",
+ "eparen", "24A0",
+ "epsilon", "03B5",
+ "epsilontonos", "03AD",
+ "equal", "003D",
+ "equalmonospace", "FF1D",
+ "equalsmall", "FE66",
+ "equalsuperior", "207C",
+ "equivalence", "2261",
+ "erbopomofo", "3126",
+ "ercyrillic", "0440",
+ "ereversed", "0258",
+ "ereversedcyrillic", "044D",
+ "escyrillic", "0441",
+ "esdescendercyrillic", "04AB",
+ "esh", "0283",
+ "eshcurl", "0286",
+ "eshortdeva", "090E",
+ "eshortvowelsigndeva", "0946",
+ "eshreversedloop", "01AA",
+ "eshsquatreversed", "0285",
+ "esmallhiragana", "3047",
+ "esmallkatakana", "30A7",
+ "esmallkatakanahalfwidth", "FF6A",
+ "estimated", "212E",
+ "eta", "03B7",
+ "etarmenian", "0568",
+ "etatonos", "03AE",
+ "eth", "00F0",
+ "etilde", "1EBD",
+ "etildebelow", "1E1B",
+ "etnahtafoukhhebrew", "0591",
+ "etnahtafoukhlefthebrew", "0591",
+ "etnahtahebrew", "0591",
+ "etnahtalefthebrew", "0591",
+ "eturned", "01DD",
+ "eukorean", "3161",
+ "euro", "20AC",
+ "evowelsignbengali", "09C7",
+ "evowelsigndeva", "0947",
+ "evowelsigngujarati", "0AC7",
+ "exclam", "0021",
+ "exclamarmenian", "055C",
+ "exclamdbl", "203C",
+ "exclamdown", "00A1",
+ "exclammonospace", "FF01",
+ "existential", "2203",
+ "ezh", "0292",
+ "ezhcaron", "01EF",
+ "ezhcurl", "0293",
+ "ezhreversed", "01B9",
+ "ezhtail", "01BA",
+ "f", "0066",
+ "fadeva", "095E",
+ "fagurmukhi", "0A5E",
+ "fahrenheit", "2109",
+ "fathaarabic", "064E",
+ "fathalowarabic", "064E",
+ "fathatanarabic", "064B",
+ "fbopomofo", "3108",
+ "fcircle", "24D5",
+ "fdotaccent", "1E1F",
+ "feharabic", "0641",
+ "feharmenian", "0586",
+ "fehfinalarabic", "FED2",
+ "fehinitialarabic", "FED3",
+ "fehmedialarabic", "FED4",
+ "feicoptic", "03E5",
+ "female", "2640",
+ "ff", "FB00",
+ "ffi", "FB03",
+ "ffl", "FB04",
+ "fi", "FB01",
+ "fifteencircle", "246E",
+ "fifteenparen", "2482",
+ "fifteenperiod", "2496",
+ "figuredash", "2012",
+ "filledbox", "25A0",
+ "filledrect", "25AC",
+ "finalkaf", "05DA",
+ "finalkafdagesh", "FB3A",
+ "finalkafdageshhebrew", "FB3A",
+ "finalkafhebrew", "05DA",
+ "finalkafqamats", "05DA_05B8",
+ "finalkafqamatshebrew", "05DA_05B8",
+ "finalkafsheva", "05DA_05B0",
+ "finalkafshevahebrew", "05DA_05B0",
+ "finalmem", "05DD",
+ "finalmemhebrew", "05DD",
+ "finalnun", "05DF",
+ "finalnunhebrew", "05DF",
+ "finalpe", "05E3",
+ "finalpehebrew", "05E3",
+ "finaltsadi", "05E5",
+ "finaltsadihebrew", "05E5",
+ "firsttonechinese", "02C9",
+ "fisheye", "25C9",
+ "fitacyrillic", "0473",
+ "five", "0035",
+ "fivearabic", "0665",
+ "fivebengali", "09EB",
+ "fivecircle", "2464",
+ "fivecircleinversesansserif", "278E",
+ "fivedeva", "096B",
+ "fiveeighths", "215D",
+ "fivegujarati", "0AEB",
+ "fivegurmukhi", "0A6B",
+ "fivehackarabic", "0665",
+ "fivehangzhou", "3025",
+ "fiveideographicparen", "3224",
+ "fiveinferior", "2085",
+ "fivemonospace", "FF15",
+ "fiveparen", "2478",
+ "fiveperiod", "248C",
+ "fivepersian", "06F5",
+ "fiveroman", "2174",
+ "fivesuperior", "2075",
+ "fivethai", "0E55",
+ "fl", "FB02",
+ "florin", "0192",
+ "fmonospace", "FF46",
+ "fmsquare", "3399",
+ "fofanthai", "0E1F",
+ "fofathai", "0E1D",
+ "fongmanthai", "0E4F",
+ "forall", "2200",
+ "four", "0034",
+ "fourarabic", "0664",
+ "fourbengali", "09EA",
+ "fourcircle", "2463",
+ "fourcircleinversesansserif", "278D",
+ "fourdeva", "096A",
+ "fourgujarati", "0AEA",
+ "fourgurmukhi", "0A6A",
+ "fourhackarabic", "0664",
+ "fourhangzhou", "3024",
+ "fourideographicparen", "3223",
+ "fourinferior", "2084",
+ "fourmonospace", "FF14",
+ "fournumeratorbengali", "09F7",
+ "fourparen", "2477",
+ "fourperiod", "248B",
+ "fourpersian", "06F4",
+ "fourroman", "2173",
+ "foursuperior", "2074",
+ "fourteencircle", "246D",
+ "fourteenparen", "2481",
+ "fourteenperiod", "2495",
+ "fourthai", "0E54",
+ "fourthtonechinese", "02CB",
+ "fparen", "24A1",
+ "fraction", "2044",
+ "franc", "20A3",
+ "g", "0067",
+ "gabengali", "0997",
+ "gacute", "01F5",
+ "gadeva", "0917",
+ "gafarabic", "06AF",
+ "gaffinalarabic", "FB93",
+ "gafinitialarabic", "FB94",
+ "gafmedialarabic", "FB95",
+ "gagujarati", "0A97",
+ "gagurmukhi", "0A17",
+ "gahiragana", "304C",
+ "gakatakana", "30AC",
+ "gamma", "03B3",
+ "gammalatinsmall", "0263",
+ "gammasuperior", "02E0",
+ "gangiacoptic", "03EB",
+ "gbopomofo", "310D",
+ "gbreve", "011F",
+ "gcaron", "01E7",
+ "gcedilla", "0123",
+ "gcircle", "24D6",
+ "gcircumflex", "011D",
+ "gcommaaccent", "0123",
+ "gdot", "0121",
+ "gdotaccent", "0121",
+ "gecyrillic", "0433",
+ "gehiragana", "3052",
+ "gekatakana", "30B2",
+ "geometricallyequal", "2251",
+ "gereshaccenthebrew", "059C",
+ "gereshhebrew", "05F3",
+ "gereshmuqdamhebrew", "059D",
+ "germandbls", "00DF",
+ "gershayimaccenthebrew", "059E",
+ "gershayimhebrew", "05F4",
+ "getamark", "3013",
+ "ghabengali", "0998",
+ "ghadarmenian", "0572",
+ "ghadeva", "0918",
+ "ghagujarati", "0A98",
+ "ghagurmukhi", "0A18",
+ "ghainarabic", "063A",
+ "ghainfinalarabic", "FECE",
+ "ghaininitialarabic", "FECF",
+ "ghainmedialarabic", "FED0",
+ "ghemiddlehookcyrillic", "0495",
+ "ghestrokecyrillic", "0493",
+ "gheupturncyrillic", "0491",
+ "ghhadeva", "095A",
+ "ghhagurmukhi", "0A5A",
+ "ghook", "0260",
+ "ghzsquare", "3393",
+ "gihiragana", "304E",
+ "gikatakana", "30AE",
+ "gimarmenian", "0563",
+ "gimel", "05D2",
+ "gimeldagesh", "FB32",
+ "gimeldageshhebrew", "FB32",
+ "gimelhebrew", "05D2",
+ "gjecyrillic", "0453",
+ "glottalinvertedstroke", "01BE",
+ "glottalstop", "0294",
+ "glottalstopinverted", "0296",
+ "glottalstopmod", "02C0",
+ "glottalstopreversed", "0295",
+ "glottalstopreversedmod", "02C1",
+ "glottalstopreversedsuperior", "02E4",
+ "glottalstopstroke", "02A1",
+ "glottalstopstrokereversed", "02A2",
+ "gmacron", "1E21",
+ "gmonospace", "FF47",
+ "gohiragana", "3054",
+ "gokatakana", "30B4",
+ "gparen", "24A2",
+ "gpasquare", "33AC",
+ "gradient", "2207",
+ "grave", "0060",
+ "gravebelowcmb", "0316",
+ "gravecmb", "0300",
+ "gravecomb", "0300",
+ "gravedeva", "0953",
+ "gravelowmod", "02CE",
+ "gravemonospace", "FF40",
+ "gravetonecmb", "0340",
+ "greater", "003E",
+ "greaterequal", "2265",
+ "greaterequalorless", "22DB",
+ "greatermonospace", "FF1E",
+ "greaterorequivalent", "2273",
+ "greaterorless", "2277",
+ "greateroverequal", "2267",
+ "greatersmall", "FE65",
+ "gscript", "0261",
+ "gstroke", "01E5",
+ "guhiragana", "3050",
+ "guillemotleft", "00AB",
+ "guillemotright", "00BB",
+ "guilsinglleft", "2039",
+ "guilsinglright", "203A",
+ "gukatakana", "30B0",
+ "guramusquare", "3318",
+ "gysquare", "33C9",
+ "h", "0068",
+ "haabkhasiancyrillic", "04A9",
+ "haaltonearabic", "06C1",
+ "habengali", "09B9",
+ "hadescendercyrillic", "04B3",
+ "hadeva", "0939",
+ "hagujarati", "0AB9",
+ "hagurmukhi", "0A39",
+ "haharabic", "062D",
+ "hahfinalarabic", "FEA2",
+ "hahinitialarabic", "FEA3",
+ "hahiragana", "306F",
+ "hahmedialarabic", "FEA4",
+ "haitusquare", "332A",
+ "hakatakana", "30CF",
+ "hakatakanahalfwidth", "FF8A",
+ "halantgurmukhi", "0A4D",
+ "hamzaarabic", "0621",
+ "hamzadammaarabic", "0621_064F",
+ "hamzadammatanarabic", "0621_064C",
+ "hamzafathaarabic", "0621_064E",
+ "hamzafathatanarabic", "0621_064B",
+ "hamzalowarabic", "0621",
+ "hamzalowkasraarabic", "0621_0650",
+ "hamzalowkasratanarabic", "0621_064D",
+ "hamzasukunarabic", "0621_0652",
+ "hangulfiller", "3164",
+ "hardsigncyrillic", "044A",
+ "harpoonleftbarbup", "21BC",
+ "harpoonrightbarbup", "21C0",
+ "hasquare", "33CA",
+ "hatafpatah", "05B2",
+ "hatafpatah16", "05B2",
+ "hatafpatah23", "05B2",
+ "hatafpatah2f", "05B2",
+ "hatafpatahhebrew", "05B2",
+ "hatafpatahnarrowhebrew", "05B2",
+ "hatafpatahquarterhebrew", "05B2",
+ "hatafpatahwidehebrew", "05B2",
+ "hatafqamats", "05B3",
+ "hatafqamats1b", "05B3",
+ "hatafqamats28", "05B3",
+ "hatafqamats34", "05B3",
+ "hatafqamatshebrew", "05B3",
+ "hatafqamatsnarrowhebrew", "05B3",
+ "hatafqamatsquarterhebrew", "05B3",
+ "hatafqamatswidehebrew", "05B3",
+ "hatafsegol", "05B1",
+ "hatafsegol17", "05B1",
+ "hatafsegol24", "05B1",
+ "hatafsegol30", "05B1",
+ "hatafsegolhebrew", "05B1",
+ "hatafsegolnarrowhebrew", "05B1",
+ "hatafsegolquarterhebrew", "05B1",
+ "hatafsegolwidehebrew", "05B1",
+ "hbar", "0127",
+ "hbopomofo", "310F",
+ "hbrevebelow", "1E2B",
+ "hcedilla", "1E29",
+ "hcircle", "24D7",
+ "hcircumflex", "0125",
+ "hdieresis", "1E27",
+ "hdotaccent", "1E23",
+ "hdotbelow", "1E25",
+ "he", "05D4",
+ "heart", "2665",
+ "heartsuitblack", "2665",
+ "heartsuitwhite", "2661",
+ "hedagesh", "FB34",
+ "hedageshhebrew", "FB34",
+ "hehaltonearabic", "06C1",
+ "heharabic", "0647",
+ "hehebrew", "05D4",
+ "hehfinalaltonearabic", "FBA7",
+ "hehfinalalttwoarabic", "FEEA",
+ "hehfinalarabic", "FEEA",
+ "hehhamzaabovefinalarabic", "FBA5",
+ "hehhamzaaboveisolatedarabic", "FBA4",
+ "hehinitialaltonearabic", "FBA8",
+ "hehinitialarabic", "FEEB",
+ "hehiragana", "3078",
+ "hehmedialaltonearabic", "FBA9",
+ "hehmedialarabic", "FEEC",
+ "heiseierasquare", "337B",
+ "hekatakana", "30D8",
+ "hekatakanahalfwidth", "FF8D",
+ "hekutaarusquare", "3336",
+ "henghook", "0267",
+ "herutusquare", "3339",
+ "het", "05D7",
+ "hethebrew", "05D7",
+ "hhook", "0266",
+ "hhooksuperior", "02B1",
+ "hieuhacirclekorean", "327B",
+ "hieuhaparenkorean", "321B",
+ "hieuhcirclekorean", "326D",
+ "hieuhkorean", "314E",
+ "hieuhparenkorean", "320D",
+ "hihiragana", "3072",
+ "hikatakana", "30D2",
+ "hikatakanahalfwidth", "FF8B",
+ "hiriq", "05B4",
+ "hiriq14", "05B4",
+ "hiriq21", "05B4",
+ "hiriq2d", "05B4",
+ "hiriqhebrew", "05B4",
+ "hiriqnarrowhebrew", "05B4",
+ "hiriqquarterhebrew", "05B4",
+ "hiriqwidehebrew", "05B4",
+ "hlinebelow", "1E96",
+ "hmonospace", "FF48",
+ "hoarmenian", "0570",
+ "hohipthai", "0E2B",
+ "hohiragana", "307B",
+ "hokatakana", "30DB",
+ "hokatakanahalfwidth", "FF8E",
+ "holam", "05B9",
+ "holam19", "05B9",
+ "holam26", "05B9",
+ "holam32", "05B9",
+ "holamhebrew", "05B9",
+ "holamnarrowhebrew", "05B9",
+ "holamquarterhebrew", "05B9",
+ "holamwidehebrew", "05B9",
+ "honokhukthai", "0E2E",
+ "hookabovecomb", "0309",
+ "hookcmb", "0309",
+ "hookpalatalizedbelowcmb", "0321",
+ "hookretroflexbelowcmb", "0322",
+ "hoonsquare", "3342",
+ "horicoptic", "03E9",
+ "horizontalbar", "2015",
+ "horncmb", "031B",
+ "hotsprings", "2668",
+ "house", "2302",
+ "hparen", "24A3",
+ "hsuperior", "02B0",
+ "hturned", "0265",
+ "huhiragana", "3075",
+ "huiitosquare", "3333",
+ "hukatakana", "30D5",
+ "hukatakanahalfwidth", "FF8C",
+ "hungarumlaut", "02DD",
+ "hungarumlautcmb", "030B",
+ "hv", "0195",
+ "hyphen", "002D",
+ "hyphenmonospace", "FF0D",
+ "hyphensmall", "FE63",
+ "hyphentwo", "2010",
+ "i", "0069",
+ "iacute", "00ED",
+ "iacyrillic", "044F",
+ "ibengali", "0987",
+ "ibopomofo", "3127",
+ "ibreve", "012D",
+ "icaron", "01D0",
+ "icircle", "24D8",
+ "icircumflex", "00EE",
+ "icyrillic", "0456",
+ "idblgrave", "0209",
+ "ideographearthcircle", "328F",
+ "ideographfirecircle", "328B",
+ "ideographicallianceparen", "323F",
+ "ideographiccallparen", "323A",
+ "ideographiccentrecircle", "32A5",
+ "ideographicclose", "3006",
+ "ideographiccomma", "3001",
+ "ideographiccommaleft", "FF64",
+ "ideographiccongratulationparen", "3237",
+ "ideographiccorrectcircle", "32A3",
+ "ideographicearthparen", "322F",
+ "ideographicenterpriseparen", "323D",
+ "ideographicexcellentcircle", "329D",
+ "ideographicfestivalparen", "3240",
+ "ideographicfinancialcircle", "3296",
+ "ideographicfinancialparen", "3236",
+ "ideographicfireparen", "322B",
+ "ideographichaveparen", "3232",
+ "ideographichighcircle", "32A4",
+ "ideographiciterationmark", "3005",
+ "ideographiclaborcircle", "3298",
+ "ideographiclaborparen", "3238",
+ "ideographicleftcircle", "32A7",
+ "ideographiclowcircle", "32A6",
+ "ideographicmedicinecircle", "32A9",
+ "ideographicmetalparen", "322E",
+ "ideographicmoonparen", "322A",
+ "ideographicnameparen", "3234",
+ "ideographicperiod", "3002",
+ "ideographicprintcircle", "329E",
+ "ideographicreachparen", "3243",
+ "ideographicrepresentparen", "3239",
+ "ideographicresourceparen", "323E",
+ "ideographicrightcircle", "32A8",
+ "ideographicsecretcircle", "3299",
+ "ideographicselfparen", "3242",
+ "ideographicsocietyparen", "3233",
+ "ideographicspace", "3000",
+ "ideographicspecialparen", "3235",
+ "ideographicstockparen", "3231",
+ "ideographicstudyparen", "323B",
+ "ideographicsunparen", "3230",
+ "ideographicsuperviseparen", "323C",
+ "ideographicwaterparen", "322C",
+ "ideographicwoodparen", "322D",
+ "ideographiczero", "3007",
+ "ideographmetalcircle", "328E",
+ "ideographmooncircle", "328A",
+ "ideographnamecircle", "3294",
+ "ideographsuncircle", "3290",
+ "ideographwatercircle", "328C",
+ "ideographwoodcircle", "328D",
+ "ideva", "0907",
+ "idieresis", "00EF",
+ "idieresisacute", "1E2F",
+ "idieresiscyrillic", "04E5",
+ "idotbelow", "1ECB",
+ "iebrevecyrillic", "04D7",
+ "iecyrillic", "0435",
+ "ieungacirclekorean", "3275",
+ "ieungaparenkorean", "3215",
+ "ieungcirclekorean", "3267",
+ "ieungkorean", "3147",
+ "ieungparenkorean", "3207",
+ "igrave", "00EC",
+ "igujarati", "0A87",
+ "igurmukhi", "0A07",
+ "ihiragana", "3044",
+ "ihookabove", "1EC9",
+ "iibengali", "0988",
+ "iicyrillic", "0438",
+ "iideva", "0908",
+ "iigujarati", "0A88",
+ "iigurmukhi", "0A08",
+ "iimatragurmukhi", "0A40",
+ "iinvertedbreve", "020B",
+ "iishortcyrillic", "0439",
+ "iivowelsignbengali", "09C0",
+ "iivowelsigndeva", "0940",
+ "iivowelsigngujarati", "0AC0",
+ "ij", "0133",
+ "ikatakana", "30A4",
+ "ikatakanahalfwidth", "FF72",
+ "ikorean", "3163",
+ "ilde", "02DC",
+ "iluyhebrew", "05AC",
+ "imacron", "012B",
+ "imacroncyrillic", "04E3",
+ "imageorapproximatelyequal", "2253",
+ "imatragurmukhi", "0A3F",
+ "imonospace", "FF49",
+ "increment", "2206",
+ "infinity", "221E",
+ "iniarmenian", "056B",
+ "integral", "222B",
+ "integralbottom", "2321",
+ "integralbt", "2321",
+ "integraltop", "2320",
+ "integraltp", "2320",
+ "intersection", "2229",
+ "intisquare", "3305",
+ "invbullet", "25D8",
+ "invcircle", "25D9",
+ "invsmileface", "263B",
+ "iocyrillic", "0451",
+ "iogonek", "012F",
+ "iota", "03B9",
+ "iotadieresis", "03CA",
+ "iotadieresistonos", "0390",
+ "iotalatin", "0269",
+ "iotatonos", "03AF",
+ "iparen", "24A4",
+ "irigurmukhi", "0A72",
+ "ismallhiragana", "3043",
+ "ismallkatakana", "30A3",
+ "ismallkatakanahalfwidth", "FF68",
+ "issharbengali", "09FA",
+ "istroke", "0268",
+ "iterationhiragana", "309D",
+ "iterationkatakana", "30FD",
+ "itilde", "0129",
+ "itildebelow", "1E2D",
+ "iubopomofo", "3129",
+ "iucyrillic", "044E",
+ "ivowelsignbengali", "09BF",
+ "ivowelsigndeva", "093F",
+ "ivowelsigngujarati", "0ABF",
+ "izhitsacyrillic", "0475",
+ "izhitsadblgravecyrillic", "0477",
+ "j", "006A",
+ "jaarmenian", "0571",
+ "jabengali", "099C",
+ "jadeva", "091C",
+ "jagujarati", "0A9C",
+ "jagurmukhi", "0A1C",
+ "jbopomofo", "3110",
+ "jcaron", "01F0",
+ "jcircle", "24D9",
+ "jcircumflex", "0135",
+ "jcrossedtail", "029D",
+ "jdotlessstroke", "025F",
+ "jecyrillic", "0458",
+ "jeemarabic", "062C",
+ "jeemfinalarabic", "FE9E",
+ "jeeminitialarabic", "FE9F",
+ "jeemmedialarabic", "FEA0",
+ "jeharabic", "0698",
+ "jehfinalarabic", "FB8B",
+ "jhabengali", "099D",
+ "jhadeva", "091D",
+ "jhagujarati", "0A9D",
+ "jhagurmukhi", "0A1D",
+ "jheharmenian", "057B",
+ "jis", "3004",
+ "jmonospace", "FF4A",
+ "jparen", "24A5",
+ "jsuperior", "02B2",
+ "k", "006B",
+ "kabashkircyrillic", "04A1",
+ "kabengali", "0995",
+ "kacute", "1E31",
+ "kacyrillic", "043A",
+ "kadescendercyrillic", "049B",
+ "kadeva", "0915",
+ "kaf", "05DB",
+ "kafarabic", "0643",
+ "kafdagesh", "FB3B",
+ "kafdageshhebrew", "FB3B",
+ "kaffinalarabic", "FEDA",
+ "kafhebrew", "05DB",
+ "kafinitialarabic", "FEDB",
+ "kafmedialarabic", "FEDC",
+ "kafrafehebrew", "FB4D",
+ "kagujarati", "0A95",
+ "kagurmukhi", "0A15",
+ "kahiragana", "304B",
+ "kahookcyrillic", "04C4",
+ "kakatakana", "30AB",
+ "kakatakanahalfwidth", "FF76",
+ "kappa", "03BA",
+ "kappasymbolgreek", "03F0",
+ "kapyeounmieumkorean", "3171",
+ "kapyeounphieuphkorean", "3184",
+ "kapyeounpieupkorean", "3178",
+ "kapyeounssangpieupkorean", "3179",
+ "karoriisquare", "330D",
+ "kashidaautoarabic", "0640",
+ "kashidaautonosidebearingarabic", "0640",
+ "kasmallkatakana", "30F5",
+ "kasquare", "3384",
+ "kasraarabic", "0650",
+ "kasratanarabic", "064D",
+ "kastrokecyrillic", "049F",
+ "katahiraprolongmarkhalfwidth", "FF70",
+ "kaverticalstrokecyrillic", "049D",
+ "kbopomofo", "310E",
+ "kcalsquare", "3389",
+ "kcaron", "01E9",
+ "kcedilla", "0137",
+ "kcircle", "24DA",
+ "kcommaaccent", "0137",
+ "kdotbelow", "1E33",
+ "keharmenian", "0584",
+ "kehiragana", "3051",
+ "kekatakana", "30B1",
+ "kekatakanahalfwidth", "FF79",
+ "kenarmenian", "056F",
+ "kesmallkatakana", "30F6",
+ "kgreenlandic", "0138",
+ "khabengali", "0996",
+ "khacyrillic", "0445",
+ "khadeva", "0916",
+ "khagujarati", "0A96",
+ "khagurmukhi", "0A16",
+ "khaharabic", "062E",
+ "khahfinalarabic", "FEA6",
+ "khahinitialarabic", "FEA7",
+ "khahmedialarabic", "FEA8",
+ "kheicoptic", "03E7",
+ "khhadeva", "0959",
+ "khhagurmukhi", "0A59",
+ "khieukhacirclekorean", "3278",
+ "khieukhaparenkorean", "3218",
+ "khieukhcirclekorean", "326A",
+ "khieukhkorean", "314B",
+ "khieukhparenkorean", "320A",
+ "khokhaithai", "0E02",
+ "khokhonthai", "0E05",
+ "khokhuatthai", "0E03",
+ "khokhwaithai", "0E04",
+ "khomutthai", "0E5B",
+ "khook", "0199",
+ "khorakhangthai", "0E06",
+ "khzsquare", "3391",
+ "kihiragana", "304D",
+ "kikatakana", "30AD",
+ "kikatakanahalfwidth", "FF77",
+ "kiroguramusquare", "3315",
+ "kiromeetorusquare", "3316",
+ "kirosquare", "3314",
+ "kiyeokacirclekorean", "326E",
+ "kiyeokaparenkorean", "320E",
+ "kiyeokcirclekorean", "3260",
+ "kiyeokkorean", "3131",
+ "kiyeokparenkorean", "3200",
+ "kiyeoksioskorean", "3133",
+ "kjecyrillic", "045C",
+ "klinebelow", "1E35",
+ "klsquare", "3398",
+ "kmcubedsquare", "33A6",
+ "kmonospace", "FF4B",
+ "kmsquaredsquare", "33A2",
+ "kohiragana", "3053",
+ "kohmsquare", "33C0",
+ "kokaithai", "0E01",
+ "kokatakana", "30B3",
+ "kokatakanahalfwidth", "FF7A",
+ "kooposquare", "331E",
+ "koppacyrillic", "0481",
+ "koreanstandardsymbol", "327F",
+ "koroniscmb", "0343",
+ "kparen", "24A6",
+ "kpasquare", "33AA",
+ "ksicyrillic", "046F",
+ "ktsquare", "33CF",
+ "kturned", "029E",
+ "kuhiragana", "304F",
+ "kukatakana", "30AF",
+ "kukatakanahalfwidth", "FF78",
+ "kvsquare", "33B8",
+ "kwsquare", "33BE",
+ "l", "006C",
+ "labengali", "09B2",
+ "lacute", "013A",
+ "ladeva", "0932",
+ "lagujarati", "0AB2",
+ "lagurmukhi", "0A32",
+ "lakkhangyaothai", "0E45",
+ "lamaleffinalarabic", "FEFC",
+ "lamalefhamzaabovefinalarabic", "FEF8",
+ "lamalefhamzaaboveisolatedarabic", "FEF7",
+ "lamalefhamzabelowfinalarabic", "FEFA",
+ "lamalefhamzabelowisolatedarabic", "FEF9",
+ "lamalefisolatedarabic", "FEFB",
+ "lamalefmaddaabovefinalarabic", "FEF6",
+ "lamalefmaddaaboveisolatedarabic", "FEF5",
+ "lamarabic", "0644",
+ "lambda", "03BB",
+ "lambdastroke", "019B",
+ "lamed", "05DC",
+ "lameddagesh", "FB3C",
+ "lameddageshhebrew", "FB3C",
+ "lamedhebrew", "05DC",
+ "lamedholam", "05DC_05B9",
+ "lamedholamdagesh", "05DC_05B9_05BC",
+ "lamedholamdageshhebrew", "05DC_05B9_05BC",
+ "lamedholamhebrew", "05DC_05B9",
+ "lamfinalarabic", "FEDE",
+ "lamhahinitialarabic", "FCCA",
+ "laminitialarabic", "FEDF",
+ "lamjeeminitialarabic", "FCC9",
+ "lamkhahinitialarabic", "FCCB",
+ "lamlamhehisolatedarabic", "FDF2",
+ "lammedialarabic", "FEE0",
+ "lammeemhahinitialarabic", "FD88",
+ "lammeeminitialarabic", "FCCC",
+ "lammeemjeeminitialarabic", "FEDF_FEE4_FEA0",
+ "lammeemkhahinitialarabic", "FEDF_FEE4_FEA8",
+ "largecircle", "25EF",
+ "lbar", "019A",
+ "lbelt", "026C",
+ "lbopomofo", "310C",
+ "lcaron", "013E",
+ "lcedilla", "013C",
+ "lcircle", "24DB",
+ "lcircumflexbelow", "1E3D",
+ "lcommaaccent", "013C",
+ "ldot", "0140",
+ "ldotaccent", "0140",
+ "ldotbelow", "1E37",
+ "ldotbelowmacron", "1E39",
+ "leftangleabovecmb", "031A",
+ "lefttackbelowcmb", "0318",
+ "less", "003C",
+ "lessequal", "2264",
+ "lessequalorgreater", "22DA",
+ "lessmonospace", "FF1C",
+ "lessorequivalent", "2272",
+ "lessorgreater", "2276",
+ "lessoverequal", "2266",
+ "lesssmall", "FE64",
+ "lezh", "026E",
+ "lfblock", "258C",
+ "lhookretroflex", "026D",
+ "lira", "20A4",
+ "liwnarmenian", "056C",
+ "lj", "01C9",
+ "ljecyrillic", "0459",
+ "lladeva", "0933",
+ "llagujarati", "0AB3",
+ "llinebelow", "1E3B",
+ "llladeva", "0934",
+ "llvocalicbengali", "09E1",
+ "llvocalicdeva", "0961",
+ "llvocalicvowelsignbengali", "09E3",
+ "llvocalicvowelsigndeva", "0963",
+ "lmiddletilde", "026B",
+ "lmonospace", "FF4C",
+ "lmsquare", "33D0",
+ "lochulathai", "0E2C",
+ "logicaland", "2227",
+ "logicalnot", "00AC",
+ "logicalnotreversed", "2310",
+ "logicalor", "2228",
+ "lolingthai", "0E25",
+ "longs", "017F",
+ "lowlinecenterline", "FE4E",
+ "lowlinecmb", "0332",
+ "lowlinedashed", "FE4D",
+ "lozenge", "25CA",
+ "lparen", "24A7",
+ "lslash", "0142",
+ "lsquare", "2113",
+ "ltshade", "2591",
+ "luthai", "0E26",
+ "lvocalicbengali", "098C",
+ "lvocalicdeva", "090C",
+ "lvocalicvowelsignbengali", "09E2",
+ "lvocalicvowelsigndeva", "0962",
+ "lxsquare", "33D3",
+ "m", "006D",
+ "mabengali", "09AE",
+ "macron", "00AF",
+ "macronbelowcmb", "0331",
+ "macroncmb", "0304",
+ "macronlowmod", "02CD",
+ "macronmonospace", "FFE3",
+ "macute", "1E3F",
+ "madeva", "092E",
+ "magujarati", "0AAE",
+ "magurmukhi", "0A2E",
+ "mahapakhhebrew", "05A4",
+ "mahapakhlefthebrew", "05A4",
+ "mahiragana", "307E",
+ "maichattawathai", "0E4B",
+ "maiekthai", "0E48",
+ "maihanakatthai", "0E31",
+ "maitaikhuthai", "0E47",
+ "maithothai", "0E49",
+ "maitrithai", "0E4A",
+ "maiyamokthai", "0E46",
+ "makatakana", "30DE",
+ "makatakanahalfwidth", "FF8F",
+ "male", "2642",
+ "mansyonsquare", "3347",
+ "maqafhebrew", "05BE",
+ "mars", "2642",
+ "masoracirclehebrew", "05AF",
+ "masquare", "3383",
+ "mbopomofo", "3107",
+ "mbsquare", "33D4",
+ "mcircle", "24DC",
+ "mcubedsquare", "33A5",
+ "mdotaccent", "1E41",
+ "mdotbelow", "1E43",
+ "meemarabic", "0645",
+ "meemfinalarabic", "FEE2",
+ "meeminitialarabic", "FEE3",
+ "meemmedialarabic", "FEE4",
+ "meemmeeminitialarabic", "FCD1",
+ "meemmeemisolatedarabic", "FC48",
+ "meetorusquare", "334D",
+ "mehiragana", "3081",
+ "meizierasquare", "337E",
+ "mekatakana", "30E1",
+ "mekatakanahalfwidth", "FF92",
+ "mem", "05DE",
+ "memdagesh", "FB3E",
+ "memdageshhebrew", "FB3E",
+ "memhebrew", "05DE",
+ "menarmenian", "0574",
+ "merkhahebrew", "05A5",
+ "merkhakefulahebrew", "05A6",
+ "merkhakefulalefthebrew", "05A6",
+ "merkhalefthebrew", "05A5",
+ "mhook", "0271",
+ "mhzsquare", "3392",
+ "middledotkatakanahalfwidth", "FF65",
+ "middot", "00B7",
+ "mieumacirclekorean", "3272",
+ "mieumaparenkorean", "3212",
+ "mieumcirclekorean", "3264",
+ "mieumkorean", "3141",
+ "mieumpansioskorean", "3170",
+ "mieumparenkorean", "3204",
+ "mieumpieupkorean", "316E",
+ "mieumsioskorean", "316F",
+ "mihiragana", "307F",
+ "mikatakana", "30DF",
+ "mikatakanahalfwidth", "FF90",
+ "minus", "2212",
+ "minusbelowcmb", "0320",
+ "minuscircle", "2296",
+ "minusmod", "02D7",
+ "minusplus", "2213",
+ "minute", "2032",
+ "miribaarusquare", "334A",
+ "mirisquare", "3349",
+ "mlonglegturned", "0270",
+ "mlsquare", "3396",
+ "mmcubedsquare", "33A3",
+ "mmonospace", "FF4D",
+ "mmsquaredsquare", "339F",
+ "mohiragana", "3082",
+ "mohmsquare", "33C1",
+ "mokatakana", "30E2",
+ "mokatakanahalfwidth", "FF93",
+ "molsquare", "33D6",
+ "momathai", "0E21",
+ "moverssquare", "33A7",
+ "moverssquaredsquare", "33A8",
+ "mparen", "24A8",
+ "mpasquare", "33AB",
+ "mssquare", "33B3",
+ "mturned", "026F",
+ "mu", "03BC", # groff: not U+00B5
+ "mu1", "00B5",
+ "muasquare", "3382",
+ "muchgreater", "226B",
+ "muchless", "226A",
+ "mufsquare", "338C",
+ "mugreek", "03BC",
+ "mugsquare", "338D",
+ "muhiragana", "3080",
+ "mukatakana", "30E0",
+ "mukatakanahalfwidth", "FF91",
+ "mulsquare", "3395",
+ "multiply", "00D7",
+ "mumsquare", "339B",
+ "munahhebrew", "05A3",
+ "munahlefthebrew", "05A3",
+ "musicalnote", "266A",
+ "musicalnotedbl", "266B",
+ "musicflatsign", "266D",
+ "musicsharpsign", "266F",
+ "mussquare", "33B2",
+ "muvsquare", "33B6",
+ "muwsquare", "33BC",
+ "mvmegasquare", "33B9",
+ "mvsquare", "33B7",
+ "mwmegasquare", "33BF",
+ "mwsquare", "33BD",
+ "n", "006E",
+ "nabengali", "09A8",
+ "nabla", "2207",
+ "nacute", "0144",
+ "nadeva", "0928",
+ "nagujarati", "0AA8",
+ "nagurmukhi", "0A28",
+ "nahiragana", "306A",
+ "nakatakana", "30CA",
+ "nakatakanahalfwidth", "FF85",
+ "napostrophe", "0149",
+ "nasquare", "3381",
+ "nbopomofo", "310B",
+ "nbspace", "00A0",
+ "ncaron", "0148",
+ "ncedilla", "0146",
+ "ncircle", "24DD",
+ "ncircumflexbelow", "1E4B",
+ "ncommaaccent", "0146",
+ "ndotaccent", "1E45",
+ "ndotbelow", "1E47",
+ "nehiragana", "306D",
+ "nekatakana", "30CD",
+ "nekatakanahalfwidth", "FF88",
+ "newsheqelsign", "20AA",
+ "nfsquare", "338B",
+ "ngabengali", "0999",
+ "ngadeva", "0919",
+ "ngagujarati", "0A99",
+ "ngagurmukhi", "0A19",
+ "ngonguthai", "0E07",
+ "nhiragana", "3093",
+ "nhookleft", "0272",
+ "nhookretroflex", "0273",
+ "nieunacirclekorean", "326F",
+ "nieunaparenkorean", "320F",
+ "nieuncieuckorean", "3135",
+ "nieuncirclekorean", "3261",
+ "nieunhieuhkorean", "3136",
+ "nieunkorean", "3134",
+ "nieunpansioskorean", "3168",
+ "nieunparenkorean", "3201",
+ "nieunsioskorean", "3167",
+ "nieuntikeutkorean", "3166",
+ "nihiragana", "306B",
+ "nikatakana", "30CB",
+ "nikatakanahalfwidth", "FF86",
+ "nikhahitthai", "0E4D",
+ "nine", "0039",
+ "ninearabic", "0669",
+ "ninebengali", "09EF",
+ "ninecircle", "2468",
+ "ninecircleinversesansserif", "2792",
+ "ninedeva", "096F",
+ "ninegujarati", "0AEF",
+ "ninegurmukhi", "0A6F",
+ "ninehackarabic", "0669",
+ "ninehangzhou", "3029",
+ "nineideographicparen", "3228",
+ "nineinferior", "2089",
+ "ninemonospace", "FF19",
+ "nineparen", "247C",
+ "nineperiod", "2490",
+ "ninepersian", "06F9",
+ "nineroman", "2178",
+ "ninesuperior", "2079",
+ "nineteencircle", "2472",
+ "nineteenparen", "2486",
+ "nineteenperiod", "249A",
+ "ninethai", "0E59",
+ "nj", "01CC",
+ "njecyrillic", "045A",
+ "nkatakana", "30F3",
+ "nkatakanahalfwidth", "FF9D",
+ "nlegrightlong", "019E",
+ "nlinebelow", "1E49",
+ "nmonospace", "FF4E",
+ "nmsquare", "339A",
+ "nnabengali", "09A3",
+ "nnadeva", "0923",
+ "nnagujarati", "0AA3",
+ "nnagurmukhi", "0A23",
+ "nnnadeva", "0929",
+ "nohiragana", "306E",
+ "nokatakana", "30CE",
+ "nokatakanahalfwidth", "FF89",
+ "nonbreakingspace", "00A0",
+ "nonenthai", "0E13",
+ "nonuthai", "0E19",
+ "noonarabic", "0646",
+ "noonfinalarabic", "FEE6",
+ "noonghunnaarabic", "06BA",
+ "noonghunnafinalarabic", "FB9F",
+ "noonhehinitialarabic", "FEE7_FEEC",
+ "nooninitialarabic", "FEE7",
+ "noonjeeminitialarabic", "FCD2",
+ "noonjeemisolatedarabic", "FC4B",
+ "noonmedialarabic", "FEE8",
+ "noonmeeminitialarabic", "FCD5",
+ "noonmeemisolatedarabic", "FC4E",
+ "noonnoonfinalarabic", "FC8D",
+ "notcontains", "220C",
+ "notelement", "2209",
+ "notelementof", "2209",
+ "notequal", "2260",
+ "notgreater", "226F",
+ "notgreaternorequal", "2271",
+ "notgreaternorless", "2279",
+ "notidentical", "2262",
+ "notless", "226E",
+ "notlessnorequal", "2270",
+ "notparallel", "2226",
+ "notprecedes", "2280",
+ "notsubset", "2284",
+ "notsucceeds", "2281",
+ "notsuperset", "2285",
+ "nowarmenian", "0576",
+ "nparen", "24A9",
+ "nssquare", "33B1",
+ "nsuperior", "207F",
+ "ntilde", "00F1",
+ "nu", "03BD",
+ "nuhiragana", "306C",
+ "nukatakana", "30CC",
+ "nukatakanahalfwidth", "FF87",
+ "nuktabengali", "09BC",
+ "nuktadeva", "093C",
+ "nuktagujarati", "0ABC",
+ "nuktagurmukhi", "0A3C",
+ "numbersign", "0023",
+ "numbersignmonospace", "FF03",
+ "numbersignsmall", "FE5F",
+ "numeralsigngreek", "0374",
+ "numeralsignlowergreek", "0375",
+ "numero", "2116",
+ "nun", "05E0",
+ "nundagesh", "FB40",
+ "nundageshhebrew", "FB40",
+ "nunhebrew", "05E0",
+ "nvsquare", "33B5",
+ "nwsquare", "33BB",
+ "nyabengali", "099E",
+ "nyadeva", "091E",
+ "nyagujarati", "0A9E",
+ "nyagurmukhi", "0A1E",
+ "o", "006F",
+ "oacute", "00F3",
+ "oangthai", "0E2D",
+ "obarred", "0275",
+ "obarredcyrillic", "04E9",
+ "obarreddieresiscyrillic", "04EB",
+ "obengali", "0993",
+ "obopomofo", "311B",
+ "obreve", "014F",
+ "ocandradeva", "0911",
+ "ocandragujarati", "0A91",
+ "ocandravowelsigndeva", "0949",
+ "ocandravowelsigngujarati", "0AC9",
+ "ocaron", "01D2",
+ "ocircle", "24DE",
+ "ocircumflex", "00F4",
+ "ocircumflexacute", "1ED1",
+ "ocircumflexdotbelow", "1ED9",
+ "ocircumflexgrave", "1ED3",
+ "ocircumflexhookabove", "1ED5",
+ "ocircumflextilde", "1ED7",
+ "ocyrillic", "043E",
+ "odblacute", "0151",
+ "odblgrave", "020D",
+ "odeva", "0913",
+ "odieresis", "00F6",
+ "odieresiscyrillic", "04E7",
+ "odotbelow", "1ECD",
+ "oe", "0153",
+ "oekorean", "315A",
+ "ogonek", "02DB",
+ "ogonekcmb", "0328",
+ "ograve", "00F2",
+ "ogujarati", "0A93",
+ "oharmenian", "0585",
+ "ohiragana", "304A",
+ "ohookabove", "1ECF",
+ "ohorn", "01A1",
+ "ohornacute", "1EDB",
+ "ohorndotbelow", "1EE3",
+ "ohorngrave", "1EDD",
+ "ohornhookabove", "1EDF",
+ "ohorntilde", "1EE1",
+ "ohungarumlaut", "0151",
+ "oi", "01A3",
+ "oinvertedbreve", "020F",
+ "okatakana", "30AA",
+ "okatakanahalfwidth", "FF75",
+ "okorean", "3157",
+ "olehebrew", "05AB",
+ "omacron", "014D",
+ "omacronacute", "1E53",
+ "omacrongrave", "1E51",
+ "omdeva", "0950",
+ "omega", "03C9",
+ "omega1", "03D6",
+ "omegacyrillic", "0461",
+ "omegalatinclosed", "0277",
+ "omegaroundcyrillic", "047B",
+ "omegatitlocyrillic", "047D",
+ "omegatonos", "03CE",
+ "omgujarati", "0AD0",
+ "omicron", "03BF",
+ "omicrontonos", "03CC",
+ "omonospace", "FF4F",
+ "one", "0031",
+ "onearabic", "0661",
+ "onebengali", "09E7",
+ "onecircle", "2460",
+ "onecircleinversesansserif", "278A",
+ "onedeva", "0967",
+ "onedotenleader", "2024",
+ "oneeighth", "215B",
+ "onegujarati", "0AE7",
+ "onegurmukhi", "0A67",
+ "onehackarabic", "0661",
+ "onehalf", "00BD",
+ "onehangzhou", "3021",
+ "oneideographicparen", "3220",
+ "oneinferior", "2081",
+ "onemonospace", "FF11",
+ "onenumeratorbengali", "09F4",
+ "oneparen", "2474",
+ "oneperiod", "2488",
+ "onepersian", "06F1",
+ "onequarter", "00BC",
+ "oneroman", "2170",
+ "onesuperior", "00B9",
+ "onethai", "0E51",
+ "onethird", "2153",
+ "oogonek", "01EB",
+ "oogonekmacron", "01ED",
+ "oogurmukhi", "0A13",
+ "oomatragurmukhi", "0A4B",
+ "oopen", "0254",
+ "oparen", "24AA",
+ "openbullet", "25E6",
+ "option", "2325",
+ "ordfeminine", "00AA",
+ "ordmasculine", "00BA",
+ "orthogonal", "221F",
+ "oshortdeva", "0912",
+ "oshortvowelsigndeva", "094A",
+ "oslash", "00F8",
+ "oslashacute", "01FF",
+ "osmallhiragana", "3049",
+ "osmallkatakana", "30A9",
+ "osmallkatakanahalfwidth", "FF6B",
+ "ostrokeacute", "01FF",
+ "otcyrillic", "047F",
+ "otilde", "00F5",
+ "otildeacute", "1E4D",
+ "otildedieresis", "1E4F",
+ "oubopomofo", "3121",
+ "overline", "203E",
+ "overlinecenterline", "FE4A",
+ "overlinecmb", "0305",
+ "overlinedashed", "FE49",
+ "overlinedblwavy", "FE4C",
+ "overlinewavy", "FE4B",
+ "overscore", "00AF",
+ "ovowelsignbengali", "09CB",
+ "ovowelsigndeva", "094B",
+ "ovowelsigngujarati", "0ACB",
+ "p", "0070",
+ "paampssquare", "3380",
+ "paasentosquare", "332B",
+ "pabengali", "09AA",
+ "pacute", "1E55",
+ "padeva", "092A",
+ "pagedown", "21DF",
+ "pageup", "21DE",
+ "pagujarati", "0AAA",
+ "pagurmukhi", "0A2A",
+ "pahiragana", "3071",
+ "paiyannoithai", "0E2F",
+ "pakatakana", "30D1",
+ "palatalizationcyrilliccmb", "0484",
+ "palochkacyrillic", "04C0",
+ "pansioskorean", "317F",
+ "paragraph", "00B6",
+ "parallel", "2225",
+ "parenleft", "0028",
+ "parenleftaltonearabic", "FD3E",
+ "parenleftinferior", "208D",
+ "parenleftmonospace", "FF08",
+ "parenleftsmall", "FE59",
+ "parenleftsuperior", "207D",
+ "parenleftvertical", "FE35",
+ "parenright", "0029",
+ "parenrightaltonearabic", "FD3F",
+ "parenrightinferior", "208E",
+ "parenrightmonospace", "FF09",
+ "parenrightsmall", "FE5A",
+ "parenrightsuperior", "207E",
+ "parenrightvertical", "FE36",
+ "partialdiff", "2202",
+ "paseqhebrew", "05C0",
+ "pashtahebrew", "0599",
+ "pasquare", "33A9",
+ "patah", "05B7",
+ "patah11", "05B7",
+ "patah1d", "05B7",
+ "patah2a", "05B7",
+ "patahhebrew", "05B7",
+ "patahnarrowhebrew", "05B7",
+ "patahquarterhebrew", "05B7",
+ "patahwidehebrew", "05B7",
+ "pazerhebrew", "05A1",
+ "pbopomofo", "3106",
+ "pcircle", "24DF",
+ "pdotaccent", "1E57",
+ "pe", "05E4",
+ "pecyrillic", "043F",
+ "pedagesh", "FB44",
+ "pedageshhebrew", "FB44",
+ "peezisquare", "333B",
+ "pefinaldageshhebrew", "FB43",
+ "peharabic", "067E",
+ "peharmenian", "057A",
+ "pehebrew", "05E4",
+ "pehfinalarabic", "FB57",
+ "pehinitialarabic", "FB58",
+ "pehiragana", "307A",
+ "pehmedialarabic", "FB59",
+ "pekatakana", "30DA",
+ "pemiddlehookcyrillic", "04A7",
+ "perafehebrew", "FB4E",
+ "percent", "0025",
+ "percentarabic", "066A",
+ "percentmonospace", "FF05",
+ "percentsmall", "FE6A",
+ "period", "002E",
+ "periodarmenian", "0589",
+ "periodcentered", "00B7",
+ "periodhalfwidth", "FF61",
+ "periodmonospace", "FF0E",
+ "periodsmall", "FE52",
+ "perispomenigreekcmb", "0342",
+ "perpendicular", "22A5",
+ "perthousand", "2030",
+ "peseta", "20A7",
+ "pfsquare", "338A",
+ "phabengali", "09AB",
+ "phadeva", "092B",
+ "phagujarati", "0AAB",
+ "phagurmukhi", "0A2B",
+ "phi", "03C6",
+ "phi1", "03D5",
+ "phieuphacirclekorean", "327A",
+ "phieuphaparenkorean", "321A",
+ "phieuphcirclekorean", "326C",
+ "phieuphkorean", "314D",
+ "phieuphparenkorean", "320C",
+ "philatin", "0278",
+ "phinthuthai", "0E3A",
+ "phisymbolgreek", "03D5",
+ "phook", "01A5",
+ "phophanthai", "0E1E",
+ "phophungthai", "0E1C",
+ "phosamphaothai", "0E20",
+ "pi", "03C0",
+ "pieupacirclekorean", "3273",
+ "pieupaparenkorean", "3213",
+ "pieupcieuckorean", "3176",
+ "pieupcirclekorean", "3265",
+ "pieupkiyeokkorean", "3172",
+ "pieupkorean", "3142",
+ "pieupparenkorean", "3205",
+ "pieupsioskiyeokkorean", "3174",
+ "pieupsioskorean", "3144",
+ "pieupsiostikeutkorean", "3175",
+ "pieupthieuthkorean", "3177",
+ "pieuptikeutkorean", "3173",
+ "pihiragana", "3074",
+ "pikatakana", "30D4",
+ "pisymbolgreek", "03D6",
+ "piwrarmenian", "0583",
+ "plus", "002B",
+ "plusbelowcmb", "031F",
+ "pluscircle", "2295",
+ "plusminus", "00B1",
+ "plusmod", "02D6",
+ "plusmonospace", "FF0B",
+ "plussmall", "FE62",
+ "plussuperior", "207A",
+ "pmonospace", "FF50",
+ "pmsquare", "33D8",
+ "pohiragana", "307D",
+ "pointingindexdownwhite", "261F",
+ "pointingindexleftwhite", "261C",
+ "pointingindexrightwhite", "261E",
+ "pointingindexupwhite", "261D",
+ "pokatakana", "30DD",
+ "poplathai", "0E1B",
+ "postalmark", "3012",
+ "postalmarkface", "3020",
+ "pparen", "24AB",
+ "precedes", "227A",
+ "prescription", "211E",
+ "primemod", "02B9",
+ "primereversed", "2035",
+ "product", "220F",
+ "projective", "2305",
+ "prolongedkana", "30FC",
+ "propellor", "2318",
+ "propersubset", "2282",
+ "propersuperset", "2283",
+ "proportion", "2237",
+ "proportional", "221D",
+ "psi", "03C8",
+ "psicyrillic", "0471",
+ "psilipneumatacyrilliccmb", "0486",
+ "pssquare", "33B0",
+ "puhiragana", "3077",
+ "pukatakana", "30D7",
+ "pvsquare", "33B4",
+ "pwsquare", "33BA",
+ "q", "0071",
+ "qadeva", "0958",
+ "qadmahebrew", "05A8",
+ "qafarabic", "0642",
+ "qaffinalarabic", "FED6",
+ "qafinitialarabic", "FED7",
+ "qafmedialarabic", "FED8",
+ "qamats", "05B8",
+ "qamats10", "05B8",
+ "qamats1a", "05B8",
+ "qamats1c", "05B8",
+ "qamats27", "05B8",
+ "qamats29", "05B8",
+ "qamats33", "05B8",
+ "qamatsde", "05B8",
+ "qamatshebrew", "05B8",
+ "qamatsnarrowhebrew", "05B8",
+ "qamatsqatanhebrew", "05B8",
+ "qamatsqatannarrowhebrew", "05B8",
+ "qamatsqatanquarterhebrew", "05B8",
+ "qamatsqatanwidehebrew", "05B8",
+ "qamatsquarterhebrew", "05B8",
+ "qamatswidehebrew", "05B8",
+ "qarneyparahebrew", "059F",
+ "qbopomofo", "3111",
+ "qcircle", "24E0",
+ "qhook", "02A0",
+ "qmonospace", "FF51",
+ "qof", "05E7",
+ "qofdagesh", "FB47",
+ "qofdageshhebrew", "FB47",
+ "qofhatafpatah", "05E7_05B2",
+ "qofhatafpatahhebrew", "05E7_05B2",
+ "qofhatafsegol", "05E7_05B1",
+ "qofhatafsegolhebrew", "05E7_05B1",
+ "qofhebrew", "05E7",
+ "qofhiriq", "05E7_05B4",
+ "qofhiriqhebrew", "05E7_05B4",
+ "qofholam", "05E7_05B9",
+ "qofholamhebrew", "05E7_05B9",
+ "qofpatah", "05E7_05B7",
+ "qofpatahhebrew", "05E7_05B7",
+ "qofqamats", "05E7_05B8",
+ "qofqamatshebrew", "05E7_05B8",
+ "qofqubuts", "05E7_05BB",
+ "qofqubutshebrew", "05E7_05BB",
+ "qofsegol", "05E7_05B6",
+ "qofsegolhebrew", "05E7_05B6",
+ "qofsheva", "05E7_05B0",
+ "qofshevahebrew", "05E7_05B0",
+ "qoftsere", "05E7_05B5",
+ "qoftserehebrew", "05E7_05B5",
+ "qparen", "24AC",
+ "quarternote", "2669",
+ "qubuts", "05BB",
+ "qubuts18", "05BB",
+ "qubuts25", "05BB",
+ "qubuts31", "05BB",
+ "qubutshebrew", "05BB",
+ "qubutsnarrowhebrew", "05BB",
+ "qubutsquarterhebrew", "05BB",
+ "qubutswidehebrew", "05BB",
+ "question", "003F",
+ "questionarabic", "061F",
+ "questionarmenian", "055E",
+ "questiondown", "00BF",
+ "questiongreek", "037E",
+ "questionmonospace", "FF1F",
+ "quotedbl", "0022",
+ "quotedblbase", "201E",
+ "quotedblleft", "201C",
+ "quotedblmonospace", "FF02",
+ "quotedblprime", "301E",
+ "quotedblprimereversed", "301D",
+ "quotedblright", "201D",
+ "quoteleft", "2018",
+ "quoteleftreversed", "201B",
+ "quotereversed", "201B",
+ "quoteright", "2019",
+ "quoterightn", "0149",
+ "quotesinglbase", "201A",
+ "quotesingle", "0027",
+ "quotesinglemonospace", "FF07",
+ "r", "0072",
+ "raarmenian", "057C",
+ "rabengali", "09B0",
+ "racute", "0155",
+ "radeva", "0930",
+ "radical", "221A",
+ "radoverssquare", "33AE",
+ "radoverssquaredsquare", "33AF",
+ "radsquare", "33AD",
+ "rafe", "05BF",
+ "rafehebrew", "05BF",
+ "ragujarati", "0AB0",
+ "ragurmukhi", "0A30",
+ "rahiragana", "3089",
+ "rakatakana", "30E9",
+ "rakatakanahalfwidth", "FF97",
+ "ralowerdiagonalbengali", "09F1",
+ "ramiddlediagonalbengali", "09F0",
+ "ramshorn", "0264",
+ "ratio", "2236",
+ "rbopomofo", "3116",
+ "rcaron", "0159",
+ "rcedilla", "0157",
+ "rcircle", "24E1",
+ "rcommaaccent", "0157",
+ "rdblgrave", "0211",
+ "rdotaccent", "1E59",
+ "rdotbelow", "1E5B",
+ "rdotbelowmacron", "1E5D",
+ "referencemark", "203B",
+ "reflexsubset", "2286",
+ "reflexsuperset", "2287",
+ "registered", "00AE",
+ "reharabic", "0631",
+ "reharmenian", "0580",
+ "rehfinalarabic", "FEAE",
+ "rehiragana", "308C",
+ "rehyehaleflamarabic", "0631_FEF3_FE8E_0644",
+ "rekatakana", "30EC",
+ "rekatakanahalfwidth", "FF9A",
+ "resh", "05E8",
+ "reshdageshhebrew", "FB48",
+ "reshhatafpatah", "05E8_05B2",
+ "reshhatafpatahhebrew", "05E8_05B2",
+ "reshhatafsegol", "05E8_05B1",
+ "reshhatafsegolhebrew", "05E8_05B1",
+ "reshhebrew", "05E8",
+ "reshhiriq", "05E8_05B4",
+ "reshhiriqhebrew", "05E8_05B4",
+ "reshholam", "05E8_05B9",
+ "reshholamhebrew", "05E8_05B9",
+ "reshpatah", "05E8_05B7",
+ "reshpatahhebrew", "05E8_05B7",
+ "reshqamats", "05E8_05B8",
+ "reshqamatshebrew", "05E8_05B8",
+ "reshqubuts", "05E8_05BB",
+ "reshqubutshebrew", "05E8_05BB",
+ "reshsegol", "05E8_05B6",
+ "reshsegolhebrew", "05E8_05B6",
+ "reshsheva", "05E8_05B0",
+ "reshshevahebrew", "05E8_05B0",
+ "reshtsere", "05E8_05B5",
+ "reshtserehebrew", "05E8_05B5",
+ "reversedtilde", "223D",
+ "reviahebrew", "0597",
+ "reviamugrashhebrew", "0597",
+ "revlogicalnot", "2310",
+ "rfishhook", "027E",
+ "rfishhookreversed", "027F",
+ "rhabengali", "09DD",
+ "rhadeva", "095D",
+ "rho", "03C1",
+ "rhook", "027D",
+ "rhookturned", "027B",
+ "rhookturnedsuperior", "02B5",
+ "rhosymbolgreek", "03F1",
+ "rhotichookmod", "02DE",
+ "rieulacirclekorean", "3271",
+ "rieulaparenkorean", "3211",
+ "rieulcirclekorean", "3263",
+ "rieulhieuhkorean", "3140",
+ "rieulkiyeokkorean", "313A",
+ "rieulkiyeoksioskorean", "3169",
+ "rieulkorean", "3139",
+ "rieulmieumkorean", "313B",
+ "rieulpansioskorean", "316C",
+ "rieulparenkorean", "3203",
+ "rieulphieuphkorean", "313F",
+ "rieulpieupkorean", "313C",
+ "rieulpieupsioskorean", "316B",
+ "rieulsioskorean", "313D",
+ "rieulthieuthkorean", "313E",
+ "rieultikeutkorean", "316A",
+ "rieulyeorinhieuhkorean", "316D",
+ "rightangle", "221F",
+ "righttackbelowcmb", "0319",
+ "righttriangle", "22BF",
+ "rihiragana", "308A",
+ "rikatakana", "30EA",
+ "rikatakanahalfwidth", "FF98",
+ "ring", "02DA",
+ "ringbelowcmb", "0325",
+ "ringcmb", "030A",
+ "ringhalfleft", "02BF",
+ "ringhalfleftarmenian", "0559",
+ "ringhalfleftbelowcmb", "031C",
+ "ringhalfleftcentered", "02D3",
+ "ringhalfright", "02BE",
+ "ringhalfrightbelowcmb", "0339",
+ "ringhalfrightcentered", "02D2",
+ "rinvertedbreve", "0213",
+ "rittorusquare", "3351",
+ "rlinebelow", "1E5F",
+ "rlongleg", "027C",
+ "rlonglegturned", "027A",
+ "rmonospace", "FF52",
+ "rohiragana", "308D",
+ "rokatakana", "30ED",
+ "rokatakanahalfwidth", "FF9B",
+ "roruathai", "0E23",
+ "rparen", "24AD",
+ "rrabengali", "09DC",
+ "rradeva", "0931",
+ "rragurmukhi", "0A5C",
+ "rreharabic", "0691",
+ "rrehfinalarabic", "FB8D",
+ "rrvocalicbengali", "09E0",
+ "rrvocalicdeva", "0960",
+ "rrvocalicgujarati", "0AE0",
+ "rrvocalicvowelsignbengali", "09C4",
+ "rrvocalicvowelsigndeva", "0944",
+ "rrvocalicvowelsigngujarati", "0AC4",
+ "rtblock", "2590",
+ "rturned", "0279",
+ "rturnedsuperior", "02B4",
+ "ruhiragana", "308B",
+ "rukatakana", "30EB",
+ "rukatakanahalfwidth", "FF99",
+ "rupeemarkbengali", "09F2",
+ "rupeesignbengali", "09F3",
+ "ruthai", "0E24",
+ "rvocalicbengali", "098B",
+ "rvocalicdeva", "090B",
+ "rvocalicgujarati", "0A8B",
+ "rvocalicvowelsignbengali", "09C3",
+ "rvocalicvowelsigndeva", "0943",
+ "rvocalicvowelsigngujarati", "0AC3",
+ "s", "0073",
+ "sabengali", "09B8",
+ "sacute", "015B",
+ "sacutedotaccent", "1E65",
+ "sadarabic", "0635",
+ "sadeva", "0938",
+ "sadfinalarabic", "FEBA",
+ "sadinitialarabic", "FEBB",
+ "sadmedialarabic", "FEBC",
+ "sagujarati", "0AB8",
+ "sagurmukhi", "0A38",
+ "sahiragana", "3055",
+ "sakatakana", "30B5",
+ "sakatakanahalfwidth", "FF7B",
+ "sallallahoualayhewasallamarabic", "FDFA",
+ "samekh", "05E1",
+ "samekhdagesh", "FB41",
+ "samekhdageshhebrew", "FB41",
+ "samekhhebrew", "05E1",
+ "saraaathai", "0E32",
+ "saraaethai", "0E41",
+ "saraaimaimalaithai", "0E44",
+ "saraaimaimuanthai", "0E43",
+ "saraamthai", "0E33",
+ "saraathai", "0E30",
+ "saraethai", "0E40",
+ "saraiithai", "0E35",
+ "saraithai", "0E34",
+ "saraothai", "0E42",
+ "saraueethai", "0E37",
+ "sarauethai", "0E36",
+ "sarauthai", "0E38",
+ "sarauuthai", "0E39",
+ "sbopomofo", "3119",
+ "scaron", "0161",
+ "scarondotaccent", "1E67",
+ "scedilla", "015F",
+ "schwa", "0259",
+ "schwacyrillic", "04D9",
+ "schwadieresiscyrillic", "04DB",
+ "schwahook", "025A",
+ "scircle", "24E2",
+ "scircumflex", "015D",
+ "scommaaccent", "0219",
+ "sdotaccent", "1E61",
+ "sdotbelow", "1E63",
+ "sdotbelowdotaccent", "1E69",
+ "seagullbelowcmb", "033C",
+ "second", "2033",
+ "secondtonechinese", "02CA",
+ "section", "00A7",
+ "seenarabic", "0633",
+ "seenfinalarabic", "FEB2",
+ "seeninitialarabic", "FEB3",
+ "seenmedialarabic", "FEB4",
+ "segol", "05B6",
+ "segol13", "05B6",
+ "segol1f", "05B6",
+ "segol2c", "05B6",
+ "segolhebrew", "05B6",
+ "segolnarrowhebrew", "05B6",
+ "segolquarterhebrew", "05B6",
+ "segoltahebrew", "0592",
+ "segolwidehebrew", "05B6",
+ "seharmenian", "057D",
+ "sehiragana", "305B",
+ "sekatakana", "30BB",
+ "sekatakanahalfwidth", "FF7E",
+ "semicolon", "003B",
+ "semicolonarabic", "061B",
+ "semicolonmonospace", "FF1B",
+ "semicolonsmall", "FE54",
+ "semivoicedmarkkana", "309C",
+ "semivoicedmarkkanahalfwidth", "FF9F",
+ "sentisquare", "3322",
+ "sentosquare", "3323",
+ "seven", "0037",
+ "sevenarabic", "0667",
+ "sevenbengali", "09ED",
+ "sevencircle", "2466",
+ "sevencircleinversesansserif", "2790",
+ "sevendeva", "096D",
+ "seveneighths", "215E",
+ "sevengujarati", "0AED",
+ "sevengurmukhi", "0A6D",
+ "sevenhackarabic", "0667",
+ "sevenhangzhou", "3027",
+ "sevenideographicparen", "3226",
+ "seveninferior", "2087",
+ "sevenmonospace", "FF17",
+ "sevenparen", "247A",
+ "sevenperiod", "248E",
+ "sevenpersian", "06F7",
+ "sevenroman", "2176",
+ "sevensuperior", "2077",
+ "seventeencircle", "2470",
+ "seventeenparen", "2484",
+ "seventeenperiod", "2498",
+ "seventhai", "0E57",
+ "sfthyphen", "00AD",
+ "shaarmenian", "0577",
+ "shabengali", "09B6",
+ "shacyrillic", "0448",
+ "shaddaarabic", "0651",
+ "shaddadammaarabic", "FC61",
+ "shaddadammatanarabic", "FC5E",
+ "shaddafathaarabic", "FC60",
+ "shaddafathatanarabic", "0651_064B",
+ "shaddakasraarabic", "FC62",
+ "shaddakasratanarabic", "FC5F",
+ "shade", "2592",
+ "shadedark", "2593",
+ "shadelight", "2591",
+ "shademedium", "2592",
+ "shadeva", "0936",
+ "shagujarati", "0AB6",
+ "shagurmukhi", "0A36",
+ "shalshelethebrew", "0593",
+ "shbopomofo", "3115",
+ "shchacyrillic", "0449",
+ "sheenarabic", "0634",
+ "sheenfinalarabic", "FEB6",
+ "sheeninitialarabic", "FEB7",
+ "sheenmedialarabic", "FEB8",
+ "sheicoptic", "03E3",
+ "sheqel", "20AA",
+ "sheqelhebrew", "20AA",
+ "sheva", "05B0",
+ "sheva115", "05B0",
+ "sheva15", "05B0",
+ "sheva22", "05B0",
+ "sheva2e", "05B0",
+ "shevahebrew", "05B0",
+ "shevanarrowhebrew", "05B0",
+ "shevaquarterhebrew", "05B0",
+ "shevawidehebrew", "05B0",
+ "shhacyrillic", "04BB",
+ "shimacoptic", "03ED",
+ "shin", "05E9",
+ "shindagesh", "FB49",
+ "shindageshhebrew", "FB49",
+ "shindageshshindot", "FB2C",
+ "shindageshshindothebrew", "FB2C",
+ "shindageshsindot", "FB2D",
+ "shindageshsindothebrew", "FB2D",
+ "shindothebrew", "05C1",
+ "shinhebrew", "05E9",
+ "shinshindot", "FB2A",
+ "shinshindothebrew", "FB2A",
+ "shinsindot", "FB2B",
+ "shinsindothebrew", "FB2B",
+ "shook", "0282",
+ "sigma", "03C3",
+ "sigma1", "03C2",
+ "sigmafinal", "03C2",
+ "sigmalunatesymbolgreek", "03F2",
+ "sihiragana", "3057",
+ "sikatakana", "30B7",
+ "sikatakanahalfwidth", "FF7C",
+ "siluqhebrew", "05BD",
+ "siluqlefthebrew", "05BD",
+ "similar", "223C",
+ "sindothebrew", "05C2",
+ "siosacirclekorean", "3274",
+ "siosaparenkorean", "3214",
+ "sioscieuckorean", "317E",
+ "sioscirclekorean", "3266",
+ "sioskiyeokkorean", "317A",
+ "sioskorean", "3145",
+ "siosnieunkorean", "317B",
+ "siosparenkorean", "3206",
+ "siospieupkorean", "317D",
+ "siostikeutkorean", "317C",
+ "six", "0036",
+ "sixarabic", "0666",
+ "sixbengali", "09EC",
+ "sixcircle", "2465",
+ "sixcircleinversesansserif", "278F",
+ "sixdeva", "096C",
+ "sixgujarati", "0AEC",
+ "sixgurmukhi", "0A6C",
+ "sixhackarabic", "0666",
+ "sixhangzhou", "3026",
+ "sixideographicparen", "3225",
+ "sixinferior", "2086",
+ "sixmonospace", "FF16",
+ "sixparen", "2479",
+ "sixperiod", "248D",
+ "sixpersian", "06F6",
+ "sixroman", "2175",
+ "sixsuperior", "2076",
+ "sixteencircle", "246F",
+ "sixteencurrencydenominatorbengali", "09F9",
+ "sixteenparen", "2483",
+ "sixteenperiod", "2497",
+ "sixthai", "0E56",
+ "slash", "002F",
+ "slashmonospace", "FF0F",
+ "slong", "017F",
+ "slongdotaccent", "1E9B",
+ "smileface", "263A",
+ "smonospace", "FF53",
+ "sofpasuqhebrew", "05C3",
+ "softhyphen", "00AD",
+ "softsigncyrillic", "044C",
+ "sohiragana", "305D",
+ "sokatakana", "30BD",
+ "sokatakanahalfwidth", "FF7F",
+ "soliduslongoverlaycmb", "0338",
+ "solidusshortoverlaycmb", "0337",
+ "sorusithai", "0E29",
+ "sosalathai", "0E28",
+ "sosothai", "0E0B",
+ "sosuathai", "0E2A",
+ "space", "0020",
+ "spacehackarabic", "0020",
+ "spade", "2660",
+ "spadesuitblack", "2660",
+ "spadesuitwhite", "2664",
+ "sparen", "24AE",
+ "squarebelowcmb", "033B",
+ "squarecc", "33C4",
+ "squarecm", "339D",
+ "squarediagonalcrosshatchfill", "25A9",
+ "squarehorizontalfill", "25A4",
+ "squarekg", "338F",
+ "squarekm", "339E",
+ "squarekmcapital", "33CE",
+ "squareln", "33D1",
+ "squarelog", "33D2",
+ "squaremg", "338E",
+ "squaremil", "33D5",
+ "squaremm", "339C",
+ "squaremsquared", "33A1",
+ "squareorthogonalcrosshatchfill", "25A6",
+ "squareupperlefttolowerrightfill", "25A7",
+ "squareupperrighttolowerleftfill", "25A8",
+ "squareverticalfill", "25A5",
+ "squarewhitewithsmallblack", "25A3",
+ "srsquare", "33DB",
+ "ssabengali", "09B7",
+ "ssadeva", "0937",
+ "ssagujarati", "0AB7",
+ "ssangcieuckorean", "3149",
+ "ssanghieuhkorean", "3185",
+ "ssangieungkorean", "3180",
+ "ssangkiyeokkorean", "3132",
+ "ssangnieunkorean", "3165",
+ "ssangpieupkorean", "3143",
+ "ssangsioskorean", "3146",
+ "ssangtikeutkorean", "3138",
+ "sterling", "00A3",
+ "sterlingmonospace", "FFE1",
+ "strokelongoverlaycmb", "0336",
+ "strokeshortoverlaycmb", "0335",
+ "subset", "2282",
+ "subsetnotequal", "228A",
+ "subsetorequal", "2286",
+ "succeeds", "227B",
+ "suchthat", "220B",
+ "suhiragana", "3059",
+ "sukatakana", "30B9",
+ "sukatakanahalfwidth", "FF7D",
+ "sukunarabic", "0652",
+ "summation", "2211",
+ "sun", "263C",
+ "superset", "2283",
+ "supersetnotequal", "228B",
+ "supersetorequal", "2287",
+ "svsquare", "33DC",
+ "syouwaerasquare", "337C",
+ "t", "0074",
+ "tabengali", "09A4",
+ "tackdown", "22A4",
+ "tackleft", "22A3",
+ "tadeva", "0924",
+ "tagujarati", "0AA4",
+ "tagurmukhi", "0A24",
+ "taharabic", "0637",
+ "tahfinalarabic", "FEC2",
+ "tahinitialarabic", "FEC3",
+ "tahiragana", "305F",
+ "tahmedialarabic", "FEC4",
+ "taisyouerasquare", "337D",
+ "takatakana", "30BF",
+ "takatakanahalfwidth", "FF80",
+ "tatweelarabic", "0640",
+ "tau", "03C4",
+ "tav", "05EA",
+ "tavdages", "FB4A",
+ "tavdagesh", "FB4A",
+ "tavdageshhebrew", "FB4A",
+ "tavhebrew", "05EA",
+ "tbar", "0167",
+ "tbopomofo", "310A",
+ "tcaron", "0165",
+ "tccurl", "02A8",
+ "tcedilla", "0163",
+ "tcheharabic", "0686",
+ "tchehfinalarabic", "FB7B",
+ "tchehinitialarabic", "FB7C",
+ "tchehmedialarabic", "FB7D",
+ "tchehmeeminitialarabic", "FB7C_FEE4",
+ "tcircle", "24E3",
+ "tcircumflexbelow", "1E71",
+ "tcommaaccent", "0163",
+ "tdieresis", "1E97",
+ "tdotaccent", "1E6B",
+ "tdotbelow", "1E6D",
+ "tecyrillic", "0442",
+ "tedescendercyrillic", "04AD",
+ "teharabic", "062A",
+ "tehfinalarabic", "FE96",
+ "tehhahinitialarabic", "FCA2",
+ "tehhahisolatedarabic", "FC0C",
+ "tehinitialarabic", "FE97",
+ "tehiragana", "3066",
+ "tehjeeminitialarabic", "FCA1",
+ "tehjeemisolatedarabic", "FC0B",
+ "tehmarbutaarabic", "0629",
+ "tehmarbutafinalarabic", "FE94",
+ "tehmedialarabic", "FE98",
+ "tehmeeminitialarabic", "FCA4",
+ "tehmeemisolatedarabic", "FC0E",
+ "tehnoonfinalarabic", "FC73",
+ "tekatakana", "30C6",
+ "tekatakanahalfwidth", "FF83",
+ "telephone", "2121",
+ "telephoneblack", "260E",
+ "telishagedolahebrew", "05A0",
+ "telishaqetanahebrew", "05A9",
+ "tencircle", "2469",
+ "tenideographicparen", "3229",
+ "tenparen", "247D",
+ "tenperiod", "2491",
+ "tenroman", "2179",
+ "tesh", "02A7",
+ "tet", "05D8",
+ "tetdagesh", "FB38",
+ "tetdageshhebrew", "FB38",
+ "tethebrew", "05D8",
+ "tetsecyrillic", "04B5",
+ "tevirhebrew", "059B",
+ "tevirlefthebrew", "059B",
+ "thabengali", "09A5",
+ "thadeva", "0925",
+ "thagujarati", "0AA5",
+ "thagurmukhi", "0A25",
+ "thalarabic", "0630",
+ "thalfinalarabic", "FEAC",
+ "thanthakhatthai", "0E4C",
+ "theharabic", "062B",
+ "thehfinalarabic", "FE9A",
+ "thehinitialarabic", "FE9B",
+ "thehmedialarabic", "FE9C",
+ "thereexists", "2203",
+ "therefore", "2234",
+ "theta", "03B8",
+ "theta1", "03D1",
+ "thetasymbolgreek", "03D1",
+ "thieuthacirclekorean", "3279",
+ "thieuthaparenkorean", "3219",
+ "thieuthcirclekorean", "326B",
+ "thieuthkorean", "314C",
+ "thieuthparenkorean", "320B",
+ "thirteencircle", "246C",
+ "thirteenparen", "2480",
+ "thirteenperiod", "2494",
+ "thonangmonthothai", "0E11",
+ "thook", "01AD",
+ "thophuthaothai", "0E12",
+ "thorn", "00FE",
+ "thothahanthai", "0E17",
+ "thothanthai", "0E10",
+ "thothongthai", "0E18",
+ "thothungthai", "0E16",
+ "thousandcyrillic", "0482",
+ "thousandsseparatorarabic", "066C",
+ "thousandsseparatorpersian", "066C",
+ "three", "0033",
+ "threearabic", "0663",
+ "threebengali", "09E9",
+ "threecircle", "2462",
+ "threecircleinversesansserif", "278C",
+ "threedeva", "0969",
+ "threeeighths", "215C",
+ "threegujarati", "0AE9",
+ "threegurmukhi", "0A69",
+ "threehackarabic", "0663",
+ "threehangzhou", "3023",
+ "threeideographicparen", "3222",
+ "threeinferior", "2083",
+ "threemonospace", "FF13",
+ "threenumeratorbengali", "09F6",
+ "threeparen", "2476",
+ "threeperiod", "248A",
+ "threepersian", "06F3",
+ "threequarters", "00BE",
+ "threeroman", "2172",
+ "threesuperior", "00B3",
+ "threethai", "0E53",
+ "thzsquare", "3394",
+ "tihiragana", "3061",
+ "tikatakana", "30C1",
+ "tikatakanahalfwidth", "FF81",
+ "tikeutacirclekorean", "3270",
+ "tikeutaparenkorean", "3210",
+ "tikeutcirclekorean", "3262",
+ "tikeutkorean", "3137",
+ "tikeutparenkorean", "3202",
+ "tilde", "02DC",
+ "tildebelowcmb", "0330",
+ "tildecmb", "0303",
+ "tildecomb", "0303",
+ "tildedoublecmb", "0360",
+ "tildeoperator", "223C",
+ "tildeoverlaycmb", "0334",
+ "tildeverticalcmb", "033E",
+ "timescircle", "2297",
+ "tipehahebrew", "0596",
+ "tipehalefthebrew", "0596",
+ "tippigurmukhi", "0A70",
+ "titlocyrilliccmb", "0483",
+ "tiwnarmenian", "057F",
+ "tlinebelow", "1E6F",
+ "tmonospace", "FF54",
+ "toarmenian", "0569",
+ "tohiragana", "3068",
+ "tokatakana", "30C8",
+ "tokatakanahalfwidth", "FF84",
+ "tonebarextrahighmod", "02E5",
+ "tonebarextralowmod", "02E9",
+ "tonebarhighmod", "02E6",
+ "tonebarlowmod", "02E8",
+ "tonebarmidmod", "02E7",
+ "tonefive", "01BD",
+ "tonesix", "0185",
+ "tonetwo", "01A8",
+ "tonos", "0384",
+ "tonsquare", "3327",
+ "topatakthai", "0E0F",
+ "tortoiseshellbracketleft", "3014",
+ "tortoiseshellbracketleftsmall", "FE5D",
+ "tortoiseshellbracketleftvertical", "FE39",
+ "tortoiseshellbracketright", "3015",
+ "tortoiseshellbracketrightsmall", "FE5E",
+ "tortoiseshellbracketrightvertical", "FE3A",
+ "totaothai", "0E15",
+ "tpalatalhook", "01AB",
+ "tparen", "24AF",
+ "trademark", "2122",
+ "tretroflexhook", "0288",
+ "triagdn", "25BC",
+ "triaglf", "25C4",
+ "triagrt", "25BA",
+ "triagup", "25B2",
+ "ts", "02A6",
+ "tsadi", "05E6",
+ "tsadidagesh", "FB46",
+ "tsadidageshhebrew", "FB46",
+ "tsadihebrew", "05E6",
+ "tsecyrillic", "0446",
+ "tsere", "05B5",
+ "tsere12", "05B5",
+ "tsere1e", "05B5",
+ "tsere2b", "05B5",
+ "tserehebrew", "05B5",
+ "tserenarrowhebrew", "05B5",
+ "tserequarterhebrew", "05B5",
+ "tserewidehebrew", "05B5",
+ "tshecyrillic", "045B",
+ "ttabengali", "099F",
+ "ttadeva", "091F",
+ "ttagujarati", "0A9F",
+ "ttagurmukhi", "0A1F",
+ "tteharabic", "0679",
+ "ttehfinalarabic", "FB67",
+ "ttehinitialarabic", "FB68",
+ "ttehmedialarabic", "FB69",
+ "tthabengali", "09A0",
+ "tthadeva", "0920",
+ "tthagujarati", "0AA0",
+ "tthagurmukhi", "0A20",
+ "tturned", "0287",
+ "tuhiragana", "3064",
+ "tukatakana", "30C4",
+ "tukatakanahalfwidth", "FF82",
+ "tusmallhiragana", "3063",
+ "tusmallkatakana", "30C3",
+ "tusmallkatakanahalfwidth", "FF6F",
+ "twelvecircle", "246B",
+ "twelveparen", "247F",
+ "twelveperiod", "2493",
+ "twelveroman", "217B",
+ "twentycircle", "2473",
+ "twentyhangzhou", "5344",
+ "twentyparen", "2487",
+ "twentyperiod", "249B",
+ "two", "0032",
+ "twoarabic", "0662",
+ "twobengali", "09E8",
+ "twocircle", "2461",
+ "twocircleinversesansserif", "278B",
+ "twodeva", "0968",
+ "twodotenleader", "2025",
+ "twodotleader", "2025",
+ "twodotleadervertical", "FE30",
+ "twogujarati", "0AE8",
+ "twogurmukhi", "0A68",
+ "twohackarabic", "0662",
+ "twohangzhou", "3022",
+ "twoideographicparen", "3221",
+ "twoinferior", "2082",
+ "twomonospace", "FF12",
+ "twonumeratorbengali", "09F5",
+ "twoparen", "2475",
+ "twoperiod", "2489",
+ "twopersian", "06F2",
+ "tworoman", "2171",
+ "twostroke", "01BB",
+ "twosuperior", "00B2",
+ "twothai", "0E52",
+ "twothirds", "2154",
+ "u", "0075",
+ "uacute", "00FA",
+ "ubar", "0289",
+ "ubengali", "0989",
+ "ubopomofo", "3128",
+ "ubreve", "016D",
+ "ucaron", "01D4",
+ "ucircle", "24E4",
+ "ucircumflex", "00FB",
+ "ucircumflexbelow", "1E77",
+ "ucyrillic", "0443",
+ "udattadeva", "0951",
+ "udblacute", "0171",
+ "udblgrave", "0215",
+ "udeva", "0909",
+ "udieresis", "00FC",
+ "udieresisacute", "01D8",
+ "udieresisbelow", "1E73",
+ "udieresiscaron", "01DA",
+ "udieresiscyrillic", "04F1",
+ "udieresisgrave", "01DC",
+ "udieresismacron", "01D6",
+ "udotbelow", "1EE5",
+ "ugrave", "00F9",
+ "ugujarati", "0A89",
+ "ugurmukhi", "0A09",
+ "uhiragana", "3046",
+ "uhookabove", "1EE7",
+ "uhorn", "01B0",
+ "uhornacute", "1EE9",
+ "uhorndotbelow", "1EF1",
+ "uhorngrave", "1EEB",
+ "uhornhookabove", "1EED",
+ "uhorntilde", "1EEF",
+ "uhungarumlaut", "0171",
+ "uhungarumlautcyrillic", "04F3",
+ "uinvertedbreve", "0217",
+ "ukatakana", "30A6",
+ "ukatakanahalfwidth", "FF73",
+ "ukcyrillic", "0479",
+ "ukorean", "315C",
+ "umacron", "016B",
+ "umacroncyrillic", "04EF",
+ "umacrondieresis", "1E7B",
+ "umatragurmukhi", "0A41",
+ "umonospace", "FF55",
+ "underscore", "005F",
+ "underscoredbl", "2017",
+ "underscoremonospace", "FF3F",
+ "underscorevertical", "FE33",
+ "underscorewavy", "FE4F",
+ "union", "222A",
+ "universal", "2200",
+ "uogonek", "0173",
+ "uparen", "24B0",
+ "upblock", "2580",
+ "upperdothebrew", "05C4",
+ "upsilon", "03C5",
+ "upsilondieresis", "03CB",
+ "upsilondieresistonos", "03B0",
+ "upsilonlatin", "028A",
+ "upsilontonos", "03CD",
+ "uptackbelowcmb", "031D",
+ "uptackmod", "02D4",
+ "uragurmukhi", "0A73",
+ "uring", "016F",
+ "ushortcyrillic", "045E",
+ "usmallhiragana", "3045",
+ "usmallkatakana", "30A5",
+ "usmallkatakanahalfwidth", "FF69",
+ "ustraightcyrillic", "04AF",
+ "ustraightstrokecyrillic", "04B1",
+ "utilde", "0169",
+ "utildeacute", "1E79",
+ "utildebelow", "1E75",
+ "uubengali", "098A",
+ "uudeva", "090A",
+ "uugujarati", "0A8A",
+ "uugurmukhi", "0A0A",
+ "uumatragurmukhi", "0A42",
+ "uuvowelsignbengali", "09C2",
+ "uuvowelsigndeva", "0942",
+ "uuvowelsigngujarati", "0AC2",
+ "uvowelsignbengali", "09C1",
+ "uvowelsigndeva", "0941",
+ "uvowelsigngujarati", "0AC1",
+ "v", "0076",
+ "vadeva", "0935",
+ "vagujarati", "0AB5",
+ "vagurmukhi", "0A35",
+ "vakatakana", "30F7",
+ "vav", "05D5",
+ "vavdagesh", "FB35",
+ "vavdagesh65", "FB35",
+ "vavdageshhebrew", "FB35",
+ "vavhebrew", "05D5",
+ "vavholam", "FB4B",
+ "vavholamhebrew", "FB4B",
+ "vavvavhebrew", "05F0",
+ "vavyodhebrew", "05F1",
+ "vcircle", "24E5",
+ "vdotbelow", "1E7F",
+ "vecyrillic", "0432",
+ "veharabic", "06A4",
+ "vehfinalarabic", "FB6B",
+ "vehinitialarabic", "FB6C",
+ "vehmedialarabic", "FB6D",
+ "vekatakana", "30F9",
+ "venus", "2640",
+ "verticalbar", "007C",
+ "verticallineabovecmb", "030D",
+ "verticallinebelowcmb", "0329",
+ "verticallinelowmod", "02CC",
+ "verticallinemod", "02C8",
+ "vewarmenian", "057E",
+ "vhook", "028B",
+ "vikatakana", "30F8",
+ "viramabengali", "09CD",
+ "viramadeva", "094D",
+ "viramagujarati", "0ACD",
+ "visargabengali", "0983",
+ "visargadeva", "0903",
+ "visargagujarati", "0A83",
+ "vmonospace", "FF56",
+ "voarmenian", "0578",
+ "voicediterationhiragana", "309E",
+ "voicediterationkatakana", "30FE",
+ "voicedmarkkana", "309B",
+ "voicedmarkkanahalfwidth", "FF9E",
+ "vokatakana", "30FA",
+ "vparen", "24B1",
+ "vtilde", "1E7D",
+ "vturned", "028C",
+ "vuhiragana", "3094",
+ "vukatakana", "30F4",
+ "w", "0077",
+ "wacute", "1E83",
+ "waekorean", "3159",
+ "wahiragana", "308F",
+ "wakatakana", "30EF",
+ "wakatakanahalfwidth", "FF9C",
+ "wakorean", "3158",
+ "wasmallhiragana", "308E",
+ "wasmallkatakana", "30EE",
+ "wattosquare", "3357",
+ "wavedash", "301C",
+ "wavyunderscorevertical", "FE34",
+ "wawarabic", "0648",
+ "wawfinalarabic", "FEEE",
+ "wawhamzaabovearabic", "0624",
+ "wawhamzaabovefinalarabic", "FE86",
+ "wbsquare", "33DD",
+ "wcircle", "24E6",
+ "wcircumflex", "0175",
+ "wdieresis", "1E85",
+ "wdotaccent", "1E87",
+ "wdotbelow", "1E89",
+ "wehiragana", "3091",
+ "weierstrass", "2118",
+ "wekatakana", "30F1",
+ "wekorean", "315E",
+ "weokorean", "315D",
+ "wgrave", "1E81",
+ "whitebullet", "25E6",
+ "whitecircle", "25CB",
+ "whitecircleinverse", "25D9",
+ "whitecornerbracketleft", "300E",
+ "whitecornerbracketleftvertical", "FE43",
+ "whitecornerbracketright", "300F",
+ "whitecornerbracketrightvertical", "FE44",
+ "whitediamond", "25C7",
+ "whitediamondcontainingblacksmalldiamond", "25C8",
+ "whitedownpointingsmalltriangle", "25BF",
+ "whitedownpointingtriangle", "25BD",
+ "whiteleftpointingsmalltriangle", "25C3",
+ "whiteleftpointingtriangle", "25C1",
+ "whitelenticularbracketleft", "3016",
+ "whitelenticularbracketright", "3017",
+ "whiterightpointingsmalltriangle", "25B9",
+ "whiterightpointingtriangle", "25B7",
+ "whitesmallsquare", "25AB",
+ "whitesmilingface", "263A",
+ "whitesquare", "25A1",
+ "whitestar", "2606",
+ "whitetelephone", "260F",
+ "whitetortoiseshellbracketleft", "3018",
+ "whitetortoiseshellbracketright", "3019",
+ "whiteuppointingsmalltriangle", "25B5",
+ "whiteuppointingtriangle", "25B3",
+ "wihiragana", "3090",
+ "wikatakana", "30F0",
+ "wikorean", "315F",
+ "wmonospace", "FF57",
+ "wohiragana", "3092",
+ "wokatakana", "30F2",
+ "wokatakanahalfwidth", "FF66",
+ "won", "20A9",
+ "wonmonospace", "FFE6",
+ "wowaenthai", "0E27",
+ "wparen", "24B2",
+ "wring", "1E98",
+ "wsuperior", "02B7",
+ "wturned", "028D",
+ "wynn", "01BF",
+ "x", "0078",
+ "xabovecmb", "033D",
+ "xbopomofo", "3112",
+ "xcircle", "24E7",
+ "xdieresis", "1E8D",
+ "xdotaccent", "1E8B",
+ "xeharmenian", "056D",
+ "xi", "03BE",
+ "xmonospace", "FF58",
+ "xparen", "24B3",
+ "xsuperior", "02E3",
+ "y", "0079",
+ "yaadosquare", "334E",
+ "yabengali", "09AF",
+ "yacute", "00FD",
+ "yadeva", "092F",
+ "yaekorean", "3152",
+ "yagujarati", "0AAF",
+ "yagurmukhi", "0A2F",
+ "yahiragana", "3084",
+ "yakatakana", "30E4",
+ "yakatakanahalfwidth", "FF94",
+ "yakorean", "3151",
+ "yamakkanthai", "0E4E",
+ "yasmallhiragana", "3083",
+ "yasmallkatakana", "30E3",
+ "yasmallkatakanahalfwidth", "FF6C",
+ "yatcyrillic", "0463",
+ "ycircle", "24E8",
+ "ycircumflex", "0177",
+ "ydieresis", "00FF",
+ "ydotaccent", "1E8F",
+ "ydotbelow", "1EF5",
+ "yeharabic", "064A",
+ "yehbarreearabic", "06D2",
+ "yehbarreefinalarabic", "FBAF",
+ "yehfinalarabic", "FEF2",
+ "yehhamzaabovearabic", "0626",
+ "yehhamzaabovefinalarabic", "FE8A",
+ "yehhamzaaboveinitialarabic", "FE8B",
+ "yehhamzaabovemedialarabic", "FE8C",
+ "yehinitialarabic", "FEF3",
+ "yehmedialarabic", "FEF4",
+ "yehmeeminitialarabic", "FCDD",
+ "yehmeemisolatedarabic", "FC58",
+ "yehnoonfinalarabic", "FC94",
+ "yehthreedotsbelowarabic", "06D1",
+ "yekorean", "3156",
+ "yen", "00A5",
+ "yenmonospace", "FFE5",
+ "yeokorean", "3155",
+ "yeorinhieuhkorean", "3186",
+ "yerahbenyomohebrew", "05AA",
+ "yerahbenyomolefthebrew", "05AA",
+ "yericyrillic", "044B",
+ "yerudieresiscyrillic", "04F9",
+ "yesieungkorean", "3181",
+ "yesieungpansioskorean", "3183",
+ "yesieungsioskorean", "3182",
+ "yetivhebrew", "059A",
+ "ygrave", "1EF3",
+ "yhook", "01B4",
+ "yhookabove", "1EF7",
+ "yiarmenian", "0575",
+ "yicyrillic", "0457",
+ "yikorean", "3162",
+ "yinyang", "262F",
+ "yiwnarmenian", "0582",
+ "ymonospace", "FF59",
+ "yod", "05D9",
+ "yoddagesh", "FB39",
+ "yoddageshhebrew", "FB39",
+ "yodhebrew", "05D9",
+ "yodyodhebrew", "05F2",
+ "yodyodpatahhebrew", "FB1F",
+ "yohiragana", "3088",
+ "yoikorean", "3189",
+ "yokatakana", "30E8",
+ "yokatakanahalfwidth", "FF96",
+ "yokorean", "315B",
+ "yosmallhiragana", "3087",
+ "yosmallkatakana", "30E7",
+ "yosmallkatakanahalfwidth", "FF6E",
+ "yotgreek", "03F3",
+ "yoyaekorean", "3188",
+ "yoyakorean", "3187",
+ "yoyakthai", "0E22",
+ "yoyingthai", "0E0D",
+ "yparen", "24B4",
+ "ypogegrammeni", "037A",
+ "ypogegrammenigreekcmb", "0345",
+ "yr", "01A6",
+ "yring", "1E99",
+ "ysuperior", "02B8",
+ "ytilde", "1EF9",
+ "yturned", "028E",
+ "yuhiragana", "3086",
+ "yuikorean", "318C",
+ "yukatakana", "30E6",
+ "yukatakanahalfwidth", "FF95",
+ "yukorean", "3160",
+ "yusbigcyrillic", "046B",
+ "yusbigiotifiedcyrillic", "046D",
+ "yuslittlecyrillic", "0467",
+ "yuslittleiotifiedcyrillic", "0469",
+ "yusmallhiragana", "3085",
+ "yusmallkatakana", "30E5",
+ "yusmallkatakanahalfwidth", "FF6D",
+ "yuyekorean", "318B",
+ "yuyeokorean", "318A",
+ "yyabengali", "09DF",
+ "yyadeva", "095F",
+ "z", "007A",
+ "zaarmenian", "0566",
+ "zacute", "017A",
+ "zadeva", "095B",
+ "zagurmukhi", "0A5B",
+ "zaharabic", "0638",
+ "zahfinalarabic", "FEC6",
+ "zahinitialarabic", "FEC7",
+ "zahiragana", "3056",
+ "zahmedialarabic", "FEC8",
+ "zainarabic", "0632",
+ "zainfinalarabic", "FEB0",
+ "zakatakana", "30B6",
+ "zaqefgadolhebrew", "0595",
+ "zaqefqatanhebrew", "0594",
+ "zarqahebrew", "0598",
+ "zayin", "05D6",
+ "zayindagesh", "FB36",
+ "zayindageshhebrew", "FB36",
+ "zayinhebrew", "05D6",
+ "zbopomofo", "3117",
+ "zcaron", "017E",
+ "zcircle", "24E9",
+ "zcircumflex", "1E91",
+ "zcurl", "0291",
+ "zdot", "017C",
+ "zdotaccent", "017C",
+ "zdotbelow", "1E93",
+ "zecyrillic", "0437",
+ "zedescendercyrillic", "0499",
+ "zedieresiscyrillic", "04DF",
+ "zehiragana", "305C",
+ "zekatakana", "30BC",
+ "zero", "0030",
+ "zeroarabic", "0660",
+ "zerobengali", "09E6",
+ "zerodeva", "0966",
+ "zerogujarati", "0AE6",
+ "zerogurmukhi", "0A66",
+ "zerohackarabic", "0660",
+ "zeroinferior", "2080",
+ "zeromonospace", "FF10",
+ "zeropersian", "06F0",
+ "zerosuperior", "2070",
+ "zerothai", "0E50",
+ "zerowidthjoiner", "FEFF",
+ "zerowidthnonjoiner", "200C",
+ "zerowidthspace", "200B",
+ "zeta", "03B6",
+ "zhbopomofo", "3113",
+ "zhearmenian", "056A",
+ "zhebrevecyrillic", "04C2",
+ "zhecyrillic", "0436",
+ "zhedescendercyrillic", "0497",
+ "zhedieresiscyrillic", "04DD",
+ "zihiragana", "3058",
+ "zikatakana", "30B8",
+ "zinorhebrew", "05AE",
+ "zlinebelow", "1E95",
+ "zmonospace", "FF5A",
+ "zohiragana", "305E",
+ "zokatakana", "30BE",
+ "zparen", "24B5",
+ "zretroflexhook", "0290",
+ "zstroke", "01B6",
+ "zuhiragana", "305A",
+ "zukatakana", "30BA",
+);
diff --git a/src/utils/afmtodit/make-afmtodit-tables b/src/utils/afmtodit/make-afmtodit-tables
new file mode 100755
index 0000000..937bb72
--- /dev/null
+++ b/src/utils/afmtodit/make-afmtodit-tables
@@ -0,0 +1,139 @@
+#! /bin/sh
+#
+# make-afmtodit-tables -- script for creating the 'unicode_decomposed'
+# and 'AGL_to_unicode' tables
+#
+# Copyright (C) 2005-2020 Free Software Foundation, Inc.
+# Written by Werner Lemberg <wl@gnu.org>
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#
+# usage:
+#
+# make-afmtodit-tables \
+# UnicodeData.txt version-string glyphlist.txt > afmtodit.in
+#
+# 'UnicodeData.txt' is the central database file from the Unicode
+# standard. Unfortunately, it doesn't contain a version number, which
+# must be thus provided manually as an additional parameter.
+#
+# 'glyphlist.txt' holds the Adobe Glyph List (AGL).
+#
+# This program needs a C preprocessor.
+#
+
+if [ $# -ne 3 ]
+then
+ echo "usage: $0 UnicodeData.txt UNICODE-VERSION-STRING" \
+ "glyphlist.txt > afmtodit.tables"
+ exit 2
+fi
+
+unicode_data="$1"
+unicode_version="$2"
+glyph_list="$3"
+
+for f in "$1" "$3"
+do
+ if ! [ -r "$f" ]
+ then
+ echo "$0: '$f' does not exist or is not readable" >&2
+ exit 1
+ fi
+done
+
+# Handle UnicodeData.txt.
+#
+# Remove ranges and control characters,
+# then extract the decomposition field,
+# then remove lines without decomposition,
+# then remove all compatibility decompositions.
+cat "$1" \
+| sed -e '/^[^;]*;</d' \
+| sed -e 's/;[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\);.*$/;\1/' \
+| sed -e '/^[^;]*;$/d' \
+| sed -e '/^[^;]*;</d' > $$1
+
+# Prepare input for running cpp.
+cat $$1 \
+| sed -e 's/^\([^;]*\);/#define \1 /' \
+ -e 's/ / u/g' > $$2
+cat $$1 \
+| sed -e 's/^\([^;]*\);.*$/\1 u\1/' >> $$2
+
+# Run C preprocessor to recursively decompose.
+"${CPP:-cpp}" $$2 $$3
+
+# Convert it back to original format.
+cat $$3 \
+| sed -e '/#/d' \
+ -e '/^$/d' \
+ -e 's/ \+/ /g' \
+ -e 's/ *$//' \
+ -e 's/u//g' \
+ -e 's/^\([^ ]*\) /\1;/' > $$4
+
+# Write comment.
+cat <<END
+# This table was algorithmically derived from the file 'UnicodeData.txt'
+# for Unicode $unicode_version, available from unicode.org,
+# on `date '+%Y-%m-%d'`.
+END
+
+# Emit first table.
+echo 'my %unicode_decomposed = ('
+cat $$4 \
+| sed -e 's/ /_/g' \
+ -e 's/\(.*\);\(.*\)/ "\1", "\2",/'
+echo ');'
+echo ''
+
+# Write comment.
+cat <<END
+# This table was algorithmically derived from the Adobe Glyph List (AGL)
+# file 'glyphlist.txt' from the GitHub Adobe Type Tools agl-aglfn
+# project, on `date '+%Y-%m-%d'`.
+#
+# See "groff:" comments for altered mappings.
+END
+
+# Convert AGL syntax to a chunk of Perl.
+cat "$3" \
+| sed -e '/#/d' \
+ -e 's/ /_/g' \
+ -e '/;\(E\|F[0-8]\)/d' \
+ -e 's/\(.*\);\(.*\)/ "\1", "\2",/' > $$5
+
+# Perform groff replacements.
+sed \
+ -e 's/\("Delta"\), "2206",$/\1, "0394", # groff: not U+2206/' \
+ -e 's/\("Omega"\), "2126",$/\1, "03A9", # groff: not U+2126/' \
+ -e 's/\("mu"\), "00B5",$/\1, "03BC", # groff: not U+00B5/' \
+ < $$5 > $$6
+
+# Emit second table.
+echo 'my %AGL_to_unicode = ('
+cat $$6
+echo ');'
+
+# Remove temporary files.
+rm $$1 $$2 $$3 $$4 $$5 $$6
+
+# Local Variables:
+# fill-column: 72
+# End:
+# vim: set textwidth=72:
diff --git a/src/utils/grog/grog.1.man b/src/utils/grog/grog.1.man
new file mode 100644
index 0000000..efcd728
--- /dev/null
+++ b/src/utils/grog/grog.1.man
@@ -0,0 +1,628 @@
+.TH grog @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+grog \- \(lqgroff guess\(rq\(eminfer the
+.I groff
+command a document requires
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2021 Free Software Foundation, Inc.
+.\"
+.\" This file is part of grog, which is part of groff, a free software
+.\" project. You can redistribute it and/or modify it under the terms
+.\" of the GNU General Public License version 2 (GPL2) as published by
+.\" the Free Software Foundation.
+.\"
+.\" groff is distributed in the hope that it will be useful, but WITHOUT
+.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+.\" or FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" The text for GPL2 is available in the internet at
+.\" <http://www.gnu.org/licenses/gpl2.0.txt>.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_grog_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY grog
+.RB [ \-\-run ]
+.RB [ \-\-ligatures ]
+.RI [ groff-option\~ .\|.\|.\&]
+.RB [ \-\- ]
+.RI [ file\~ .\|.\|.]
+.YS
+.
+.
+.SY grog
+.B \-h
+.
+.SY grog
+.B \-\-help
+.YS
+.
+.
+.SY grog
+.B \-v
+.
+.SY grog
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I grog
+reads its input
+and guesses which
+.MR groff @MAN1EXT@
+options are needed to render it.
+.
+If no operands are given,
+or if
+.I file
+is
+.RB \[lq] \- \[rq],
+.I grog
+reads the standard input stream.
+.
+The corresponding
+.I groff
+command is normally written to the standard output stream.
+.
+With the option
+.BR \-\-run ,
+the inferred command is written to the standard error stream and then
+executed.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-h
+and
+.B \-\-help
+display a usage message,
+whereas
+.B \-v
+and
+.B \-\-version
+display version information;
+all exit afterward.
+.
+.
+.TP
+.B \-\-ligatures
+includes the arguments
+.B \-P\-y \-PU
+in the inferred
+.I groff
+command.
+.
+These are supported only by the
+.B pdf
+output device.
+.
+.
+.TP
+.B \-\-run
+writes the inferred command to the standard error stream and then
+executes it.
+.
+.
+.P
+All other specified short options
+(that is,
+arguments beginning with a minus sign
+.RB \[lq] \- \[rq]
+followed by a letter)
+are interpreted as
+.I groff
+options or option clusters with or without an option argument.
+.
+Such options are included in the constructed
+.I groff
+command line.
+.
+.
+.\" ====================================================================
+.SH Details
+.\" ====================================================================
+.
+.I grog
+reads each
+.I file
+operand,
+pattern-matching strings that are statistically likely to be
+characteristic of
+.MR roff @MAN7EXT@
+documents.
+.
+It tries to guess which of the following
+.I groff
+options are required to correctly render the input:
+.BR \-e ,
+.BR \-g ,
+.BR \-G ,
+.BR \-j ,
+.\" gideal is not implemented yet.
+.\" .BR \-J ,
+.BR \-p ,
+.BR \-R ,
+.\".BR \-s ,
+.B \-t
+(preprocessors);
+and
+.BR \-man ,
+.BR \-mdoc ,
+.BR \-mdoc\-old ,
+.BR \-me ,
+.BR \-mm ,
+.BR \-mom ,
+and
+.B \-ms
+(macro packages).
+.
+The inferred
+.I groff
+command including these options and any
+.I file
+parameters is written to the standard output stream.
+.
+.
+.P
+It is possible to specify arbitrary
+.I groff
+options on the command line.
+.
+These are included in the inferred command without change.
+.
+Choices of
+.I groff
+options include
+.B \-C
+to enable AT&T
+.I troff
+compatibility mode and
+.B \-T
+to select a non-default output device.
+.
+If the input is not encoded in US-ASCII,
+ISO 8859-1,
+or IBM code page 1047,
+specification of a
+.I groff
+option to run the
+.MR preconv @MAN1EXT@
+preprocessor is advised;
+see the
+.BR \-D ,
+.BR \-k ,
+and
+.B \-K
+options of
+.MR groff @MAN1EXT@ .
+.
+For UTF-8 input,
+.B \-k
+is a good choice.
+.
+.
+.P
+.I groff
+may issue diagnostic messages when an inappropriate
+.B \-m
+option,
+or multiple conflicting ones,
+are specified.
+.
+Consequently,
+it is best to specify no
+.B \-m
+options to
+.I grog
+unless it cannot correctly infer all of the
+.B \-m
+arguments a document requires.
+.
+A
+.I roff
+document can also be written without recourse to any macro package.
+.
+In such cases,
+.I grog
+will infer a
+.I groff
+command without an
+.B \-m
+option.
+.
+.
+.\" ====================================================================
+.SS Limitations
+.\" ====================================================================
+.
+.I grog
+presumes that the input does not change the escape,
+control,
+or no-break control characters.
+.
+.I grog
+does not parse
+.I roff
+input line continuation or control structures
+(brace escape sequences and the
+.RB \[lq] if \[rq],
+.RB \[lq] ie \[rq],
+and
+.RB \[lq] el \[rq]
+requests)
+nor
+.IR groff 's
+.RB \[lq] while \[rq].
+.
+Thus the input
+.
+.RS
+.EX
+\&.if \[rs]
+t .NH 1
+\&.if n .SH
+Introduction
+.EE
+.RE
+.
+will conceal the use of the
+.I ms
+macros
+.B NH
+and
+.B SH
+from
+.IR grog .
+.
+Such constructions are regarded by
+.IR grog 's
+implementors as insufficiently common to cause many inference problems.
+.
+Preprocessors can be even stricter when matching macro calls that
+bracket the regions of an input file they replace.
+.
+.IR pic ,
+for example,
+requires
+.BR PS ,
+.BR PE ,
+and
+.B PF
+calls to immediately follow the default control character at the
+beginning of a line.
+.
+.
+.P
+Detection of the
+.B \-s
+option
+(the
+.MR @g@soelim @MAN1EXT@
+preprocessor)
+is tricky;
+to correctly infer its necessity would require
+.I grog
+to recursively open all files given as arguments to the
+.B .so
+request under the same conditions that
+.I @g@soelim
+itself does so;
+see its man page.
+.
+Recall that
+.I @g@soelim
+is necessary only if sourced files need to be preprocessed.
+.
+Therefore,
+as a workaround,
+you may want to run the input through
+.I @g@soelim
+manually,
+piping it to
+.IR grog ,
+and compare the output to running
+.I grog
+on the input directly.
+.
+If the
+.RI \[lq] @g@soelim \[rq]ed
+input causes
+.I grog
+to infer additional preprocessor options,
+then
+.B \-s
+is likely necessary.
+.
+.
+.RS
+.P
+.EX
+$ \c
+.B printf \[dq].TS\[rs]nl.\[rs]nI\[aq]m a table.\[rs]n.TE\[rs]n\[dq] > \
+3.roff
+$ \c
+.B printf \[dq].so 3.roff\[rs]n\[dq] > 2.roff
+$ \c
+.B printf \[dq].XP\[rs]n.so 2.roff\[rs]n\[dq] > 1.roff
+$ \c
+.B grog 1.roff
+groff \-ms 1.roff
+$ \c
+.B @g@soelim 1.roff | grog
+groff \-t \-ms \-
+.EE
+.RE
+.
+.
+.P
+In the foregoing example,
+we see that this procedure enabled
+.I grog
+to detect
+.MR @g@tbl @MAN1EXT@
+macros,
+so we would add
+.B \-s
+as well as the detected
+.B \-t
+option to a revised
+.I grog
+or
+.I groff
+command.
+.
+.
+.RS
+.P
+.EX
+$ \c
+.B grog \-st 1.roff
+groff \-st \-ms 1.roff
+.EE
+.RE
+.
+.
+.\" ====================================================================
+.SH "Exit status"
+.\" ====================================================================
+.
+.I grog
+exits with error status
+.B 1
+if a macro package appears to be in use by the input document,
+but
+.I grog
+was unable to infer which one,
+or
+.B 2
+if there were problems handling an option or operand.
+.
+It otherwise exits with status
+.BR 0 .
+.
+(If the
+.B \-\-run
+option is specified,
+.IR groff 's
+exit status is discarded.)
+.
+Inferring no preprocessors or macro packages is not an error condition;
+a valid
+.I roff
+document need not use either.
+.
+Even plain text is valid input,
+if one is mindful of the syntax of the control and escape characters.
+.
+.
+.\" ====================================================================
+.SH Examples
+.\" ====================================================================
+.
+Running
+.
+.RS
+.EX
+.B grog @DOCDIR@/meintro.me
+.EE
+.RE
+at the command line results in
+.RS
+.EX
+groff \-me @DOCDIR@/meintro.me
+.EE
+.RE
+.
+because
+.I grog
+recognizes that the file
+.I meintro.me
+is written using macros from the
+.I me
+package.
+.
+The command
+.
+.RS
+.EX
+.B grog @DOCDIR@/pic.ms
+.EE
+.RE
+.
+outputs
+.
+.RS
+.EX
+groff \-e \-p \-t \-ms @DOCDIR@/pic.ms
+.EE
+.RE
+.
+on the other hand.
+.
+Besides discerning the
+.I ms
+macro package,
+.I grog
+recognizes that the file
+.I pic.ms
+additionally needs the combination of
+.B \-t
+for
+.IR tbl ,
+.B \-e
+for
+.IR eqn ,
+and
+.B \-p
+for
+.IR pic .
+.
+.
+.\" XXX: grog no longer (June 2021) attempts to detect this scenario.
+.\" It's also not a practical one; full-service macro packages don't
+.\" generally support being "unloaded" for subsequent processing of
+.\" another document using a different one. We do achieve it, with
+.\" care, in groff with man(7) and mdoc(7) (see andoc.tmac).
+.\" .P
+.\" If both of the former example files are combined in the command
+.\" .
+.\" .RS
+.\" .EX
+.\" .B grog meintro.me pic.ms
+.\" .EE
+.\" .RE
+.\" .
+.\" a diagnostic message is sent to the standard error stream because
+.\" some macro packages cannot be combined.
+.\" .
+.\" Nevertheless the corresponding output with the wrong options is
+.\" written to standard output:
+.\" .
+.\" .RS
+.\" .EX
+.\" groff \-t \-e \-p \-ms meintro.me pic.ms
+.\" .EE
+.\" .RE
+.\" .
+.\" and
+.\" .I grog
+.\" terminates with an error exit status.
+.
+.
+.P
+Consider a file
+.IR \%doc/\:\%grnexampl.me ,
+which uses the
+.I @g@grn
+preprocessor to include a
+.MR gremlin 1
+picture file in an
+.I me \" generic
+document.
+.
+Let's say we want to suppress color output,
+produce a DVI file,
+and get backtraces for any errors that
+.I @g@troff
+encounters.
+.
+The command
+.
+.RS
+.EX
+.B grog \-bc \-Idoc \-Tdvi doc/grnexmpl.me
+.EE
+.RE
+.
+is processed by
+.I grog
+into
+.
+.RS
+.EX
+groff \-bc \-Idoc \-Tdvi \-e \-g \-me doc/grnexmpl.me
+.EE
+.RE
+.
+where we can see that
+.I grog
+has inferred the
+.I me \" generic
+macro package along with the
+.I eqn \" generic
+and
+.I grn \" generic
+preprocessors.
+.
+(The input file is located in
+.I @DOCDIR@
+if you'd like to try this example yourself.)
+.
+.
+.\" ====================================================================
+.SH Authors
+.\" ====================================================================
+.
+.I grog
+was originally written in Bourne shell by James Clark.
+.
+The current implementation in Perl was written by
+.MT groff\-bernd\:.warken\-72@\:web\:.de
+Bernd Warken
+.ME
+and heavily revised by
+.MT g.branden\:.robinson@\:gmail\:.com
+G.\& Branden Robinson
+.ME .
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR groff @MAN1EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_grog_1_man_C]
+.do rr *groff_grog_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/grog/grog.am b/src/utils/grog/grog.am
new file mode 100644
index 0000000..f7ca5eb
--- /dev/null
+++ b/src/utils/grog/grog.am
@@ -0,0 +1,50 @@
+# Copyright (C) 1993-2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+grog_srcdir = $(top_srcdir)/src/utils/grog
+bin_SCRIPTS += grog
+man1_MANS += src/utils/grog/grog.1
+EXTRA_DIST += \
+ src/utils/grog/grog.1.man \
+ src/utils/grog/grog.pl \
+ src/utils/grog/tests/foo.man
+
+grog: $(grog_srcdir)/grog.pl $(SH_DEPS_SED_SCRIPT)
+ $(AM_V_GEN)$(RM) $@ \
+ && sed -f "$(SH_DEPS_SED_SCRIPT)" \
+ -e "s|[@]PERL[@]|$(PERL)|" \
+ -e "s|[@]VERSION[@]|$(VERSION)|" \
+ -e "$(SH_SCRIPT_SED_CMD)" \
+ $(grog_srcdir)/grog.pl \
+ >$@ \
+ && chmod +x $@
+
+grog_TESTS = \
+ src/utils/grog/tests/PF-does-not-start-pic-region.sh \
+ src/utils/grog/tests/avoid-refer-fakeout.sh \
+ src/utils/grog/tests/preserve-groff-options.sh \
+ src/utils/grog/tests/recognize-perl-pod.sh \
+ src/utils/grog/tests/smoke-test.sh
+TESTS += $(grog_TESTS)
+EXTRA_DIST += $(grog_TESTS)
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/grog/grog.pl b/src/utils/grog/grog.pl
new file mode 100644
index 0000000..28973c5
--- /dev/null
+++ b/src/utils/grog/grog.pl
@@ -0,0 +1,721 @@
+#!@PERL@
+# grog - guess options for groff command
+# Inspired by doctype script in Kernighan & Pike, Unix Programming
+# Environment, pp 306-8.
+
+# Copyright (C) 1993-2021 Free Software Foundation, Inc.
+# Written by James Clark.
+# Rewritten in Perl by Bernd Warken <groff-bernd.warken-72@web.de>.
+# Hacked up by G. Branden Robinson, 2021.
+
+# This file is part of 'grog', which is part of 'groff'.
+
+# 'groff' is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# 'groff' is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+
+use warnings;
+use strict;
+
+use File::Spec;
+
+my $groff_version = 'DEVELOPMENT';
+
+my @command = (); # the constructed groff command
+my @requested_package = (); # arguments to '-m' grog options
+my @inferred_preprocessor = (); # preprocessors the document uses
+my @inferred_main_package = (); # full-service package(s) detected
+my $main_package; # full-service package we go with
+my $do_run = 0; # run generated 'groff' command
+my $use_compatibility_mode = 0; # is -C being passed to groff?
+
+my %preprocessor_for_macro = (
+ 'EQ', 'eqn',
+ 'G1', 'grap',
+ 'GS', 'grn',
+ 'PS', 'pic',
+ '[', 'refer',
+ #'so', 'soelim', # Can't be inferred this way; see grog man page.
+ 'TS', 'tbl',
+ 'cstart', 'chem',
+ 'lilypond', 'glilypond',
+ 'Perl', 'gperl',
+ 'pinyin', 'gpinyin',
+);
+
+my $program_name = $0;
+{
+ my ($v, $d, $f) = File::Spec->splitpath($program_name);
+ $program_name = $f;
+}
+
+my %user_macro;
+my %score = ();
+
+my @input_file;
+
+# .TH is both a man(7) macro and often used with tbl(1). We expect to
+# find .TH in ms(7) documents only between .TS and .TE calls, and in
+# man(7) documents only as the first macro call.
+my $have_seen_first_macro_call = 0;
+# man(7) and ms(7) use many of the same macro names; do extra checking.
+my $man_score = 0;
+my $ms_score = 0;
+
+my $had_inference_problem = 0;
+my $had_processing_problem = 0;
+my $have_any_valid_arguments = 0;
+
+
+sub fail {
+ my $text = shift;
+ print STDERR "$program_name: error: $text\n";
+ $had_processing_problem = 1;
+}
+
+
+sub warn {
+ my $text = shift;
+ print STDERR "$program_name: warning: $text\n";
+}
+
+
+sub process_arguments {
+ my $no_more_options = 0;
+ my $delayed_option = '';
+ my $was_minus = 0;
+ my $optarg = 0;
+ my $pdf_with_ligatures = 0;
+
+ foreach my $arg (@ARGV) {
+ if ( $optarg ) {
+ push @command, $arg;
+ $optarg = 0;
+ next;
+ }
+
+ if ($no_more_options) {
+ push @input_file, $arg;
+ next;
+ }
+
+ if ($delayed_option) {
+ if ($delayed_option eq '-m') {
+ push @requested_package, $arg;
+ $arg = '';
+ } else {
+ push @command, $delayed_option;
+ }
+
+ push @command, $arg if $arg;
+ $delayed_option = '';
+ next;
+ }
+
+ unless ( $arg =~ /^-/ ) { # file name, no opt, no optarg
+ push @input_file, $arg;
+ next;
+ }
+
+ # now $arg starts with '-'
+
+ if ($arg eq '-') {
+ unless ($was_minus) {
+ push @input_file, $arg;
+ $was_minus = 1;
+ }
+ next;
+ }
+
+ if ($arg eq '--') {
+ $no_more_options = 1;
+ next;
+ }
+
+ # Handle options that cause an early exit.
+ &version() if ($arg eq '-v' || $arg eq '--version');
+ &usage(0) if ($arg eq '-h' || $arg eq '--help');
+
+ if ($arg =~ '^--.') {
+ if ($arg =~ '^--(run|with-ligatures)$') {
+ $do_run = 1 if ($arg eq '--run');
+ $pdf_with_ligatures = 1 if ($arg eq '--with-ligatures');
+ } else {
+ &fail("unrecognized grog option '$arg'; ignored");
+ &usage(1);
+ }
+ next;
+ }
+
+ # Handle groff options that take an argument.
+
+ # Handle the option argument being separated by whitespace.
+ if ($arg =~ /^-[dfFIKLmMnoPrTwW]$/) {
+ $delayed_option = $arg;
+ next;
+ }
+
+ # Handle '-m' option without subsequent whitespace.
+ if ($arg =~ /^-m/) {
+ my $package = $arg;
+ $package =~ s/-m//;
+ push @requested_package, $package;
+ next;
+ }
+
+ # Treat anything else as (possibly clustered) groff options that
+ # take no arguments.
+
+ # Our do_line() needs to know if it should do compatibility parsing.
+ $use_compatibility_mode = 1 if ($arg =~ /C/);
+
+ push @command, $arg;
+ }
+
+ if ($pdf_with_ligatures) {
+ push @command, '-P-y';
+ push @command, '-PU';
+ }
+
+ @input_file = ('-') unless (@input_file);
+} # process_arguments()
+
+
+sub process_input {
+ foreach my $file (@input_file) {
+ unless ( open(FILE, $file eq "-" ? $file : "< $file") ) {
+ &fail("cannot open '$file': $!");
+ next;
+ }
+
+ $have_any_valid_arguments = 1;
+
+ while (my $line = <FILE>) {
+ chomp $line;
+ &do_line($line);
+ }
+
+ close(FILE);
+ } # end foreach
+} # process_input()
+
+
+# Push item onto inferred full-service list only if not already present.
+sub push_main_package {
+ my $pkg = shift;
+ if (!grep(/^$pkg/, @inferred_main_package)) {
+ push @inferred_main_package, $pkg;
+ }
+} # push_main_package()
+
+
+sub do_line {
+ my $command; # request or macro name
+ my $args; # request or macro arguments
+
+ my $line = shift;
+
+ # Check for a Perl Pod::Man comment.
+ #
+ # An alternative to this kludge is noted below: if a "standard" macro
+ # is redefined, we could delete it from the relevant lists and
+ # hashes.
+ if ($line =~ /\\\" Automatically generated by Pod::Man/) {
+ $man_score += 100;
+ }
+
+ # Strip comments.
+ $line =~ s/\\".*//;
+ $line =~ s/\\#.*// unless $use_compatibility_mode;
+
+ return unless ($line =~ /^[.']/); # Ignore text lines.
+
+ # Perform preprocessor checks; they scan their inputs using a rump
+ # interpretation of roff(7) syntax that requires the default control
+ # character and no space between it and the macro name. In AT&T
+ # compatibility mode, no space (or newline!) is required after the
+ # macro name, either. We mimic the preprocessors themselves; eqn(1),
+ # for instance, does not recognize '.EN' if '.EQ' has not been seen.
+ my $boundary = '\\b';
+ $boundary = '' if ($use_compatibility_mode);
+
+ if ($line =~ /^\.(\S\S)$boundary/ || $line =~ /^\.(\[)/) {
+ my $macro = $1;
+ # groff identifiers can have extremely weird characters in them.
+ # The ones we care about are conventionally named, but me(7)
+ # documents can call macros like '+c', so quote carefully.
+ if (grep(/^\Q$macro\E$/, keys %preprocessor_for_macro)) {
+ my $preproc = $preprocessor_for_macro{$macro};
+ if (!grep(/$preproc/, @inferred_preprocessor)) {
+ push @inferred_preprocessor, $preproc;
+ }
+ }
+ }
+
+ # Normalize control lines; convert no-break control character to the
+ # regular one and remove unnecessary whitespace.
+ $line =~ s/^['.]\s*/./;
+ $line =~ s/\s+$//;
+
+ return if ($line =~ /^\.$/); # Ignore empty request.
+ return if ($line =~ /^\.\\?\.$/); # Ignore macro definition ends.
+
+ # Split control line into a request or macro call and its arguments.
+
+ # Handle single-letter macro names.
+ if ($line =~ /^\.(\S)(\s+(.*))?$/) {
+ $command = $1;
+ $args = $2;
+ # Handle two-letter macro/request names in compatibility mode.
+ } elsif ($use_compatibility_mode) {
+ $line =~ /^\.(\S\S)\s*(.*)$/;
+ $command = $1;
+ $args = $2;
+ # Handle multi-letter macro/request names in groff mode.
+ } else {
+ $line =~ /^\.(\S+)(\s+(.*))?$/;
+ $command = $1;
+ $args = $3;
+ }
+
+ $command = '' unless ($command);
+ $args = '' unless ($args);
+
+ ######################################################################
+ # user-defined macros
+
+ # If the line calls a user-defined macro, skip it.
+ return if (exists $user_macro{$command});
+
+ # These are all requests supported by groff 1.23.0.
+ my @request = ('ab', 'ad', 'af', 'aln', 'als', 'am', 'am1', 'ami',
+ 'ami1', 'as', 'as1', 'asciify', 'backtrace', 'bd',
+ 'blm', 'box', 'boxa', 'bp', 'br', 'brp', 'break', 'c2',
+ 'cc', 'ce', 'cf', 'cflags', 'ch', 'char', 'chop',
+ 'class', 'close', 'color', 'composite', 'continue',
+ 'cp', 'cs', 'cu', 'da', 'de', 'de1', 'defcolor', 'dei',
+ 'dei1', 'device', 'devicem', 'di', 'do', 'ds', 'ds1',
+ 'dt', 'ec', 'ecr', 'ecs', 'el', 'em', 'eo', 'ev',
+ 'evc', 'ex', 'fam', 'fc', 'fchar', 'fcolor', 'fi',
+ 'fp', 'fschar', 'fspecial', 'ft', 'ftr', 'fzoom',
+ 'gcolor', 'hc', 'hcode', 'hla', 'hlm', 'hpf', 'hpfa',
+ 'hpfcode', 'hw', 'hy', 'hym', 'hys', 'ie', 'if', 'ig',
+ 'in', 'it', 'itc', 'kern', 'lc', 'length', 'linetabs',
+ 'lf', 'lg', 'll', 'lsm', 'ls', 'lt', 'mc', 'mk', 'mso',
+ 'msoquiet', 'na', 'ne', 'nf', 'nh', 'nm', 'nn', 'nop',
+ 'nr', 'nroff', 'ns', 'nx', 'open', 'opena', 'os',
+ 'output', 'pc', 'pev', 'pi', 'pl', 'pm', 'pn', 'pnr',
+ 'po', 'ps', 'psbb', 'pso', 'ptr', 'pvs', 'rchar', 'rd',
+ 'return', 'rfschar', 'rj', 'rm', 'rn', 'rnn', 'rr',
+ 'rs', 'rt', 'schar', 'shc', 'shift', 'sizes', 'so',
+ 'soquiet', 'sp', 'special', 'spreadwarn', 'ss',
+ 'stringdown', 'stringup', 'sty', 'substring', 'sv',
+ 'sy', 'ta', 'tc', 'ti', 'tkf', 'tl', 'tm', 'tm1',
+ 'tmc', 'tr', 'trf', 'trin', 'trnt', 'troff', 'uf',
+ 'ul', 'unformat', 'vpt', 'vs', 'warn', 'warnscale',
+ 'wh', 'while', 'write', 'writec', 'writem');
+
+ # Add user-defined macro names to %user_macro.
+ #
+ # Macros can also be defined with .dei{,1}, ami{,1}, but supporting
+ # that would be a heavy lift for the benefit of users that probably
+ # don't require grog's help. --GBR
+ if ($command =~ /^(de|am)1?$/) {
+ my $name = $args;
+ # Strip off any end macro.
+ $name =~ s/\s+.*$//;
+ # Handle special cases of macros starting with '[' or ']'.
+ if ($name =~ /^[][]/) {
+ delete $preprocessor_for_macro{'['};
+ }
+ # XXX: If the macro name shadows a standard macro name, maybe we
+ # should delete the latter from our lists and hashes. This might
+ # depend on whether the document is trying to remain compatible
+ # with an existing interface, or simply colliding with names they
+ # don't care about (consider a raw roff document that defines 'PP').
+ # --GBR
+ $user_macro{$name} = 0 unless (exists $user_macro{$name});
+ return;
+ }
+
+ # XXX: Handle .rm as well?
+
+ # Ignore all other requests. Again, macro names can contain Perl
+ # regex metacharacters, so be careful.
+ return if (grep(/^\Q$command\E$/, @request));
+ # What remains must be a macro name.
+ my $macro = $command;
+
+ $have_seen_first_macro_call = 1;
+ $score{$macro}++;
+
+
+ ######################################################################
+ # macro package (tmac)
+ ######################################################################
+
+ # man and ms share too many macro names for the following approach to
+ # be fruitful for many documents; see &infer_man_or_ms_package.
+ #
+ # We can put one thumb on the scale, however.
+ if ((!$have_seen_first_macro_call) && ($macro eq 'TH')) {
+ # TH as the first call in a document screams man(7).
+ $man_score += 100;
+ }
+
+ ##########
+ # mdoc
+ if ($macro =~ /^Dd$/) {
+ &push_main_package('doc');
+ return;
+ }
+
+ ##########
+ # old mdoc
+ if ($macro =~ /^(Tp|Dp|De|Cx|Cl)$/) {
+ &push_main_package('doc-old');
+ return;
+ }
+
+ ##########
+ # me
+
+ if ($macro =~ /^(
+ [ilnp]p|
+ n[12]|
+ sh
+ )$/x) {
+ &push_main_package('e');
+ return;
+ }
+
+
+ #############
+ # mm and mmse
+
+ if ($macro =~ /^(
+ H|
+ MULB|
+ LO|
+ LT|
+ NCOL|
+ PH|
+ SA
+ )$/x) {
+ if ($macro =~ /^LO$/) {
+ if ( $args =~ /^(DNAMN|MDAT|BIL|KOMP|DBET|BET|SIDOR)/ ) {
+ &push_main_package('mse');
+ return;
+ }
+ } elsif ($macro =~ /^LT$/) {
+ if ( $args =~ /^(SVV|SVH)/ ) {
+ &push_main_package('mse');
+ return;
+ }
+ }
+ &push_main_package('m');
+ return;
+ }
+
+ ##########
+ # mom
+
+ if ($macro =~ /^(
+ ALD|
+ AUTHOR|
+ CHAPTER_TITLE|
+ CHAPTER|
+ COLLATE|
+ DOCHEADER|
+ DOCTITLE|
+ DOCTYPE|
+ DOC_COVER|
+ FAMILY|
+ FAM|
+ FT|
+ LEFT|
+ LL|
+ LS|
+ NEWPAGE|
+ NO_TOC_ENTRY|
+ PAGENUMBER|
+ PAGE|
+ PAGINATION|
+ PAPER|
+ PRINTSTYLE|
+ PT_SIZE|
+ START|
+ TITLE|
+ TOC_AFTER_HERE
+ TOC|
+ T_MARGIN|
+ )$/x) {
+ &push_main_package('om');
+ return;
+ }
+} # do_line()
+
+my @preprocessor = ();
+
+
+sub infer_preprocessors {
+ my %option_for_preprocessor = (
+ 'eqn', '-e',
+ 'grap', '-G',
+ 'grn', '-g',
+ 'pic', '-p',
+ 'refer', '-R',
+ #'soelim', '-s', # Can't be inferred this way; see grog man page.
+ 'tbl', '-t',
+ 'chem', '-j'
+ );
+
+ # Use a temporary list we can sort later. We want the options to show
+ # up in a stable order for testing purposes instead of the order their
+ # macros turn up in the input. groff doesn't care about the order.
+ my @opt = ();
+
+ foreach my $preproc (@inferred_preprocessor) {
+ my $preproc_option = $option_for_preprocessor{$preproc};
+
+ if ($preproc_option) {
+ push @opt, $preproc_option;
+ } else {
+ push @preprocessor, $preproc;
+ }
+ }
+ push @command, sort @opt;
+} # infer_preprocessors()
+
+
+# Return true (1) if either the man or ms package is inferred.
+sub infer_man_or_ms_package {
+ my @macro_ms = ('RP', 'TL', 'AU', 'AI', 'DA', 'ND', 'AB', 'AE',
+ 'QP', 'QS', 'QE', 'XP',
+ 'NH',
+ 'R',
+ 'CW',
+ 'BX', 'UL', 'LG', 'NL',
+ 'KS', 'KF', 'KE', 'B1', 'B2',
+ 'DS', 'DE', 'LD', 'ID', 'BD', 'CD', 'RD',
+ 'FS', 'FE',
+ 'OH', 'OF', 'EH', 'EF', 'P1',
+ 'TA', '1C', '2C', 'MC',
+ 'XS', 'XE', 'XA', 'TC', 'PX',
+ 'IX', 'SG');
+
+ my @macro_man = ('BR', 'IB', 'IR', 'RB', 'RI', 'P', 'TH', 'TP', 'SS',
+ 'HP', 'PD',
+ 'AT', 'UC',
+ 'SB',
+ 'EE', 'EX',
+ 'OP',
+ 'MT', 'ME', 'SY', 'YS', 'TQ', 'UR', 'UE');
+
+ my @macro_man_or_ms = ('B', 'I', 'BI',
+ 'DT',
+ 'RS', 'RE',
+ 'SH',
+ 'SM',
+ 'IP', 'LP', 'PP');
+
+ for my $key (@macro_man_or_ms, @macro_man, @macro_ms) {
+ $score{$key} = 0 unless exists $score{$key};
+ }
+
+ # Compute a score for each package by counting occurrences of their
+ # characteristic macros.
+ foreach my $key (@macro_man_or_ms) {
+ $man_score += $score{$key};
+ $ms_score += $score{$key};
+ }
+
+ foreach my $key (@macro_man) {
+ $man_score += $score{$key};
+ }
+
+ foreach my $key (@macro_ms) {
+ $ms_score += $score{$key};
+ }
+
+ if (!$ms_score && !$man_score) {
+ # The input may be a "raw" roff document; this is not a problem,
+ # but it does mean no package was inferred.
+ return 0;
+ } elsif ($ms_score == $man_score) {
+ # If there was no TH call, it's not a (valid) man(7) document.
+ if (!$score{'TH'}) {
+ &push_main_package('s');
+ } else {
+ &warn("document ambiguous; disambiguate with -man or -ms option");
+ $had_inference_problem = 1;
+ }
+ return 0;
+ } elsif ($ms_score > $man_score) {
+ &push_main_package('s');
+ } else {
+ &push_main_package('an');
+ }
+
+ return 1;
+} # infer_man_or_ms_package()
+
+
+sub construct_command {
+ my @main_package = ('an', 'doc', 'doc-old', 'e', 'm', 'om', 's');
+ my $file_args_included; # file args now only at 1st preproc
+ unshift @command, 'groff';
+ if (@preprocessor) {
+ my @progs;
+ $progs[0] = shift @preprocessor;
+ push(@progs, @input_file);
+ for (@preprocessor) {
+ push @progs, '|';
+ push @progs, $_;
+ }
+ push @progs, '|';
+ unshift @command, @progs;
+ $file_args_included = 1;
+ } else {
+ $file_args_included = 0;
+ }
+
+ foreach (@command) {
+ next unless /\s/;
+ # when one argument has several words, use accents
+ $_ = "'" . $_ . "'";
+ }
+
+ my $have_ambiguous_main_package = 0;
+ my $inferred_main_package_count = scalar @inferred_main_package;
+
+ # Did we infer multiple full-service packages?
+ if ($inferred_main_package_count > 1) {
+ $have_ambiguous_main_package = 1;
+ # For each one the user explicitly requested...
+ for my $pkg (@requested_package) {
+ # ...did it resolve the ambiguity for us?
+ if (grep(/$pkg/, @inferred_main_package)) {
+ @inferred_main_package = ($pkg);
+ $have_ambiguous_main_package = 0;
+ last;
+ }
+ }
+ } elsif ($inferred_main_package_count == 1) {
+ $main_package = shift @inferred_main_package;
+ }
+
+ if ($have_ambiguous_main_package) {
+ # TODO: Alphabetical is probably not the best ordering here. We
+ # should tally up scores on a per-package basis generally, not just
+ # for an and s.
+ for my $pkg (@main_package) {
+ if (grep(/$pkg/, @inferred_main_package)) {
+ $main_package = $pkg;
+ &warn("document ambiguous (choosing '$main_package'"
+ . " from '@inferred_main_package'); disambiguate with -m"
+ . " option");
+ $had_inference_problem = 1;
+ last;
+ }
+ }
+ }
+
+ # If a full-service package was explicitly requested, warn if the
+ # inference differs from the request. This also ensures that all -m
+ # arguments are placed in the same order that the user gave them;
+ # caveat dictator.
+ my @auxiliary_package_argument = ();
+ for my $pkg (@requested_package) {
+ my $is_auxiliary_package = 1;
+ if (grep(/$pkg/, @main_package)) {
+ $is_auxiliary_package = 0;
+ if ($pkg ne $main_package) {
+ &warn("overriding inferred package '$main_package'"
+ . " with requested package '$pkg'");
+ $main_package = $pkg;
+ }
+ }
+ if ($is_auxiliary_package) {
+ push @auxiliary_package_argument, "-m" . $pkg;
+ }
+ }
+
+ push @command, '-m' . $main_package if ($main_package);
+ push @command, @auxiliary_package_argument;
+ push @command, @input_file unless ($file_args_included);
+
+ #########
+ # execute the 'groff' command here with option '--run'
+ if ( $do_run ) { # with --run
+ print STDERR "@command\n";
+ my $cmd = join ' ', @command;
+ system($cmd);
+ } else {
+ print "@command\n";
+ }
+} # construct_command()
+
+
+sub usage {
+ my $stream = *STDOUT;
+ my $had_error = shift;
+ $stream = *STDERR if $had_error;
+ my $grog = $program_name;
+ print $stream "usage: $grog [--ligatures] [--run]" .
+ " [groff-option ...] [--] [file ...]\n" .
+ "usage: $grog {-v | --version}\n" .
+ "usage: $grog {-h | --help}\n";
+ unless ($had_error) {
+ print $stream "\n" .
+"Read each roff(7) input FILE and attempt to infer an appropriate\n" .
+"groff(1) command to format it. See the grog(1) manual page.\n";
+ }
+ exit $had_error;
+}
+
+
+sub version {
+ print "GNU $program_name (groff) $groff_version\n";
+ exit 0;
+} # version()
+
+
+# initialize
+
+my $in_unbuilt_source_tree = 0;
+{
+ my $at = '@';
+ $in_unbuilt_source_tree = 1 if ('@VERSION@' eq "${at}VERSION${at}");
+}
+
+$groff_version = '@VERSION@' unless ($in_unbuilt_source_tree);
+
+&process_arguments();
+&process_input();
+
+if ($have_any_valid_arguments) {
+ &infer_preprocessors();
+ &infer_man_or_ms_package() if (scalar @inferred_main_package != 1);
+ &construct_command();
+}
+
+exit 2 if ($had_processing_problem);
+exit 1 if ($had_inference_problem);
+exit 0;
+
+# Local Variables:
+# fill-column: 72
+# mode: CPerl
+# End:
+# vim: set cindent noexpandtab shiftwidth=2 softtabstop=2 textwidth=72:
diff --git a/src/utils/grog/tests/PF-does-not-start-pic-region.sh b/src/utils/grog/tests/PF-does-not-start-pic-region.sh
new file mode 100755
index 0000000..d3b871f
--- /dev/null
+++ b/src/utils/grog/tests/PF-does-not-start-pic-region.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+grog="${abs_top_builddir:-.}/grog"
+
+# Regression test Savannah #60772.
+#
+# .PF does not _start_ a pic(1) region; it ends one.
+
+DOC='.PF
+.PE'
+
+echo "$DOC" | "$grog" \
+ | grep -Fqx 'groff -'
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/src/utils/grog/tests/avoid-refer-fakeout.sh b/src/utils/grog/tests/avoid-refer-fakeout.sh
new file mode 100755
index 0000000..f163bed
--- /dev/null
+++ b/src/utils/grog/tests/avoid-refer-fakeout.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+grog="${abs_top_builddir:-.}/grog"
+
+# Regression-test Savannah #61520.
+#
+# Don't be fooled by documents (like xterm's ctlseqs.ms) that define
+# macros with names that start with '[' or ']'.
+
+input=".de []
+..
+.[] foo"
+
+echo "$input" | "$grog" | grep -Fqx 'groff -'
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/src/utils/grog/tests/foo.man b/src/utils/grog/tests/foo.man
new file mode 100644
index 0000000..28e9fe6
--- /dev/null
+++ b/src/utils/grog/tests/foo.man
@@ -0,0 +1,146 @@
+.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+. ds C`
+. ds C'
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is >0, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
+..
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{\
+. if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. if !\nF==2 \{\
+. nr % 0
+. nr F 2
+. \}
+. \}
+.\}
+.rr rF
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "FOO 1"
+.TH FOO 1 "2021-06-30" "perl v5.28.1" "User Contributed Perl Documentation"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "Name"
+.IX Header "Name"
+foo \- a frobnicator
+.SH "Description"
+.IX Header "Description"
+This is my program.
diff --git a/src/utils/grog/tests/preserve-groff-options.sh b/src/utils/grog/tests/preserve-groff-options.sh
new file mode 100755
index 0000000..3290798
--- /dev/null
+++ b/src/utils/grog/tests/preserve-groff-options.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+grog="${abs_top_builddir:-.}/grog"
+
+# Regression test Savannah #57873.
+#
+# Don't mangle groff options.
+
+echo | "$grog" -ww -fN -P-pa5 -ra5=0 \
+ | grep -Fqx 'groff -ww -fN -P-pa5 -ra5=0 -'
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/src/utils/grog/tests/recognize-perl-pod.sh b/src/utils/grog/tests/recognize-perl-pod.sh
new file mode 100755
index 0000000..bc13ece
--- /dev/null
+++ b/src/utils/grog/tests/recognize-perl-pod.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+grog="${abs_top_builddir:-.}/grog"
+doc="${abs_top_srcdir:-..}/src/utils/grog/tests/foo.man"
+
+# Regression test Savannah #59622.
+#
+# Recognize the strongly-accented dialect of man(7) produced by
+# pod2man(1).
+
+"$grog" "$doc" | grep '^groff -man .*/src/utils/grog/tests/foo\.man'
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/src/utils/grog/tests/smoke-test.sh b/src/utils/grog/tests/smoke-test.sh
new file mode 100755
index 0000000..2da1fc4
--- /dev/null
+++ b/src/utils/grog/tests/smoke-test.sh
@@ -0,0 +1,153 @@
+#!/bin/sh
+#
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+set -e
+
+grog="${abs_top_builddir:-.}/grog"
+src="${abs_top_srcdir:-..}"
+
+doc=src/preproc/eqn/neqn.1
+echo "testing simple man(7) page $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -man '"$doc"
+
+doc=src/preproc/tbl/tbl.1
+echo "testing tbl(1)-using man(7) page $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -t -man '"$doc"
+
+doc=man/groff_diff.7
+echo "testing eqn(1)-using man(7) page $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -e -man '"$doc"
+
+# BUG: grog doesn't yet handle .if, .ie, .while.
+#doc=src/preproc/soelim/soelim.1
+#echo "testing pic(1)-using man(7) page $doc" >&2
+#"$grog" "$doc" | \
+# grep -Fqx 'groff -p -man '"$doc"
+
+doc=tmac/groff_mdoc.7
+echo "testing tbl(1)-using mdoc(7) page $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -t -mdoc '"$doc"
+
+doc=$src/doc/meintro.me.in
+echo "testing me(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -me '"$doc"
+
+doc=$src/doc/meintro_fr.me.in
+echo "testing tbl(1)-using me(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -t -me '"$doc"
+
+doc=$src/doc/meref.me.in
+echo "testing me(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -me '"$doc"
+
+doc=$src/doc/grnexmpl.me
+echo "testing grn(1)- and eqn(1)-using me(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -e -g -me '"$doc"
+
+doc=$src/contrib/mm/examples/letter.mm
+echo "testing mm(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mm '"$doc"
+
+doc=$src/contrib/mom/examples/copyright-chapter.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/copyright-default.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/letter.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/mom-pdf.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/mon_premier_doc.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/sample_docs.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/mom/examples/slide-demo.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -e -p -t -mom '"$doc"
+
+doc=$src/contrib/mom/examples/typesetting.mom
+echo "testing mom(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -mom '"$doc"
+
+doc=$src/contrib/pdfmark/cover.ms
+echo "testing ms(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -ms '"$doc"
+
+doc=$src/contrib/pdfmark/pdfmark.ms
+echo "testing ms(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -ms '"$doc"
+
+doc=$src/doc/ms.ms
+echo "testing eqn(1)- and tbl(1)-using ms(7) document $doc" >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -e -t -ms '"$doc"
+
+doc=$src/doc/pic.ms
+echo "testing tbl(1)-, eqn(1)-, and pic(1)-using ms(7) document $doc" \
+ >&2
+"$grog" "$doc" | \
+ grep -Fqx 'groff -e -p -t -ms '"$doc"
+
+doc=$src/doc/webpage.ms
+echo "testing ms(7) document $doc" >&2
+# BUG: Should detect -mwww (and -mpspic?) too.
+"$grog" "$doc" | \
+ grep -Fqx 'groff -ms '"$doc"
+
+# Test manual specification of auxiliary macro packages.
+echo "testing ms(7) document $doc with '-m www' option" >&2
+"$grog" "$doc" -m www | \
+ grep -Fqx 'groff -ms -mwww '"$doc"
+
+echo "testing ms(7) document $doc with '-mwww' option" >&2
+"$grog" "$doc" -mwww | \
+ grep -Fqx 'groff -ms -mwww '"$doc"
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/src/utils/hpftodit/hpftodit.1.man b/src/utils/hpftodit/hpftodit.1.man
new file mode 100644
index 0000000..12e3af7
--- /dev/null
+++ b/src/utils/hpftodit/hpftodit.1.man
@@ -0,0 +1,476 @@
+.TH hpftodit @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+hpftodit \- create font description files for use with
+.I groff
+and
+.I grolj4
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1994-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_hpftodit_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY hpftodit
+.RB [ \-aqs ]
+.RB [ \-i\~\c
+.IR n ]
+.I tfm-file
+.I map-file
+.I font-description
+.YS
+.
+.
+.SY hpftodit
+.B \-d
+.I tfm-file
+.RI [ map-file ]
+.YS
+.
+.
+.SY hpftodit
+.B \-\-help
+.YS
+.
+.
+.SY hpftodit
+.B \-v
+.
+.SY hpftodit
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I hpftodit
+creates a font description file for use with a Hewlett-Packard
+LaserJet\~4-\%series
+(or newer)
+printer with the
+.MR grolj4 @MAN1EXT@
+output driver of
+.MR groff @MAN1EXT@ ,
+using data from an HP tagged font metric (TFM) file.
+.
+.I tfm-file
+is the name of the font's TFM file;
+Intellifont and TrueType TFM files are supported,
+but symbol set TFM files are not.
+.
+.I map-file
+is a file giving the
+.I groff
+special character identifiers for glyphs in the font;
+this file should consist of a sequence of lines of the form
+.RS
+.EX
+.IR "m u c1 c2 " "\&.\|.\|.\& [#" " comment" "]"
+.EE
+.RE
+where
+.I m
+is a decimal integer giving the glyph's MSL
+(Master Symbol List)
+number,
+.I u
+is a hexadecimal integer giving its Unicode character code,
+and
+.IR c1 ,
+.IR c2 ", .\|.\|."
+are its
+.I groff
+glyph names
+(see
+.MR groff_char @MAN7EXT@
+for a list).
+.
+The values can be separated by any number of spaces and/or tabs.
+.
+The Unicode value must use uppercase hexadecimal digits A\^\[en]\^F,
+and must lack a leading
+.RB \[lq] 0x \[rq],
+.RB \[lq] u \[rq],
+or
+.RB \[lq] U+ \[rq].
+.
+Unicode values corresponding to composite glyphs are decomposed;
+that is
+.RB \[lq] u00C0 \[rq]
+becomes
+.RB \[lq] u0041_0300 \[rq].
+.
+A glyph without a
+.I groff
+special character identifier may be named
+.BI u XXXX
+if the glyph corresponds to a Unicode value,
+or as an unnamed glyph
+.RB \[lq] \-\-\- \[rq].
+.
+If the given Unicode value is in the Private Use Area (PUA)
+(0xE000\^\[en]\^0xF8FF),
+the glyph is included as an unnamed glyph.
+.
+Refer to
+.MR groff_diff @MAN1EXT@
+for additional information about unnamed glyphs and how to access them.
+.
+.
+.P
+Blank lines and lines beginning with
+.RB \[lq] # \[rq]
+are ignored.
+.
+A
+.RB \[lq] # \[rq]
+following one or more
+.I groff
+names begins a comment.
+.
+Because
+.RB \[lq] # \[rq]
+is a valid
+.I groff
+name,
+it must appear first in a list of
+.I groff
+names if a comment is included,
+as in
+.
+.RS
+.EX
+3 0023 # # number sign
+.EE
+.RE
+.
+or
+.
+.RS
+.EX
+3 0023 # sh # number sign
+.EE
+.RE
+.
+whereas in
+.
+.RS
+.EX
+3 0023 sh # # number sign
+.EE
+.RE
+.
+the first
+.RB \[lq] # \[rq]
+is interpreted as the beginning of the comment.
+.
+.
+.P
+Output is written in
+.MR groff_font @MAN5EXT@
+format to
+.I font-description,
+a file named for the intended
+.I groff
+font name;
+if this operand is
+.RB \[lq] \- \[rq],
+the font description is written to the standard output stream.
+.
+.
+.LP
+If the
+.B \-i
+option is used,
+.I hpftodit
+automatically will generate an italic correction,
+a left italic correction,
+and a subscript correction for each glyph
+(the significance of these parameters is explained in
+.MR groff_font @MAN5EXT@ ).
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.B \-a
+Include glyphs in the TFM file that are not included in
+.IR map-file .
+.
+A glyph with corresponding Unicode value is given the name
+.RI u XXXX ;
+a glyph without a Unicode value is included as an unnamed glyph
+\[lq]\-\^\-\^\-\[rq].
+.
+A glyph with a Unicode value in the Private Use Area
+(0xE000\^\[en]\^0xF8FF)
+is also included as an unnamed glyph.
+.
+.
+.IP
+This option provides a simple means of adding Unicode-named and
+unnamed glyphs to a font without including them in the map file,
+but it affords little control over which glyphs are placed in a regular
+font and which are placed in a special font.
+.
+The presence or absence of the
+.B \-s
+option has some effect on which glyphs are included:
+without it,
+only the \[lq]text\[rq] symbol sets are searched for matching glyphs;
+with it,
+only the \[lq]mathematical\[rq] symbol sets are searched.
+.
+Nonetheless,
+restricting the symbol sets searched isn't very selective\[em]many
+glyphs are placed in both regular and special fonts.
+.
+Normally,
+.B \-a
+should be used only as a last resort.
+.
+.
+.TP
+.B \-d
+Dump information about the TFM file to the standard output stream;
+use this to ensure that a TFM file is a proper match for a font,
+and that its contents are suitable.
+.
+The information includes the values of important TFM tags and a listing
+(by MSL number for Intellifont TFM files or by Unicode value for
+TrueType TFM files)
+of the glyphs included in the TFM file.
+.
+The unit of measure \[lq]DU\[rq] for some tags indicates design units;
+there are 8782\~design units per em for Intellifont fonts,
+and 2048\~design units per em for TrueType fonts.
+.
+Note that the accessibility of a glyph depends on its inclusion in a
+symbol set;
+some TFM files list many glyphs but only a few symbol sets.
+.
+.
+.IP
+The glyph listing includes the glyph index within the TFM file,
+the MSL or Unicode value,
+and the symbol set and character code that will be used to print the
+glyph.
+.
+If
+.I map-file
+is given,
+.I groff
+names are given for matching glyphs.
+.
+If only the glyph index and MSL or Unicode value are given,
+the glyph does not appear in any supported symbol set and cannot be
+printed.
+.
+.
+.IP
+With the
+.B \-d
+option,
+.I map-file
+is optional,
+and
+.I output-font
+is ignored if given.
+.
+.
+.TP
+.BI \-i\~ n
+Generate an italic correction for each glyph so that its width plus its
+italic correction is equal to
+.I n
+thousandths of an em plus the amount by which the right edge of the
+glyphs's bounding box is to the right of its origin.
+.
+If a negative italic correction would result,
+use a zero italic correction instead.
+.
+.
+.IP
+Also generate a subscript correction equal to the product of the tangent
+of the slant of the font and four fifths of the x-height of the font.
+.
+If a subscript correction greater than the italic correction would
+result,
+use a subscript correction equal to the italic correction instead.
+.
+.
+.IP
+Also generate a left italic correction for each glyph equal to
+.I n
+thousandths of an em plus the amount by which the left edge of the
+glyphs's bounding box is to the left of its origin.
+.
+The left italic correction may be negative.
+.
+.
+.IP
+This option normally is needed only with italic or oblique fonts;
+a value of 50
+(0.05\~em)
+usually is a reasonable choice.
+.
+.
+.TP
+.B \-q
+Suppress warnings about glyphs in the map file that were not found in
+the TFM file.
+.
+Warnings never are given for unnamed glyphs or by glyphs named by their
+Unicode values.
+.
+This option is useful when sending the output of
+.I hpftodit
+to the standard output stream.
+.
+.
+.TP
+.B \-s
+Add the
+.B special
+directive to the font description file,
+affecting the order in which HP symbol sets are searched for each glyph.
+.
+Without this option,
+the \[lq]text\[rq] sets are searched before the \[lq]mathematical\[rq]
+symbol sets.
+.
+With it,
+the search order is reversed.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.I @FONTDIR@/\:\%devlj4/\:DESC
+describes the
+.B lj4
+output device.
+.
+.
+.TP
+.IR @FONTDIR@/\:\%devlj4/ F
+describes the font known
+.RI as\~ F
+on device
+.BR lj4 .
+.
+.
+.TP
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:\%Makefile
+is a
+.MR make 1
+script that uses
+.MR hpftodit @MAN1EXT@
+to prepare the
+.I groff
+font description files above from HP TFM data;
+in can be used to regenerate them in the event the TFM files are
+updated.
+.
+.
+.TP
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:\%special\:.awk
+is an
+.MR awk 1
+script that corrects the Intellifont-based height metrics for several
+glyphs in the
+.B S
+(special) font for TrueType CG Times used in the HP LaserJet\~4000 and
+later.
+.
+.
+.TP
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:\%special\:.map
+.TQ
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:\%symbol\:.map
+.TQ
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:text\:.map
+.TQ
+.I @FONTDIR@/\:\%devlj4/\:\%generate/\:\%wingdings.map
+map MSL indices and HP Unicode PUA assignments to
+.I groff
+special character identifiers.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR groff @MAN1EXT@ ,
+.MR groff_diff @MAN1EXT@ ,
+.MR grolj4 @MAN1EXT@ ,
+.MR groff_font @MAN5EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_hpftodit_1_man_C]
+.do rr *groff_hpftodit_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/hpftodit/hpftodit.am b/src/utils/hpftodit/hpftodit.am
new file mode 100644
index 0000000..e31e8f5
--- /dev/null
+++ b/src/utils/hpftodit/hpftodit.am
@@ -0,0 +1,34 @@
+# Automake rules for 'src utils hpftodit'
+#
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# 'groff' is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# 'groff' is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+#
+########################################################################
+
+bin_PROGRAMS += hpftodit
+man1_MANS += src/utils/hpftodit/hpftodit.1
+EXTRA_DIST += src/utils/hpftodit/hpftodit.1.man
+hpftodit_LDADD = libgroff.a $(LIBM) lib/libgnu.a
+hpftodit_SOURCES = \
+ src/utils/hpftodit/hpftodit.cpp \
+ src/utils/hpftodit/hpuni.cpp
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/hpftodit/hpftodit.cpp b/src/utils/hpftodit/hpftodit.cpp
new file mode 100644
index 0000000..4982e19
--- /dev/null
+++ b/src/utils/hpftodit/hpftodit.cpp
@@ -0,0 +1,1465 @@
+/* Copyright (C) 1994-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/*
+TODO
+devise new names for useful characters
+option to specify symbol sets to look in
+put filename in error messages (or fix lib)
+*/
+
+#include "lib.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "nonposix.h"
+#include "unicode.h"
+
+extern "C" const char *Version_string;
+extern const char *hp_msl_to_unicode_code(const char *);
+
+#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
+#define equal(a, b) (strcmp(a, b) == 0)
+// only valid if is_uname(c) has returned true
+#define is_decomposed(c) strchr(c, '_')
+
+#define NO 0
+#define YES 1
+
+#define MSL 0
+#define SYMSET 1
+#define UNICODE 2
+
+#define UNNAMED "---"
+
+static double multiplier = 3.0; // make Agfa-based unitwidth an integer
+
+inline
+int scale(int n)
+{
+ return int(n * multiplier + 0.5);
+}
+
+// tags in TFM file
+
+enum tag_type {
+ min_tag = 400,
+ type_tag = 400,
+ copyright_tag = 401,
+ comment_tag = 402,
+ charcode_tag = 403, // MSL for Intellifont, Unicode for TrueType
+ symbol_set_tag = 404,
+ unique_identifier_tag = 405,
+ inches_per_point_tag = 406,
+ nominal_point_size_tag = 407,
+ design_units_per_em_tag = 408,
+ posture_tag = 409,
+ type_structure_tag = 410,
+ stroke_weight_tag = 411,
+ spacing_tag = 412,
+ slant_tag = 413,
+ appearance_width_tag = 414,
+ serif_style_tag = 415,
+ font_name_tag = 417,
+ typeface_source_tag = 418,
+ average_width_tag = 419,
+ max_width_tag = 420,
+ word_spacing_tag = 421,
+ recommended_line_spacing_tag = 422,
+ cap_height_tag = 423,
+ x_height_tag = 424,
+ max_ascent_tag = 425,
+ max_descent_tag = 426,
+ lower_ascent_tag = 427,
+ lower_descent_tag = 428,
+ underscore_depth_tag = 429,
+ underscore_thickness_tag = 430,
+ uppercase_accent_height_tag = 431,
+ lowercase_accent_height_tag = 432,
+ width_tag = 433,
+ vertical_escapement_tag = 434,
+ left_extent_tag = 435,
+ right_extent_tag = 436,
+ ascent_tag = 437,
+ descent_tag = 438,
+ pair_kern_tag = 439,
+ sector_kern_tag = 440,
+ track_kern_tag = 441,
+ typeface_tag = 442,
+ panose_tag = 443,
+ max_tag = 443
+};
+
+const char *tag_name[] = {
+ "Symbol Set",
+ "Font Type" // MSL for Intellifont, Unicode for TrueType
+};
+
+// types in TFM file
+enum {
+ BYTE_TYPE = 1,
+ ASCII_TYPE = 2, // NUL-terminated string
+ USHORT_TYPE = 3,
+ LONG_TYPE = 4, // unused
+ RATIONAL_TYPE = 5, // 8-byte numerator + 8-byte denominator
+ SIGNED_BYTE_TYPE = 16, // unused
+ SIGNED_SHORT_TYPE = 17,
+ SIGNED_LONG_TYPE = 18 // unused
+};
+
+typedef unsigned char byte;
+typedef unsigned short uint16;
+typedef short int16;
+typedef unsigned int uint32;
+
+class File {
+public:
+ File(const char *);
+ void skip(int n);
+ byte get_byte();
+ uint16 get_uint16();
+ uint32 get_uint32();
+ uint32 get_uint32(char *orig);
+ void seek(uint32 n);
+private:
+ unsigned char *buf_;
+ const unsigned char *ptr_;
+ const unsigned char *end_;
+};
+
+struct entry {
+ char present;
+ uint16 type;
+ uint32 count;
+ uint32 value;
+ char orig_value[4];
+ entry() : present(0) { }
+};
+
+struct char_info {
+ uint16 charcode;
+ uint16 width;
+ int16 ascent;
+ int16 descent;
+ int16 left_extent;
+ uint16 right_extent;
+ uint16 symbol_set;
+ unsigned char code;
+};
+
+const uint16 NO_GLYPH = 0xffff;
+const uint16 NO_SYMBOL_SET = 0;
+
+struct name_list {
+ char *name;
+ name_list *next;
+ name_list(const char *s, name_list *p) : name(strsave(s)), next(p) { }
+ ~name_list() { delete[] name; }
+};
+
+struct symbol_set {
+ uint16 select;
+ uint16 index[256];
+};
+
+#define SYMBOL_SET(n, c) ((n) * 32 + ((c) - 64))
+
+uint16 text_symbol_sets[] = {
+ SYMBOL_SET(19, 'U'), // Windows Latin 1 ("ANSI", code page 1252)
+ SYMBOL_SET(9, 'E'), // Windows Latin 2, Code Page 1250
+ SYMBOL_SET(5, 'T'), // Code Page 1254
+ SYMBOL_SET(7, 'J'), // Desktop
+ SYMBOL_SET(6, 'J'), // Microsoft Publishing
+ SYMBOL_SET(0, 'N'), // Latin 1 (subset of 19U,
+ // so we should never get here)
+ SYMBOL_SET(2, 'N'), // Latin 2 (subset of 9E,
+ // so we should never get here)
+ SYMBOL_SET(8, 'U'), // HP Roman 8
+ SYMBOL_SET(10, 'J'), // PS Standard
+ SYMBOL_SET(9, 'U'), // Windows 3.0 "ANSI"
+ SYMBOL_SET(1, 'U'), // U.S. Legal
+
+ SYMBOL_SET(12, 'J'), // MC Text
+ SYMBOL_SET(10, 'U'), // PC Code Page 437
+ SYMBOL_SET(11, 'U'), // PC Code Page 437N
+ SYMBOL_SET(17, 'U'), // PC Code Page 852
+ SYMBOL_SET(12, 'U'), // PC Code Page 850
+ SYMBOL_SET(9, 'T'), // PC Code Page 437T
+ 0
+};
+
+uint16 special_symbol_sets[] = {
+ SYMBOL_SET(8, 'M'), // Math 8
+ SYMBOL_SET(5, 'M'), // PS Math
+ SYMBOL_SET(15, 'U'), // Pi font
+ SYMBOL_SET(13, 'J'), // Ventura International
+ SYMBOL_SET(19, 'M'), // Symbol font
+ SYMBOL_SET(579, 'L'), // Wingdings
+ 0
+};
+
+entry tags[max_tag + 1 - min_tag];
+
+char_info *char_table;
+uint32 nchars = 0;
+
+unsigned int charcode_name_table_size = 0;
+name_list **charcode_name_table = NULL;
+
+symbol_set *symbol_set_table;
+unsigned int n_symbol_sets;
+
+static int debug_flag = NO;
+static int special_flag = NO; // not a special font
+static int italic_flag = NO; // don't add italic correction
+static int italic_sep;
+static int all_flag = NO; // don't include glyphs not in mapfile
+static int quiet_flag = NO; // don't suppress warnings about symbols not found
+
+static char *hp_msl_to_ucode_name(int);
+static char *unicode_to_ucode_name(int);
+static int is_uname(char *);
+static char *show_symset(unsigned int);
+static void usage(FILE *);
+static void usage();
+static const char *xbasename(const char *);
+static void read_tags(File &);
+static int check_type();
+static void check_units(File &, const int, double *, double *);
+static int read_map(const char *, const int);
+static void require_tag(tag_type);
+static void dump_ascii(File &, tag_type);
+static void dump_tags(File &);
+static void dump_symbol_sets(File &);
+static void dump_symbols(int);
+static void output_font_name(File &);
+static void output_spacewidth();
+static void output_pclweight();
+static void output_pclproportional();
+static void read_and_output_pcltypeface(File &);
+static void output_pclstyle();
+static void output_slant();
+static void output_ligatures();
+static void read_symbol_sets(File &);
+static void read_and_output_kernpairs(File &);
+static void output_charset(const int);
+static void read_char_table(File &);
+
+inline
+entry &tag_info(tag_type t)
+{
+ return tags[t - min_tag];
+}
+
+int
+main(int argc, char **argv)
+{
+ program_name = argv[0];
+
+ int opt;
+ int res = 1200; // PCL unit of measure for cursor moves
+ int scalesize = 4; // LaserJet 4 only allows 1/4 point increments
+ int unitwidth = 6350;
+ double ppi; // points per inch
+ double upem; // design units per em
+
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "adsqvi:", long_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'a':
+ all_flag = YES;
+ break;
+ case 'd':
+ debug_flag = YES;
+ break;
+ case 's':
+ special_flag = YES;
+ break;
+ case 'i':
+ italic_flag = YES;
+ italic_sep = atoi(optarg); // design units
+ break;
+ case 'q':
+ quiet_flag = YES; // suppress warnings about symbols not found
+ break;
+ case 'v':
+ printf("GNU hpftodit (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ if (debug_flag && argc - optind < 1)
+ usage();
+ else if (!debug_flag && argc - optind != 3)
+ usage();
+ File f(argv[optind]);
+ read_tags(f);
+ int tfm_type = check_type();
+ if (debug_flag)
+ dump_tags(f);
+ if (!debug_flag && !read_map(argv[optind + 1], tfm_type))
+ exit(1);
+ else if (debug_flag && argc - optind > 1)
+ read_map(argv[optind + 1], tfm_type);
+ current_filename = NULL;
+ current_lineno = -1; // no line numbers
+ if (!debug_flag && !equal(argv[optind + 2], "-"))
+ if (freopen(argv[optind + 2], "w", stdout) == NULL)
+ fatal("cannot open '%1': %2", argv[optind + 2], strerror(errno));
+ current_filename = argv[optind];
+
+ check_units(f, tfm_type, &ppi, &upem);
+ if (tfm_type == UNICODE) // don't calculate for Intellifont TFMs
+ multiplier = double(res) / upem / ppi * unitwidth / scalesize;
+ if (italic_flag)
+ // convert from thousandths of an em to design units
+ italic_sep = int(italic_sep * upem / 1000 + 0.5);
+
+ read_char_table(f);
+ if (nchars == 0)
+ fatal("no characters");
+
+ if (!debug_flag) {
+ output_font_name(f);
+ printf("name %s\n", xbasename(argv[optind + 2]));
+ if (special_flag)
+ printf("special\n");
+ output_spacewidth();
+ output_slant();
+ read_and_output_pcltypeface(f);
+ output_pclproportional();
+ output_pclweight();
+ output_pclstyle();
+ }
+ read_symbol_sets(f);
+ if (debug_flag)
+ dump_symbols(tfm_type);
+ else {
+ output_ligatures();
+ read_and_output_kernpairs(f);
+ output_charset(tfm_type);
+ }
+ return 0;
+}
+
+static void
+usage(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-aqs] [-i n] tfm-file map-file output-font\n"
+"usage: %s -d tfm-file [map-file]\n"
+"usage: %s {-v | --version}\n"
+"usage: %s --help\n",
+ program_name, program_name, program_name, program_name);
+}
+
+static void
+usage()
+{
+ usage(stderr);
+ exit(1);
+}
+
+File::File(const char *s)
+{
+ // We need to read the file in binary mode because hpftodit relies
+ // on byte counts.
+ int fd = open(s, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ fatal("cannot open '%1': %2", s, strerror(errno));
+ current_filename = s;
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ fatal("cannot stat: %1", strerror(errno));
+ if (!S_ISREG(sb.st_mode))
+ fatal("not a regular file");
+ buf_ = new unsigned char[sb.st_size];
+ long nread = read(fd, buf_, sb.st_size);
+ if (nread < 0)
+ fatal("read error: %1", strerror(errno));
+ if (nread != sb.st_size)
+ fatal("read unexpected number of bytes");
+ ptr_ = buf_;
+ end_ = buf_ + sb.st_size;
+}
+
+void
+File::skip(int n)
+{
+ if (end_ - ptr_ < n)
+ fatal("unexpected end of file");
+ ptr_ += n;
+}
+
+void
+File::seek(uint32 n)
+{
+ if (uint32(end_ - buf_) < n)
+ fatal("unexpected end of file");
+ ptr_ = buf_ + n;
+}
+
+byte
+File::get_byte()
+{
+ if (ptr_ >= end_)
+ fatal("unexpected end of file");
+ return *ptr_++;
+}
+
+uint16
+File::get_uint16()
+{
+ if (end_ - ptr_ < 2)
+ fatal("unexpected end of file");
+ uint16 n = *ptr_++;
+ return n + (*ptr_++ << 8);
+}
+
+uint32
+File::get_uint32()
+{
+ if (end_ - ptr_ < 4)
+ fatal("unexpected end of file");
+ uint32 n = *ptr_++;
+ for (int i = 0; i < 3; i++)
+ n += *ptr_++ << (i + 1)*8;
+ return n;
+}
+
+uint32
+File::get_uint32(char *orig)
+{
+ if (end_ - ptr_ < 4)
+ fatal("unexpected end of file");
+ unsigned char v = *ptr_++;
+ uint32 n = v;
+ orig[0] = v;
+ for (int i = 1; i < 4; i++) {
+ v = *ptr_++;
+ orig[i] = v;
+ n += v << i*8;
+ }
+ return n;
+}
+
+static void
+read_tags(File &f)
+{
+ if (f.get_byte() != 'I' || f.get_byte() != 'I')
+ fatal("not an Intel format TFM file");
+ f.skip(6);
+ uint16 ntags = f.get_uint16();
+ entry dummy;
+ for (uint16 i = 0; i < ntags; i++) {
+ uint16 tag = f.get_uint16();
+ entry *p;
+ if (min_tag <= tag && tag <= max_tag)
+ p = tags + (tag - min_tag);
+ else
+ p = &dummy;
+ p->present = 1;
+ p->type = f.get_uint16();
+ p->count = f.get_uint32();
+ p->value = f.get_uint32(p->orig_value);
+ }
+}
+
+static int
+check_type()
+{
+ require_tag(type_tag);
+ int tfm_type = tag_info(type_tag).value;
+ switch (tfm_type) {
+ case MSL:
+ case UNICODE:
+ break;
+ case SYMSET:
+ fatal("cannot handle Symbol Set TFM files");
+ break;
+ default:
+ fatal("unknown type tag %1", tfm_type);
+ }
+ return tfm_type;
+}
+
+static void
+check_units(File &f, const int tfm_type, double *ppi, double *upem)
+{
+ require_tag(design_units_per_em_tag);
+ f.seek(tag_info(design_units_per_em_tag).value);
+ uint32 num = f.get_uint32();
+ uint32 den = f.get_uint32();
+ if (tfm_type == MSL && (num != 8782 || den != 1))
+ fatal("design units per em != 8782/1");
+ *upem = double(num) / den;
+ require_tag(inches_per_point_tag);
+ f.seek(tag_info(inches_per_point_tag).value);
+ num = f.get_uint32();
+ den = f.get_uint32();
+ if (tfm_type == MSL && (num != 100 || den != 7231))
+ fatal("inches per point not 100/7231");
+ *ppi = double(den) / num;
+}
+
+static void
+require_tag(tag_type t)
+{
+ if (!tag_info(t).present)
+ fatal("tag %1 missing", int(t));
+}
+
+// put a human-readable font name in the file
+static void
+output_font_name(File &f)
+{
+ char *p;
+
+ if (!tag_info(font_name_tag).present)
+ return;
+ int count = tag_info(font_name_tag).count;
+ char *font_name = new char[count];
+
+ if (count > 4) { // value is a file offset to the string
+ f.seek(tag_info(font_name_tag).value);
+ int n = count;
+ p = font_name;
+ while (--n)
+ *p++ = f.get_byte();
+ }
+ else // orig_value contains the string
+ sprintf(font_name, "%.*s",
+ count, tag_info(font_name_tag).orig_value);
+
+ // remove any trailing space
+ p = font_name + count - 1;
+ while (csspace(*--p))
+ ;
+ *(p + 1) = '\0';
+ printf("# %s\n", font_name);
+ delete[] font_name;
+}
+
+static void
+output_spacewidth()
+{
+ require_tag(word_spacing_tag);
+ printf("spacewidth %d\n", scale(tag_info(word_spacing_tag).value));
+}
+
+static void
+read_symbol_sets(File &f)
+{
+ uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
+ uint16 *symbol_set_selectors;
+ n_symbol_sets = symbol_set_dir_length/14;
+ symbol_set_table = new symbol_set[n_symbol_sets];
+ unsigned int i;
+
+ for (i = 0; i < nchars; i++)
+ char_table[i].symbol_set = NO_SYMBOL_SET;
+
+ for (i = 0; i < n_symbol_sets; i++) {
+ f.seek(tag_info(symbol_set_tag).value + i*14);
+ (void)f.get_uint32(); // offset to symbol set name
+ uint32 off1 = f.get_uint32(); // offset to selection string
+ uint32 off2 = f.get_uint32(); // offset to symbol set index array
+
+ f.seek(off1);
+ uint16 kind = 0; // HP-GL "Kind 1" symbol set value
+ unsigned int j;
+ for (j = 0; j < off2 - off1; j++) {
+ unsigned char c = f.get_byte();
+ if ('0' <= c && c <= '9') // value
+ kind = kind*10 + (c - '0');
+ else if ('A' <= c && c <= 'Z') // terminator
+ kind = kind*32 + (c - 64);
+ }
+ symbol_set_table[i].select = kind;
+ for (j = 0; j < 256; j++)
+ symbol_set_table[i].index[j] = f.get_uint16();
+ }
+
+ symbol_set_selectors = (special_flag ? special_symbol_sets
+ : text_symbol_sets);
+ for (i = 0; symbol_set_selectors[i] != 0; i++) {
+ unsigned int j;
+ for (j = 0; j < n_symbol_sets; j++)
+ if (symbol_set_table[j].select == symbol_set_selectors[i])
+ break;
+ if (j < n_symbol_sets) {
+ for (int k = 0; k < 256; k++) {
+ uint16 idx = symbol_set_table[j].index[k];
+ if (idx != NO_GLYPH
+ && char_table[idx].symbol_set == NO_SYMBOL_SET) {
+ char_table[idx].symbol_set = symbol_set_table[j].select;
+ char_table[idx].code = k;
+ }
+ }
+ }
+ }
+
+ if (all_flag)
+ return;
+
+ symbol_set_selectors = (special_flag ? text_symbol_sets
+ : special_symbol_sets);
+ for (i = 0; symbol_set_selectors[i] != 0; i++) {
+ unsigned int j;
+ for (j = 0; j < n_symbol_sets; j++)
+ if (symbol_set_table[j].select == symbol_set_selectors[i])
+ break;
+ if (j < n_symbol_sets) {
+ for (int k = 0; k < 256; k++) {
+ uint16 idx = symbol_set_table[j].index[k];
+ if (idx != NO_GLYPH
+ && char_table[idx].symbol_set == NO_SYMBOL_SET) {
+ char_table[idx].symbol_set = symbol_set_table[j].select;
+ char_table[idx].code = k;
+ }
+ }
+ }
+ }
+ return;
+}
+
+static void
+read_char_table(File &f)
+{
+ require_tag(charcode_tag);
+ nchars = tag_info(charcode_tag).count;
+ char_table = new char_info[nchars];
+
+ f.seek(tag_info(charcode_tag).value);
+ uint32 i;
+ for (i = 0; i < nchars; i++)
+ char_table[i].charcode = f.get_uint16();
+
+ require_tag(width_tag);
+ f.seek(tag_info(width_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].width = f.get_uint16();
+
+ require_tag(ascent_tag);
+ f.seek(tag_info(ascent_tag).value);
+ for (i = 0; i < nchars; i++) {
+ char_table[i].ascent = f.get_uint16();
+ if (char_table[i].ascent < 0)
+ char_table[i].ascent = 0;
+ }
+
+ require_tag(descent_tag);
+ f.seek(tag_info(descent_tag).value);
+ for (i = 0; i < nchars; i++) {
+ char_table[i].descent = f.get_uint16();
+ if (char_table[i].descent > 0)
+ char_table[i].descent = 0;
+ }
+
+ require_tag(left_extent_tag);
+ f.seek(tag_info(left_extent_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].left_extent = int16(f.get_uint16());
+
+ require_tag(right_extent_tag);
+ f.seek(tag_info(right_extent_tag).value);
+ for (i = 0; i < nchars; i++)
+ char_table[i].right_extent = f.get_uint16();
+}
+
+static void
+output_pclweight()
+{
+ require_tag(stroke_weight_tag);
+ int stroke_weight = tag_info(stroke_weight_tag).value;
+ int pcl_stroke_weight;
+ if (stroke_weight < 128)
+ pcl_stroke_weight = -3;
+ else if (stroke_weight == 128)
+ pcl_stroke_weight = 0;
+ else if (stroke_weight <= 145)
+ pcl_stroke_weight = 1;
+ else if (stroke_weight <= 179)
+ pcl_stroke_weight = 3;
+ else
+ pcl_stroke_weight = 4;
+ printf("pclweight %d\n", pcl_stroke_weight);
+}
+
+static void
+output_pclproportional()
+{
+ require_tag(spacing_tag);
+ printf("pclproportional %d\n", tag_info(spacing_tag).value == 0);
+}
+
+static void
+read_and_output_pcltypeface(File &f)
+{
+ printf("pcltypeface ");
+ require_tag(typeface_tag);
+ if (tag_info(typeface_tag).count > 4) {
+ f.seek(tag_info(typeface_tag).value);
+ for (uint32 i = 0; i < tag_info(typeface_tag).count; i++) {
+ unsigned char c = f.get_byte();
+ if (c == '\0')
+ break;
+ putchar(c);
+ }
+ }
+ else
+ printf("%.4s", tag_info(typeface_tag).orig_value);
+ printf("\n");
+}
+
+static void
+output_pclstyle()
+{
+ unsigned pcl_style = 0;
+ // older tfms don't have the posture tag
+ if (tag_info(posture_tag).present) {
+ if (tag_info(posture_tag).value)
+ pcl_style |= 1;
+ }
+ else {
+ require_tag(slant_tag);
+ if (tag_info(slant_tag).value != 0)
+ pcl_style |= 1;
+ }
+ require_tag(appearance_width_tag);
+ if (tag_info(appearance_width_tag).value < 100) // guess
+ pcl_style |= 4;
+ printf("pclstyle %d\n", pcl_style);
+}
+
+static void
+output_slant()
+{
+ require_tag(slant_tag);
+ int slant = int16(tag_info(slant_tag).value);
+ if (slant != 0)
+ printf("slant %f\n", slant/100.0);
+}
+
+static void
+output_ligatures()
+{
+ // don't use ligatures for fixed space font
+ require_tag(spacing_tag);
+ if (tag_info(spacing_tag).value != 0)
+ return;
+ static const char *ligature_names[] = {
+ "fi", "fl", "ff", "ffi", "ffl"
+ };
+
+ static const char *ligature_chars[] = {
+ "fi", "fl", "ff", "Fi", "Fl"
+ };
+
+ unsigned ligature_mask = 0;
+ unsigned int i;
+ for (i = 0; i < nchars; i++) {
+ uint16 charcode = char_table[i].charcode;
+ if (charcode < charcode_name_table_size
+ && char_table[i].symbol_set != NO_SYMBOL_SET) {
+ for (name_list *p = charcode_name_table[charcode]; p; p = p->next)
+ for (unsigned int j = 0; j < SIZEOF(ligature_chars); j++)
+ if (strcmp(p->name, ligature_chars[j]) == 0) {
+ ligature_mask |= 1 << j;
+ break;
+ }
+ }
+ }
+ if (ligature_mask) {
+ printf("ligatures");
+ for (i = 0; i < SIZEOF(ligature_names); i++)
+ if (ligature_mask & (1 << i))
+ printf(" %s", ligature_names[i]);
+ printf(" 0\n");
+ }
+}
+
+static void
+read_and_output_kernpairs(File &f)
+{
+ if (tag_info(pair_kern_tag).present) {
+ printf("kernpairs\n");
+ f.seek(tag_info(pair_kern_tag).value);
+ uint16 n_pairs = f.get_uint16();
+ for (int i = 0; i < n_pairs; i++) {
+ uint16 i1 = f.get_uint16();
+ uint16 i2 = f.get_uint16();
+ int16 val = int16(f.get_uint16());
+ if (char_table[i1].symbol_set != NO_SYMBOL_SET
+ && char_table[i2].symbol_set != NO_SYMBOL_SET
+ && char_table[i1].charcode < charcode_name_table_size
+ && char_table[i2].charcode < charcode_name_table_size) {
+ for (name_list *p = charcode_name_table[char_table[i1].charcode];
+ p;
+ p = p->next)
+ for (name_list *q = charcode_name_table[char_table[i2].charcode];
+ q;
+ q = q->next)
+ if (!equal(p->name, UNNAMED) && !equal(q->name, UNNAMED))
+ printf("%s %s %d\n", p->name, q->name, scale(val));
+ }
+ }
+ }
+}
+
+static void
+output_charset(const int tfm_type)
+{
+ require_tag(slant_tag);
+ double slant_angle = int16(tag_info(slant_tag).value)*PI/18000.0;
+ double slant = sin(slant_angle)/cos(slant_angle);
+
+ if (italic_flag)
+ require_tag(x_height_tag);
+ require_tag(lower_ascent_tag);
+ require_tag(lower_descent_tag);
+
+ printf("charset\n");
+ unsigned int i;
+ for (i = 0; i < nchars; i++) {
+ uint16 charcode = char_table[i].charcode;
+
+ // the glyph is bound to one of the searched symbol sets
+ if (char_table[i].symbol_set != NO_SYMBOL_SET) {
+ // the character was in the map file
+ if (charcode < charcode_name_table_size && charcode_name_table[charcode])
+ printf("%s", charcode_name_table[charcode]->name);
+ else if (!all_flag)
+ continue;
+ else if (tfm_type == MSL)
+ printf("%s", hp_msl_to_ucode_name(charcode));
+ else
+ printf("%s", unicode_to_ucode_name(charcode));
+
+ printf("\t%d,%d",
+ scale(char_table[i].width), scale(char_table[i].ascent));
+
+ int depth = scale(-char_table[i].descent);
+ if (depth < 0)
+ depth = 0;
+ int italic_correction = 0;
+ int left_italic_correction = 0;
+ int subscript_correction = 0;
+
+ if (italic_flag) {
+ italic_correction = scale(char_table[i].right_extent
+ - char_table[i].width
+ + italic_sep);
+ if (italic_correction < 0)
+ italic_correction = 0;
+ subscript_correction = int((tag_info(x_height_tag).value
+ * slant * .8) + .5);
+ if (subscript_correction > italic_correction)
+ subscript_correction = italic_correction;
+ left_italic_correction = scale(italic_sep
+ - char_table[i].left_extent);
+ }
+
+ if (subscript_correction != 0)
+ printf(",%d,%d,%d,%d",
+ depth, italic_correction, left_italic_correction,
+ subscript_correction);
+ else if (left_italic_correction != 0)
+ printf(",%d,%d,%d", depth, italic_correction, left_italic_correction);
+ else if (italic_correction != 0)
+ printf(",%d,%d", depth, italic_correction);
+ else if (depth != 0)
+ printf(",%d", depth);
+ // This is fairly arbitrary. Fortunately it doesn't much matter.
+ unsigned type = 0;
+ if (char_table[i].ascent > int16(tag_info(lower_ascent_tag).value)*9/10)
+ type |= 2;
+ if (char_table[i].descent < int16(tag_info(lower_descent_tag).value)*9/10)
+ type |= 1;
+ printf("\t%d\t%d", type,
+ char_table[i].symbol_set*256 + char_table[i].code);
+
+ if (tfm_type == UNICODE) {
+ if (charcode >= 0xE000 && charcode <= 0xF8FF)
+ printf("\t-- HP PUA U+%04X", charcode);
+ else
+ printf("\t-- U+%04X", charcode);
+ }
+ else
+ printf("\t-- MSL %4d", charcode);
+ printf(" (%3s %3d)\n",
+ show_symset(char_table[i].symbol_set), char_table[i].code);
+
+ if (charcode < charcode_name_table_size
+ && charcode_name_table[charcode])
+ for (name_list *p = charcode_name_table[charcode]->next;
+ p; p = p->next)
+ printf("%s\t\"\n", p->name);
+ }
+ // warnings about characters in mapfile not found in TFM
+ else if (charcode < charcode_name_table_size
+ && charcode_name_table[charcode]) {
+ char *name = charcode_name_table[charcode]->name;
+ // don't warn about Unicode or unnamed glyphs
+ // that aren't in the TFM file
+ if (tfm_type == UNICODE && !quiet_flag && !equal(name, UNNAMED)
+ && !is_uname(name)) {
+ fprintf(stderr, "%s: warning: symbol U+%04X (%s",
+ program_name, charcode, name);
+ for (name_list *p = charcode_name_table[charcode]->next;
+ p; p = p->next)
+ fprintf(stderr, ", %s", p->name);
+ fprintf(stderr, ") not in any searched symbol set\n");
+ }
+ else if (!quiet_flag && !equal(name, UNNAMED) && !is_uname(name)) {
+ fprintf(stderr, "%s: warning: symbol MSL %d (%s",
+ program_name, charcode, name);
+ for (name_list *p = charcode_name_table[charcode]->next;
+ p; p = p->next)
+ fprintf(stderr, ", %s", p->name);
+ fprintf(stderr, ") not in any searched symbol set\n");
+ }
+ }
+ }
+}
+
+#define em_fract(a) (upem >= 0 ? double(a)/upem : 0)
+
+static void
+dump_tags(File &f)
+{
+ double upem = -1.0;
+
+ printf("TFM tags\n"
+ "\n"
+ "tag# type count value\n"
+ "---------------------\n");
+
+ for (int i = min_tag; i <= max_tag; i++) {
+ enum tag_type t = tag_type(i);
+ if (tag_info(t).present) {
+ printf("%4d %4d %5d", i, tag_info(t).type, tag_info(t).count);
+ switch (tag_info(t).type) {
+ case BYTE_TYPE:
+ case USHORT_TYPE:
+ printf(" %5u", tag_info(t).value);
+ switch (i) {
+ case type_tag:
+ printf(" Font Type ");
+ switch (tag_info(t).value) {
+ case MSL:
+ case SYMSET:
+ printf("(Intellifont)");
+ break;
+ case UNICODE:
+ printf("(TrueType)");
+ }
+ break;
+ case charcode_tag:
+ printf(" Number of Symbols (%u)", tag_info(t).count);
+ break;
+ case symbol_set_tag:
+ printf(" Symbol Sets (%u): ",
+ tag_info(symbol_set_tag).count / 14);
+ dump_symbol_sets(f);
+ break;
+ case type_structure_tag:
+ printf(" Type Structure (%u)", tag_info(t).value);
+ break;
+ case stroke_weight_tag:
+ printf(" Stroke Weight (%u)", tag_info(t).value);
+ break;
+ case spacing_tag:
+ printf(" Spacing ");
+ switch (tag_info(t).value) {
+ case 0:
+ printf("(Proportional)");
+ break;
+ case 1:
+ printf("(Fixed Pitch: %u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ }
+ break;
+ case appearance_width_tag:
+ printf(" Appearance Width (%u)", tag_info(t).value);
+ break;
+ case serif_style_tag:
+ printf(" Serif Style (%u)", tag_info(t).value);
+ break;
+ case posture_tag:
+ printf(" Posture (%s)", tag_info(t).value == 0
+ ? "Upright"
+ : tag_info(t).value == 1
+ ? "Italic"
+ : "Alternate Italic");
+ break;
+ case max_width_tag:
+ printf(" Maximum Width (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case word_spacing_tag:
+ printf(" Interword Spacing (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case recommended_line_spacing_tag:
+ printf(" Recommended Line Spacing (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case x_height_tag:
+ printf(" x-Height (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case cap_height_tag:
+ printf(" Cap Height (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case max_ascent_tag:
+ printf(" Maximum Ascent (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case lower_ascent_tag:
+ printf(" Lowercase Ascent (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case underscore_thickness_tag:
+ printf(" Underscore Thickness (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case uppercase_accent_height_tag:
+ printf(" Uppercase Accent Height (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case lowercase_accent_height_tag:
+ printf(" Lowercase Accent Height (%u DU: %.2f em)", tag_info(t).value,
+ em_fract(tag_info(t).value));
+ break;
+ case width_tag:
+ printf(" Horizontal Escapement array");
+ break;
+ case vertical_escapement_tag:
+ printf(" Vertical Escapement array");
+ break;
+ case right_extent_tag:
+ printf(" Right Extent array");
+ break;
+ case ascent_tag:
+ printf(" Character Ascent array");
+ break;
+ case pair_kern_tag:
+ f.seek(tag_info(t).value);
+ printf(" Kern Pairs (%u)", f.get_uint16());
+ break;
+ case panose_tag:
+ printf(" PANOSE Classification array");
+ break;
+ }
+ break;
+ case SIGNED_SHORT_TYPE:
+ printf(" %5d", int16(tag_info(t).value));
+ switch (i) {
+ case slant_tag:
+ printf(" Slant (%.2f degrees)", double(tag_info(t).value) / 100);
+ break;
+ case max_descent_tag:
+ printf(" Maximum Descent (%d DU: %.2f em)", int16(tag_info(t).value),
+ em_fract(int16(tag_info(t).value)));
+ break;
+ case lower_descent_tag:
+ printf(" Lowercase Descent (%d DU: %.2f em)", int16(tag_info(t).value),
+ em_fract(int16(tag_info(t).value)));
+ break;
+ case underscore_depth_tag:
+ printf(" Underscore Depth (%d DU: %.2f em)", int16(tag_info(t).value),
+ em_fract(int16(tag_info(t).value)));
+ break;
+ case left_extent_tag:
+ printf(" Left Extent array");
+ break;
+ // The type of this tag has changed from SHORT to SIGNED SHORT
+ // in TFM version 1.3.0.
+ case ascent_tag:
+ printf(" Character Ascent array");
+ break;
+ case descent_tag:
+ printf(" Character Descent array");
+ break;
+ }
+ break;
+ case RATIONAL_TYPE:
+ printf(" %5u", tag_info(t).value);
+ switch (i) {
+ case inches_per_point_tag:
+ printf(" Inches per Point");
+ break;
+ case nominal_point_size_tag:
+ printf(" Nominal Point Size");
+ break;
+ case design_units_per_em_tag:
+ printf(" Design Units per Em");
+ break;
+ case average_width_tag:
+ printf(" Average Width");
+ break;
+ }
+ if (tag_info(t).count == 1) {
+ f.seek(tag_info(t).value);
+ uint32 num = f.get_uint32();
+ uint32 den = f.get_uint32();
+ if (i == design_units_per_em_tag)
+ upem = double(num) / den;
+ printf(" (%u/%u = %g)", num, den, double(num)/den);
+ }
+ break;
+ case ASCII_TYPE:
+ printf(" %5u ", tag_info(t).value);
+ switch (i) {
+ case comment_tag:
+ printf("Comment ");
+ break;
+ case copyright_tag:
+ printf("Copyright ");
+ break;
+ case unique_identifier_tag:
+ printf("Unique ID ");
+ break;
+ case font_name_tag:
+ printf("Typeface Name ");
+ break;
+ case typeface_source_tag:
+ printf("Typeface Source ");
+ break;
+ case typeface_tag:
+ printf("PCL Typeface ");
+ break;
+ }
+ dump_ascii(f, t);
+ }
+ putchar('\n');
+ }
+ }
+ putchar('\n');
+}
+#undef em_fract
+
+static void
+dump_ascii(File &f, tag_type t)
+{
+ putchar('"');
+ if (tag_info(t).count > 4) {
+ int count = tag_info(t).count;
+ f.seek(tag_info(t).value);
+ while (--count)
+ printf("%c", f.get_byte());
+ }
+ else
+ printf("%.4s", tag_info(t).orig_value);
+ putchar('"');
+}
+
+static void
+dump_symbol_sets(File &f)
+{
+ uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
+ uint32 num_symbol_sets = symbol_set_dir_length / 14;
+
+ for (uint32 i = 0; i < num_symbol_sets; i++) {
+ f.seek(tag_info(symbol_set_tag).value + i * 14);
+ (void)f.get_uint32(); // offset to symbol set name
+ uint32 off1 = f.get_uint32(); // offset to selection string
+ uint32 off2 = f.get_uint32(); // offset to symbol set index array
+ f.seek(off1);
+ for (uint32 j = 0; j < off2 - off1; j++) {
+ unsigned char c = f.get_byte();
+ if ('0' <= c && c <= '9')
+ putchar(c);
+ else if ('A' <= c && c <= 'Z')
+ printf(i < num_symbol_sets - 1 ? "%c," : "%c", c);
+ }
+ }
+}
+
+static void
+dump_symbols(int tfm_type)
+{
+ printf("Symbols:\n"
+ "\n"
+ " glyph id# symbol set name(s)\n"
+ "----------------------------------\n");
+ for (uint32 i = 0; i < nchars; i++) {
+ uint16 charcode = char_table[i].charcode;
+ if (charcode < charcode_name_table_size
+ && charcode_name_table[charcode]) {
+ if (char_table[i].symbol_set != NO_SYMBOL_SET) {
+ printf(tfm_type == UNICODE ? "%4d (U+%04X) (%3s %3d) %s"
+ : "%4d (MSL %4d) (%3s %3d) %s",
+ i, charcode,
+ show_symset(char_table[i].symbol_set),
+ char_table[i].code,
+ charcode_name_table[charcode]->name);
+ for (name_list *p = charcode_name_table[charcode]->next;
+ p; p = p->next)
+ printf(", %s", p->name);
+ putchar('\n');
+ }
+ }
+ else {
+ printf(tfm_type == UNICODE ? "%4d (U+%04X) "
+ : "%4d (MSL %4d) ",
+ i, charcode);
+ if (char_table[i].symbol_set != NO_SYMBOL_SET)
+ printf("(%3s %3d)",
+ show_symset(char_table[i].symbol_set), char_table[i].code);
+ putchar('\n');
+ }
+ }
+ putchar('\n');
+}
+
+static char *
+show_symset(unsigned int symset)
+{
+ // A 64-bit unsigned int produces up to 20 decimal digits.
+ assert(sizeof(unsigned int) <= 8);
+ static char symset_str[22]; // 20 digits + symset char + \0
+ sprintf(symset_str, "%u%c", symset / 32, (symset & 31) + 64);
+ return symset_str;
+}
+
+static char *
+hp_msl_to_ucode_name(int msl)
+{
+ // A 64-bit signed int produces up to 19 decimal digits plus a sign.
+ assert(sizeof(int) <= 8);
+ char codestr[21]; // 19 digits + possible sign + \0
+ sprintf(codestr, "%d", msl);
+ const char *ustr = hp_msl_to_unicode_code(codestr);
+ if (ustr == NULL)
+ ustr = UNNAMED;
+ else {
+ char *nonum;
+ int ucode = int(strtol(ustr, &nonum, 16));
+ // don't allow PUA code points as Unicode names
+ if (ucode >= 0xE000 && ucode <= 0xF8FF)
+ ustr = UNNAMED;
+ }
+ if (!equal(ustr, UNNAMED)) {
+ const char *uname_decomposed = decompose_unicode(ustr);
+ if (uname_decomposed)
+ // 1st char is the number of components
+ ustr = uname_decomposed + 1;
+ }
+ char *value = new char[strlen(ustr) + 1];
+ sprintf(value, equal(ustr, UNNAMED) ? UNNAMED : "u%s", ustr);
+ return value;
+}
+
+static char *
+unicode_to_ucode_name(int ucode)
+{
+ // A 64-bit signed int produces up to 16 hexadecimal digits.
+ assert(sizeof(int) <= 8);
+ const char *ustr;
+ char codestr[17]; // 16 hex digits + \0
+
+ // don't allow PUA code points as Unicode names
+ if (ucode >= 0xE000 && ucode <= 0xF8FF)
+ ustr = UNNAMED;
+ else {
+ sprintf(codestr, "%04X", ucode);
+ ustr = codestr;
+ }
+ if (!equal(ustr, UNNAMED)) {
+ const char *uname_decomposed = decompose_unicode(ustr);
+ if (uname_decomposed)
+ // 1st char is the number of components
+ ustr = uname_decomposed + 1;
+ }
+ char *value = new char[strlen(ustr) + 1];
+ sprintf(value, equal(ustr, UNNAMED) ? UNNAMED : "u%s", ustr);
+ return value;
+}
+
+static int
+is_uname(char *name)
+{
+ size_t i;
+ size_t len = strlen(name);
+ if (len % 5)
+ return 0;
+
+ if (name[0] != 'u')
+ return 0;
+ for (i = 1; i < 4; i++)
+ if (!csxdigit(name[i]))
+ return 0;
+ for (i = 5; i < len; i++)
+ if (i % 5 ? !csxdigit(name[i]) : name[i] != '_')
+ return 0;
+
+ return 1;
+}
+
+static int
+read_map(const char *file, const int tfm_type)
+{
+ errno = 0;
+ FILE *fp = fopen(file, "r");
+ if (!fp) {
+ error("can't open '%1': %2", file, strerror(errno));
+ return 0;
+ }
+ current_filename = file;
+ char buf[512];
+ current_lineno = 0;
+ char *nonum;
+ while (fgets(buf, int(sizeof(buf)), fp)) {
+ current_lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+ ptr = strtok(ptr, " \n\t");
+ if (!ptr)
+ continue;
+
+ int msl_code = int(strtol(ptr, &nonum, 10));
+ if (*nonum != '\0') {
+ if (csxdigit(*nonum))
+ error("bad MSL map: got hex code (%1)", ptr);
+ else if (ptr == nonum)
+ error("bad MSL map: bad MSL code (%1)", ptr);
+ else
+ error("bad MSL map");
+ fclose(fp);
+ return 0;
+ }
+
+ ptr = strtok(NULL, " \n\t");
+ if (!ptr)
+ continue;
+ int unicode = int(strtol(ptr, &nonum, 16));
+ if (*nonum != '\0') {
+ if (ptr == nonum)
+ error("bad Unicode value (%1)", ptr);
+ else
+ error("bad Unicode map");
+ fclose(fp);
+ return 0;
+ }
+ if (strlen(ptr) != 4) {
+ error("bad Unicode value (%1)", ptr);
+ return 0;
+ }
+
+ int n = tfm_type == MSL ? msl_code : unicode;
+ if (tfm_type == UNICODE && n > 0xFFFF) {
+ // greatest value supported by TFM files
+ error("bad Unicode value (%1): greatest value is 0xFFFF", ptr);
+ fclose(fp);
+ return 0;
+ }
+ else if (n < 0) {
+ error("negative code value (%1)", ptr);
+ fclose(fp);
+ return 0;
+ }
+
+ ptr = strtok(NULL, " \n\t");
+ if (!ptr) { // groff name
+ error("missing name(s)");
+ fclose(fp);
+ return 0;
+ }
+ // leave decomposed Unicode values alone
+ else if (is_uname(ptr) && !is_decomposed(ptr))
+ ptr = unicode_to_ucode_name(strtol(ptr + 1, &nonum, 16));
+
+ if (size_t(n) >= charcode_name_table_size) {
+ size_t old_size = charcode_name_table_size;
+ name_list **old_table = charcode_name_table;
+ charcode_name_table_size = n + 256;
+ charcode_name_table = new name_list *[charcode_name_table_size];
+ if (old_table) {
+ memcpy(charcode_name_table, old_table, old_size*sizeof(name_list *));
+ delete[] old_table;
+ }
+ for (size_t i = old_size; i < charcode_name_table_size; i++)
+ charcode_name_table[i] = NULL;
+ }
+
+ // a '#' that isn't the first groff name begins a comment
+ for (int names = 1; ptr; ptr = strtok(NULL, " \n\t")) {
+ if (names++ > 1 && *ptr == '#')
+ break;
+ charcode_name_table[n] = new name_list(ptr, charcode_name_table[n]);
+ }
+ }
+ fclose(fp);
+ return 1;
+}
+
+static const char *
+xbasename(const char *s)
+{
+ // DIR_SEPS[] are possible directory separator characters, see
+ // nonposix.h. We want the rightmost separator of all possible
+ // ones. Example: d:/foo\\bar.
+ const char *b = strrchr(s, DIR_SEPS[0]), *b1;
+ const char *sep = &DIR_SEPS[1];
+
+ while (*sep)
+ {
+ b1 = strrchr(s, *sep);
+ if (b1 && (!b || b1 > b))
+ b = b1;
+ sep++;
+ }
+ return b ? b + 1 : s;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/hpftodit/hpuni.cpp b/src/utils/hpftodit/hpuni.cpp
new file mode 100644
index 0000000..b3f933f
--- /dev/null
+++ b/src/utils/hpftodit/hpuni.cpp
@@ -0,0 +1,697 @@
+// -*- C++ -*-
+/* Copyright (C) 2003-2020 Free Software Foundation, Inc.
+ Written by Jeff Conrad (jeff_conrad@msn.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+#include "unicode.h"
+
+struct hp_msl_to_unicode {
+ char *value;
+};
+
+declare_ptable(hp_msl_to_unicode)
+implement_ptable(hp_msl_to_unicode)
+
+PTABLE(hp_msl_to_unicode) hp_msl_to_unicode_table;
+
+struct S {
+ const char *key;
+ const char *value;
+} hp_msl_to_unicode_list[] = {
+ { "1", "0021", }, // Exclamation Mark
+ { "2", "0022", }, // Neutral Double Quote
+ { "3", "0023", }, // Number Sign
+ { "4", "0024", }, // Dollar Sign
+ { "5", "0025", }, // Per Cent Sign
+ { "6", "0026", }, // Ampersand
+ { "8", "2019", }, // Single Close Quote (9)
+ { "9", "0028", }, // Left Parenthesis
+ { "10", "0029", }, // Right Parenthesis
+ { "11", "002A", }, // Asterisk
+ { "12", "002B", }, // Plus Sign
+ { "13", "002C", }, // Comma, or Decimal Separator
+ { "14", "002D", }, // Hyphen
+ { "15", "002E", }, // Period, or Full Stop
+ { "16", "002F", }, // Solidus, or Slash
+ { "17", "0030", }, // Numeral Zero
+ { "18", "0031", }, // Numeral One
+ { "19", "0032", }, // Numeral Two
+ { "20", "0033", }, // Numeral Three
+ { "21", "0034", }, // Numeral Four
+ { "22", "0035", }, // Numeral Five
+ { "23", "0036", }, // Numeral Six
+ { "24", "0037", }, // Numeral Seven
+ { "25", "0038", }, // Numeral Eight
+ { "26", "0039", }, // Numeral Nine
+ { "27", "003A", }, // Colon
+ { "28", "003B", }, // Semicolon
+ { "29", "003C", }, // Less Than Sign
+ { "30", "003D", }, // Equals Sign
+ { "31", "003E", }, // Greater Than Sign
+ { "32", "003F", }, // Question Mark
+ { "33", "0040", }, // Commercial At
+ { "34", "0041", }, // Uppercase A
+ { "35", "0042", }, // Uppercase B
+ { "36", "0043", }, // Uppercase C
+ { "37", "0044", }, // Uppercase D
+ { "38", "0045", }, // Uppercase E
+ { "39", "0046", }, // Uppercase F
+ { "40", "0047", }, // Uppercase G
+ { "41", "0048", }, // Uppercase H
+ { "42", "0049", }, // Uppercase I
+ { "43", "004A", }, // Uppercase J
+ { "44", "004B", }, // Uppercase K
+ { "45", "004C", }, // Uppercase L
+ { "46", "004D", }, // Uppercase M
+ { "47", "004E", }, // Uppercase N
+ { "48", "004F", }, // Uppercase O
+ { "49", "0050", }, // Uppercase P
+ { "50", "0051", }, // Uppercase Q
+ { "51", "0052", }, // Uppercase R
+ { "52", "0053", }, // Uppercase S
+ { "53", "0054", }, // Uppercase T
+ { "54", "0055", }, // Uppercase U
+ { "55", "0056", }, // Uppercase V
+ { "56", "0057", }, // Uppercase W
+ { "57", "0058", }, // Uppercase X
+ { "58", "0059", }, // Uppercase Y
+ { "59", "005A", }, // Uppercase Z
+ { "60", "005B", }, // Left Bracket
+ { "61", "005C", }, // Reverse Solidus, or Backslash
+ { "62", "005D", }, // Right Bracket
+ { "63", "005E", }, // Circumflex, Exponent, or Pointer
+ { "64", "005F", }, // Underline or Underscore Character
+ { "66", "2018", }, // Single Open Quote (6)
+ { "67", "0061", }, // Lowercase A
+ { "68", "0062", }, // Lowercase B
+ { "69", "0063", }, // Lowercase C
+ { "70", "0064", }, // Lowercase D
+ { "71", "0065", }, // Lowercase E
+ { "72", "0066", }, // Lowercase F
+ { "73", "0067", }, // Lowercase G
+ { "74", "0068", }, // Lowercase H
+ { "75", "0069", }, // Lowercase I
+ { "76", "006A", }, // Lowercase J
+ { "77", "006B", }, // Lowercase K
+ { "78", "006C", }, // Lowercase L
+ { "79", "006D", }, // Lowercase M
+ { "80", "006E", }, // Lowercase N
+ { "81", "006F", }, // Lowercase O
+ { "82", "0070", }, // Lowercase P
+ { "83", "0071", }, // Lowercase Q
+ { "84", "0072", }, // Lowercase R
+ { "85", "0073", }, // Lowercase S
+ { "86", "0074", }, // Lowercase T
+ { "87", "0075", }, // Lowercase U
+ { "88", "0076", }, // Lowercase V
+ { "89", "0077", }, // Lowercase W
+ { "90", "0078", }, // Lowercase X
+ { "91", "0079", }, // Lowercase Y
+ { "92", "007A", }, // Lowercase Z
+ { "93", "007B", }, // Left Brace
+ { "94", "007C", }, // Long Vertical Mark
+ { "95", "007D", }, // Right Brace
+ { "96", "007E", }, // One Wavy Line Approximate
+ { "97", "2592", }, // Medium Shading Character
+ { "99", "00C0", }, // Uppercase A Grave
+ { "100", "00C2", }, // Uppercase A Circumflex
+ { "101", "00C8", }, // Uppercase E Grave
+ { "102", "00CA", }, // Uppercase E Circumflex
+ { "103", "00CB", }, // Uppercase E Dieresis
+ { "104", "00CE", }, // Uppercase I Circumflex
+ { "105", "00CF", }, // Uppercase I Dieresis
+ { "106", "00B4", }, // Lowercase Acute Accent (Spacing)
+ { "107", "0060", }, // Lowercase Grave Accent (Spacing)
+ { "108", "02C6", }, // Lowercase Circumflex Accent (Spacing)
+ { "109", "00A8", }, // Lowercase Dieresis Accent (Spacing)
+ { "110", "02DC", }, // Lowercase Tilde Accent (Spacing)
+ { "111", "00D9", }, // Uppercase U Grave
+ { "112", "00DB", }, // Uppercase U Circumflex
+ { "113", "00AF", }, // Overline, or Overscore Character
+ { "114", "00DD", }, // Uppercase Y Acute
+ { "115", "00FD", }, // Lowercase Y Acute
+ { "116", "00B0", }, // Degree Sign
+ { "117", "00C7", }, // Uppercase C Cedilla
+ { "118", "00E7", }, // Lowercase C Cedilla
+ { "119", "00D1", }, // Uppercase N Tilde
+ { "120", "00F1", }, // Lowercase N Tilde
+ { "121", "00A1", }, // Inverted Exclamation
+ { "122", "00BF", }, // Inverted Question Mark
+ { "123", "00A4", }, // Currency Symbol
+ { "124", "00A3", }, // Pound Sterling Sign
+ { "125", "00A5", }, // Yen Sign
+ { "126", "00A7", }, // Section Mark
+ { "127", "0192", }, // Florin Sign
+ { "128", "00A2", }, // Cent Sign
+ { "129", "00E2", }, // Lowercase A Circumflex
+ { "130", "00EA", }, // Lowercase E Circumflex
+ { "131", "00F4", }, // Lowercase O Circumflex
+ { "132", "00FB", }, // Lowercase U Circumflex
+ { "133", "00E1", }, // Lowercase A Acute
+ { "134", "00E9", }, // Lowercase E Acute
+ { "135", "00F3", }, // Lowercase O Acute
+ { "136", "00FA", }, // Lowercase U Acute
+ { "137", "00E0", }, // Lowercase A Grave
+ { "138", "00E8", }, // Lowercase E Grave
+ { "139", "00F2", }, // Lowercase O Grave
+ { "140", "00F9", }, // Lowercase U Grave
+ { "141", "00E4", }, // Lowercase A Dieresis
+ { "142", "00EB", }, // Lowercase E Dieresis
+ { "143", "00F6", }, // Lowercase O Dieresis
+ { "144", "00FC", }, // Lowercase U Dieresis
+ { "145", "00C5", }, // Uppercase A Ring
+ { "146", "00EE", }, // Lowercase I Circumflex
+ { "147", "00D8", }, // Uppercase O Oblique
+ { "148", "00C6", }, // Uppercase AE Diphthong
+ { "149", "00E5", }, // Lowercase A Ring
+ { "150", "00ED", }, // Lowercase I Acute
+ { "151", "00F8", }, // Lowercase O Oblique
+ { "152", "00E6", }, // Lowercase AE Diphthong
+ { "153", "00C4", }, // Uppercase A Dieresis
+ { "154", "00EC", }, // Lowercase I Grave
+ { "155", "00D6", }, // Uppercase O Dieresis
+ { "156", "00DC", }, // Uppercase U Dieresis
+ { "157", "00C9", }, // Uppercase E Acute
+ { "158", "00EF", }, // Lowercase I Dieresis
+ { "159", "00DF", }, // Lowercase Es-zet Ligature
+ { "160", "00D4", }, // Uppercase O Circumflex
+ { "161", "00C1", }, // Uppercase A Acute
+ { "162", "00C3", }, // Uppercase A Tilde
+ { "163", "00E3", }, // Lowercase A Tilde
+ { "164", "00D0", }, // Uppercase Eth
+//{ "164", "0110", }, // Uppercase D-Stroke
+ { "165", "00F0", }, // Lowercase Eth
+ { "166", "00CD", }, // Uppercase I Acute
+ { "167", "00CC", }, // Uppercase I Grave
+ { "168", "00D3", }, // Uppercase O Acute
+ { "169", "00D2", }, // Uppercase O Grave
+ { "170", "00D5", }, // Uppercase O Tilde
+ { "171", "00F5", }, // Lowercase O Tilde
+ { "172", "0160", }, // Uppercase S Hacek
+ { "173", "0161", }, // Lowercase S Hacek
+ { "174", "00DA", }, // Uppercase U Acute
+ { "175", "0178", }, // Uppercase Y Dieresis
+ { "176", "00FF", }, // Lowercase Y Dieresis
+ { "177", "00DE", }, // Uppercase Thorn
+ { "178", "00FE", }, // Lowercase Thorn
+ { "180", "00B5", }, // Lowercase Greek Mu, or Micro
+ { "181", "00B6", }, // Pilcrow, or Paragraph Sign
+ { "182", "00BE", }, // Vulgar Fraction 3/4
+ { "183", "2212", }, // Minus Sign
+ { "184", "00BC", }, // Vulgar Fraction 1/4
+ { "185", "00BD", }, // Vulgar Fraction 1/2
+ { "186", "00AA", }, // Female Ordinal
+ { "187", "00BA", }, // Male Ordinal
+ { "188", "00AB", }, // Left Pointing Double Angle Quote
+ { "189", "25A0", }, // Medium Solid Square Box
+ { "190", "00BB", }, // Right Pointing Double Angle Quote
+ { "191", "00B1", }, // Plus Over Minus Sign
+ { "192", "00A6", }, // Broken Vertical Mark
+ { "193", "00A9", }, // Copyright Sign
+ { "194", "00AC", }, // Not Sign
+ { "195", "00AD", }, // Soft Hyphen
+ { "196", "00AE", }, // Registered Sign
+ { "197", "00B2", }, // Superior Numeral 2
+ { "198", "00B3", }, // Superior Numeral 3
+ { "199", "00B8", }, // Lowercase Cedilla (Spacing)
+ { "200", "00B9", }, // Superior Numeral 1
+ { "201", "00D7", }, // Multiply Sign
+ { "202", "00F7", }, // Divide Sign
+ { "203", "263A", }, // Open Smiling Face
+ { "204", "263B", }, // Solid Smiling Face
+ { "205", "2665", }, // Solid Heart, Card Suit
+ { "206", "2666", }, // Solid Diamond, Card Suit
+ { "207", "2663", }, // Solid Club, Card Suit
+ { "208", "2660", }, // Solid Spade, Card Suit
+ { "209", "25CF", }, // Medium Solid Round Bullet
+ { "210", "25D8", }, // Large Solid square with White Dot
+ { "211", "EFFD", }, // Large Open Round Bullet
+ { "212", "25D9", }, // Large Solid square with White Circle
+ { "213", "2642", }, // Male Symbol
+ { "214", "2640", }, // Female Symbol
+ { "215", "266A", }, // Musical Note
+ { "216", "266B", }, // Pair Of Musical Notes
+ { "217", "263C", }, // Compass, or Eight Pointed Sun
+ { "218", "25BA", }, // Right Solid Arrowhead
+ { "219", "25C4", }, // Left Solid Arrowhead
+ { "220", "2195", }, // Up/Down Arrow
+ { "221", "203C", }, // Double Exclamation Mark
+ { "222", "25AC", }, // Thick Horizontal Mark
+ { "223", "21A8", }, // Up/Down Arrow Baseline
+ { "224", "2191", }, // Up Arrow
+ { "225", "2193", }, // Down Arrow
+ { "226", "2192", }, // Right Arrow
+ { "227", "2190", }, // Left Arrow
+ { "229", "2194", }, // Left/Right Arrow
+ { "230", "25B2", }, // Up Solid Arrowhead
+ { "231", "25BC", }, // Down Solid Arrowhead
+ { "232", "20A7", }, // Pesetas Sign
+ { "233", "2310", }, // Reversed Not Sign
+ { "234", "2591", }, // Light Shading Character
+ { "235", "2593", }, // Dark Shading Character
+ { "236", "2502", }, // Box Draw Line, Vert. 1
+ { "237", "2524", }, // Box Draw Right Tee, Vert. 1 Horiz. 1
+ { "238", "2561", }, // Box Draw Right Tee, Vert. 1 Horiz. 2
+ { "239", "2562", }, // Box Draw Right Tee, Vert. 2 Horiz. 1
+ { "240", "2556", }, // Box Draw Upper Right Corner, Vert. 2 Horiz. 1
+ { "241", "2555", }, // Box Draw Upper Right Corner, Vert. 1 Horiz. 2
+ { "242", "2563", }, // Box Draw Right Tee, Vert. 2 Horiz. 2
+ { "243", "2551", }, // Box Draw Lines, Vert. 2
+ { "244", "2557", }, // Box Draw Upper Right Corner, Vert. 2 Horiz. 2
+ { "245", "255D", }, // Box Draw Lower Right Corner, Vert. 2 Horiz. 2
+ { "246", "255C", }, // Box Draw Lower Right Corner, Vert. 2 Horiz. 1
+ { "247", "255B", }, // Box Draw Lower Right Corner, Vert. 1 Horiz. 2
+ { "248", "2510", }, // Box Draw Upper Right Corner, Vert. 1, Horiz. 1
+ { "249", "2514", }, // Box Draw Lower Left Corner, Vert. 1, Horiz. 1
+ { "250", "2534", }, // Box Draw Bottom Tee, Vert. 1 Horiz. 1
+ { "251", "252C", }, // Box Draw Top Tee, Vert. 1 Horiz. 1
+ { "252", "251C", }, // Box Draw Left Tee, Vert. 1 Horiz. 1
+ { "253", "2500", }, // Box Draw Line, Horiz. 1
+ { "254", "253C", }, // Box Draw Cross, Vert. 1 Horiz. 1
+ { "255", "255E", }, // Box Draw Left Tee, Vert. 1 Horiz. 2
+ { "256", "255F", }, // Box Draw Left Tee, Vert. 2 Horz. 1
+ { "257", "255A", }, // Box Draw Lower Left Corner, Vert. 2 Horiz. 2
+ { "258", "2554", }, // Box Draw Upper Left Corner, Vert. 2 Horiz. 2
+ { "259", "2569", }, // Box Draw Bottom Tee, Vert. 2 Horiz. 2
+ { "260", "2566", }, // Box Draw Top Tee, Vert. 2 Horiz. 2
+ { "261", "2560", }, // Box Draw Left Tee, Vert. 2 Horiz. 2
+ { "262", "2550", }, // Box Draw Lines, Horiz. 2
+ { "263", "256C", }, // Box Draw Cross Open Center, Vert. 2 Horiz. 2
+ { "264", "2567", }, // Box Draw Bottom Tee, Vert. 1 Horiz. 2
+ { "265", "2568", }, // Box Draw Bottom Tee, Vert. 2 Horiz. 1
+ { "266", "2564", }, // Box Draw Top Tee, Vert. 1 Horiz. 2
+ { "267", "2565", }, // Box Draw Top Tee, Vert. 2 Horiz. 1
+ { "268", "2559", }, // Box Draw Lower Left Corner, Vert. 2 Horiz. 1
+ { "269", "2558", }, // Box Draw Lower Left Corner, Vert. 1 Horiz. 2
+ { "270", "2552", }, // Box Draw Upper Left Corner, Vert. 1 Horiz. 2
+ { "271", "2553", }, // Box Draw Upper Left Corner, Vert. 2 Horiz. 1
+ { "272", "256B", }, // Box Draw Cross, Vert. 2 Horiz. 1
+ { "273", "256A", }, // Box Draw Cross, Vert. 1 Horiz. 2
+ { "274", "2518", }, // Box Draw Lower Right Corner, Vert. 1 Horiz. 1
+ { "275", "250C", }, // Box Draw Upper Left Corner, Vert. 1, Horiz. 1
+ { "276", "2588", }, // Solid Full High/Wide
+ { "277", "2584", }, // Bottom Half Solid Rectangle
+ { "278", "258C", }, // Left Half Solid Rectangle
+ { "279", "2590", }, // Right Half Solid Rectangle
+ { "280", "2580", }, // Top Half Solid Rectangle
+ { "290", "2126", }, // Uppercase Greek Omega, or Ohms
+ { "292", "221E", }, // Infinity Symbol
+ { "295", "2229", }, // Set Intersection Symbol
+ { "296", "2261", }, // Exactly Equals Sign
+ { "297", "2265", }, // Greater Than or Equal Sign
+ { "298", "2264", }, // Less Than or Equal Sign
+ { "299", "2320", }, // Top Integral
+ { "300", "2321", }, // Bottom Integral
+ { "301", "2248", }, // Two Wavy Line Approximate Sign
+//{ "302", "00B7", }, // Middle Dot, or Centered Period (see 2219)
+//{ "302", "2219", }, // Centered Period, Middle Dot
+ { "302", "2219", }, // Math Dot, Centered Period
+ { "303", "221A", }, // Radical Symbol, Standalone Diagonal
+ { "305", "25AA", }, // Small Solid Square Box
+ { "306", "013F", }, // Uppercase L-Dot
+ { "307", "0140", }, // Lowercase L-Dot
+ { "308", "2113", }, // Litre Symbol
+ { "309", "0149", }, // Lowercase Apostrophe-N
+ { "310", "2032", }, // Prime, Minutes, or Feet Symbol
+ { "311", "2033", }, // Double Prime, Seconds, or Inches Symbol
+ { "312", "2020", }, // Dagger Symbol
+ { "313", "2122", }, // Trademark Sign
+ { "314", "2017", }, // Double Underline Character
+ { "315", "02C7", }, // Lowercase Hacek Accent (Spacing)
+ { "316", "02DA", }, // Lowercase Ring Accent (Spacing)
+ { "317", "EFF9", }, // Uppercase Acute Accent (Spacing)
+ { "318", "EFF8", }, // Uppercase Grave Accent (Spacing)
+ { "319", "EFF7", }, // Uppercase Circumflex Accent (Spacing)
+ { "320", "EFF6", }, // Uppercase Dieresis Accent (Spacing)
+ { "321", "EFF5", }, // Uppercase Tilde Accent (Spacing)
+ { "322", "EFF4", }, // Uppercase Hacek Accent (Spacing)
+ { "323", "EFF3", }, // Uppercase Ring Accent (Spacing)
+ { "324", "2215", }, // Vulgar Fraction Bar
+ { "325", "2014", }, // Em Dash
+ { "326", "2013", }, // En Dash
+ { "327", "2021", }, // Double Dagger Symbol
+ { "328", "0131", }, // Lowercase Undotted I
+ { "329", "0027", }, // Neutral Single Quote
+ { "330", "EFF2", }, // Uppercase Cedilla (Spacing)
+ { "331", "2022", }, // Small Solid Round Bullet
+ { "332", "207F", }, // Superior Lowercase N
+ { "333", "2302", }, // Home Plate
+ { "335", "0138", }, // Lowercase Kra
+ { "338", "0166", }, // Uppercase T-Stroke
+ { "339", "0167", }, // Lowercase T-Stroke
+ { "340", "014A", }, // Uppercase Eng
+ { "341", "014B", }, // Lowercase Eng
+ { "342", "0111", }, // Lowercase D-Stroke
+ { "400", "0102", }, // Uppercase A Breve
+ { "401", "0103", }, // Lowercase A Breve
+ { "402", "0100", }, // Uppercase A Macron
+ { "403", "0101", }, // Lowercase A Macron
+ { "404", "0104", }, // Uppercase A Ogonek
+ { "405", "0105", }, // Lowercase A Ogonek
+ { "406", "0106", }, // Uppercase C Acute
+ { "407", "0107", }, // Lowercase C Acute
+ { "410", "010C", }, // Uppercase C Hacek
+ { "411", "010D", }, // Lowercase C Hacek
+ { "414", "010E", }, // Uppercase D Hacek
+ { "415", "010F", }, // Lowercase D Hacek
+ { "416", "011A", }, // Uppercase E Hacek
+ { "417", "011B", }, // Lowercase E Hacek
+ { "418", "0116", }, // Uppercase E Overdot
+ { "419", "0117", }, // Lowercase E Overdot
+ { "420", "0112", }, // Uppercase E Macron
+ { "421", "0113", }, // Lowercase E Macron
+ { "422", "0118", }, // Uppercase E Ogonek
+ { "423", "0119", }, // Lowercase E Ogonek
+ { "428", "0122", }, // Uppercase G Cedilla
+ { "429", "0123", }, // Lowercase G Cedilla
+ { "432", "012E", }, // Uppercase I Ogonek
+ { "433", "012F", }, // Lowercase I Ogonek
+ { "434", "012A", }, // Uppercase I Macron
+ { "435", "012B", }, // Lowercase I Macron
+ { "438", "0136", }, // Uppercase K Cedilla
+ { "439", "0137", }, // Lowercase K Cedilla
+ { "440", "0139", }, // Uppercase L Acute
+ { "441", "013A", }, // Lowercase L Acute
+ { "442", "013D", }, // Uppercase L Hacek
+ { "443", "013E", }, // Lowercase L Hacek
+ { "444", "013B", }, // Uppercase L Cedilla
+ { "445", "013C", }, // Lowercase L Cedilla
+ { "446", "0143", }, // Uppercase N Acute
+ { "447", "0144", }, // Lowercase N Acute
+ { "448", "0147", }, // Uppercase N Hacek
+ { "449", "0148", }, // Lowercase N Hacek
+ { "450", "0145", }, // Uppercase N Cedilla
+ { "451", "0146", }, // Lowercase N Cedilla
+ { "452", "0150", }, // Uppercase O Double Acute
+ { "453", "0151", }, // Lowercase O Double Acute
+ { "454", "014C", }, // Uppercase O Macron
+ { "455", "014D", }, // Lowercase O Macron
+ { "456", "0154", }, // Uppercase R Acute
+ { "457", "0155", }, // Lowercase R Acute
+ { "458", "0158", }, // Uppercase R Hacek
+ { "459", "0159", }, // Lowercase R Hacek
+ { "460", "0156", }, // Uppercase R Cedilla
+ { "461", "0157", }, // Lowercase R Cedilla
+ { "462", "015A", }, // Uppercase S Acute
+ { "463", "015B", }, // Lowercase S Acute
+ { "466", "0164", }, // Uppercase T Hacek
+ { "467", "0165", }, // Lowercase T Hacek
+ { "468", "0162", }, // Uppercase T Cedilla
+ { "469", "0163", }, // Lowercase T Cedilla
+ { "470", "0168", }, // Uppercase U Tilde
+ { "471", "0169", }, // Lowercase U Tilde
+ { "474", "0170", }, // Uppercase U Double Acute
+ { "475", "0171", }, // Lowercase U Double Acute
+ { "476", "016E", }, // Uppercase U Ring
+ { "477", "016F", }, // Lowercase U Ring
+ { "478", "016A", }, // Uppercase U Macron
+ { "479", "016B", }, // Lowercase U Macron
+ { "480", "0172", }, // Uppercase U Ogonek
+ { "481", "0173", }, // Lowercase U Ogonek
+ { "482", "0179", }, // Uppercase Z Acute
+ { "483", "017A", }, // Lowercase Z Acute
+ { "484", "017B", }, // Uppercase Z Overdot
+ { "485", "017C", }, // Lowercase Z Overdot
+ { "486", "0128", }, // Uppercase I Tilde
+ { "487", "0129", }, // Lowercase I Tilde
+ { "500", "EFBF", }, // Radical, Diagonal, Composite
+ { "501", "221D", }, // Proportional To Symbol
+ { "502", "212F", }, // Napierian (italic e)
+ { "503", "03F5", }, // Alternate Lowercase Greek Epsilon
+//{ "503", "EFEC", }, // Alternate Lowercase Greek Epsilon
+ { "504", "2234", }, // Therefore Symbol
+ { "505", "0393", }, // Uppercase Greek Gamma
+ { "506", "2206", }, // Increment Symbol (Delta)
+ { "507", "0398", }, // Uppercase Greek Theta
+ { "508", "039B", }, // Uppercase Greek Lambda
+ { "509", "039E", }, // Uppercase Greek Xi
+ { "510", "03A0", }, // Uppercase Greek Pi
+ { "511", "03A3", }, // Uppercase Greek Sigma
+ { "512", "03A5", }, // Uppercase Greek Upsilon
+ { "513", "03A6", }, // Uppercase Greek Phi
+ { "514", "03A8", }, // Uppercase Greek Psi
+ { "515", "03A9", }, // Uppercase Greek Omega
+ { "516", "2207", }, // Nabla Symbol (inverted Delta)
+ { "517", "2202", }, // Partial Differential Delta Symbol
+ { "518", "03C2", }, // Lowercase Sigma, Terminal
+ { "519", "2260", }, // Not Equal To Symbol
+ { "520", "EFEB", }, // Underline, Composite
+ { "521", "2235", }, // Because Symbol
+ { "522", "03B1", }, // Lowercase Greek Alpha
+ { "523", "03B2", }, // Lowercase Greek Beta
+ { "524", "03B3", }, // Lowercase Greek Gamma
+ { "525", "03B4", }, // Lowercase Greek Delta
+ { "526", "03B5", }, // Lowercase Greek Epsilon
+ { "527", "03B6", }, // Lowercase Greek Zeta
+ { "528", "03B7", }, // Lowercase Greek Eta
+ { "529", "03B8", }, // Lowercase Greek Theta
+ { "530", "03B9", }, // Lowercase Greek Iota
+ { "531", "03BA", }, // Lowercase Greek Kappa
+ { "532", "03BB", }, // Lowercase Greek Lambda
+ { "533", "03BC", }, // Lowercase Greek Mu
+ { "534", "03BD", }, // Lowercase Greek Nu
+ { "535", "03BE", }, // Lowercase Greek Xi
+ { "536", "03BF", }, // Lowercase Greek Omicron
+ { "537", "03C0", }, // Lowercase Greek Pi
+ { "538", "03C1", }, // Lowercase Greek Rho
+ { "539", "03C3", }, // Lowercase Greek Sigma
+ { "540", "03C4", }, // Lowercase Greek Tau
+ { "541", "03C5", }, // Lowercase Greek Upsilon
+ { "542", "03C6", }, // Lowercase Greek Phi
+ { "543", "03C7", }, // Lowercase Greek Chi
+ { "544", "03C8", }, // Lowercase Greek Psi
+ { "545", "03C9", }, // Lowercase Greek Omega
+ { "546", "03D1", }, // Lowercase Greek Theta, Open
+ { "547", "03D5", }, // Lowercase Greek Phi, Open
+ { "548", "03D6", }, // Lowercase Pi, Alternate
+ { "549", "2243", }, // Wavy Over Straight Approximate Symbol
+ { "550", "2262", }, // Not Exactly Equal To Symbol
+ { "551", "21D1", }, // Up Arrow Double Stroke
+ { "552", "21D2", }, // Right Arrow Double Stroke
+ { "553", "21D3", }, // Down Arrow Double Stroke
+ { "554", "21D0", }, // Left Arrow Double Stroke
+ { "555", "21D5", }, // Up/Down Arrow Double Stroke
+ { "556", "21D4", }, // Left/Right Arrow Double Stroke
+ { "557", "21C4", }, // Right Over Left Arrow
+ { "558", "21C6", }, // Left Over Right Arrow
+ { "559", "EFE9", }, // Vector Symbol
+ { "560", "0305", }, // Overline, Composite
+ { "561", "2200", }, // For All Symbol, or Universal (inverted A)
+ { "562", "2203", }, // There Exists Symbol, or Existential (inverted E)
+ { "563", "22A4", }, // Top Symbol
+ { "564", "22A5", }, // Bottom Symbol
+ { "565", "222A", }, // Set Union Symbol
+ { "566", "2208", }, // Element-Of Symbol
+ { "567", "220B", }, // Contains Symbol
+ { "568", "2209", }, // Not-Element-Of Symbol
+ { "569", "2282", }, // Proper Subset Symbol
+ { "570", "2283", }, // Proper Superset Symbol
+ { "571", "2284", }, // Not Proper Subset Symbol
+ { "572", "2285", }, // Not Proper Superset Symbol
+ { "573", "2286", }, // Subset Symbol
+ { "574", "2287", }, // Superset Symbol
+ { "575", "2295", }, // Plus In Circle Symbol
+ { "576", "2299", }, // Dot In Circle Symbol
+ { "577", "2297", }, // Times In Circle Symbol
+ { "578", "2296", }, // Minus In Circle Symbol
+ { "579", "2298", }, // Slash In Circle Symbol
+ { "580", "2227", }, // Logical And Symbol
+ { "581", "2228", }, // Logical Or Symbol
+ { "582", "22BB", }, // Exclusive Or Symbol
+ { "583", "2218", }, // Functional Composition Symbol
+ { "584", "20DD", }, // Large Open Circle
+ { "585", "22A3", }, // Assertion Symbol
+ { "586", "22A2", }, // Backwards Assertion Symbol
+ { "587", "222B", }, // Integral Symbol
+ { "588", "222E", }, // Curvilinear Integral Symbol
+ { "589", "2220", }, // Angle Symbol
+ { "590", "2205", }, // Empty Set Symbol
+ { "591", "2135", }, // Hebrew Aleph
+ { "592", "2136", }, // Hebrew Beth
+ { "593", "2137", }, // Hebrew Gimmel
+ { "594", "212D", }, // Fraktur Uppercase C
+ { "595", "2111", }, // Fraktur Uppercase I
+ { "596", "211C", }, // Fraktur Uppercase R
+ { "597", "2128", }, // Fraktur Uppercase Z
+ { "598", "23A1", }, // Top Segment Left Bracket (Left Square Bracket Upper Corner)
+ { "599", "23A3", }, // Bottom Segment Left Bracket (Left Square Bracket Lower Corner)
+ { "600", "239B", }, // Top Segment Left Brace (Left Parenthesis Upper Hook)
+//{ "600", "23A7", }, // Top Segment Left Brace (Right Curly Bracket Upper Hook)
+ { "601", "23A8", }, // Middle Segment Left Brace (Right Curly Bracket Middle Piece)
+ { "602", "239D", }, // Bottom Segment LeftBrace (Left Parenthesis Lower Hook)
+//{ "602", "23A9", }, // Bottom Segment Left Brace (Right Curly Bracket Lower Hook)
+ { "603", "EFD4", }, // Middle Segment Curvilinear Integral
+ { "604", "EFD3", }, // Top Left Segment Summation
+ { "605", "2225", }, // Double Vertical Line, Composite
+ { "606", "EFD2", }, // Bottom Left Segment Summation
+ { "607", "EFD1", }, // Bottom Diagonal Summation
+ { "608", "23A4", }, // Top Segment Right Bracket (Right Square Bracket Upper Corner)
+ { "609", "23A6", }, // Bottom Segment Right Bracket (Right Square Bracket Lower Corner)
+ { "610", "239E", }, // Top Segment Right Brace (Right Parenthesis Upper Hook)
+//{ "610", "23AB", }, // Top Segment Right Brace (Right Curly Bracket Upper Hook)
+ { "611", "23AC", }, // Middle Segment Right Brace (Right Curly Bracket Middle Piece)
+ { "612", "23A0", }, // Bottom Segment Right ( Right Parenthesis Lower Hook)
+//{ "612", "23AD", }, // Bottom Segment Right Brace (Right Curly Bracket Lower Hook)
+ { "613", "239C", }, // Thick Vertical Line, Composite (Left Parenthesis Extension)
+//{ "613", "239F", }, // Thick Vertical Line, Composite (Right Parenthesis Extension)
+//{ "613", "23AA", }, // Thick Vertical Line, Composite (Curly Bracket Extension)
+//{ "613", "23AE", }, // Thick Vertical Line, Composite (Integral Extension)
+ { "614", "2223", }, // Thin Vertical Line, Composite
+ { "615", "EFDC", }, // Bottom Segment of Vertical Radical
+ { "616", "EFD0", }, // Top Right Segment Summation
+ { "617", "EFCF", }, // Middle Segment Summation
+ { "618", "EFCE", }, // Bottom Right Segment Summation
+ { "619", "EFCD", }, // Top Diagonal Summation
+ { "620", "2213", }, // Minus Over Plus Sign
+ { "621", "2329", }, // Left Angle Bracket
+ { "622", "232A", }, // Right Angle Bracket
+ { "623", "EFFF", }, // Mask Symbol
+ { "624", "2245", }, // Wavy Over Two Straight Approximate Symbol
+ { "625", "2197", }, // 45 Degree Arrow
+ { "626", "2198", }, // -45 Degree Arrow
+ { "627", "2199", }, // -135 Degree Arrow
+ { "628", "2196", }, // 135 Degree Arrow
+ { "629", "25B5", }, // Up Open Triangle
+ { "630", "25B9", }, // Right Open Triangle
+ { "631", "25BF", }, // Down Open Triangle
+ { "632", "25C3", }, // Left Open Triangle
+ { "633", "226A", }, // Much Less Than Sign
+ { "634", "226B", }, // Much Greater Than Sign
+ { "635", "2237", }, // Proportional To Symbol (4 dots)
+ { "636", "225C", }, // Defined As Symbol
+ { "637", "03DD", }, // Lowercase Greek Digamma
+ { "638", "210F", }, // Planck's Constant divided by 2 pi
+ { "639", "2112", }, // Laplace Transform Symbol
+ { "640", "EFFE", }, // Power Set
+ { "641", "2118", }, // Weierstrassian Symbol
+ { "642", "2211", }, // Summation Symbol (large Sigma)
+ { "643", "301A", }, // Left Double Bracket
+ { "644", "EFC9", }, // Middle Segment Double Bracket
+ { "645", "301B", }, // Right Double Bracket
+ { "646", "256D", }, // Box Draw Left Top Round Corner
+ { "647", "2570", }, // Box Draw Left Bottom Round Corner
+ { "648", "EFC8", }, // Extender Large Union/Product
+ { "649", "EFC7", }, // Bottom Segment Large Union
+ { "650", "EFC6", }, // Top Segment Large Intersection
+ { "651", "EFC5", }, // Top Segment Left Double Bracket
+ { "652", "EFC4", }, // Bottom Segment Left Double Bracket
+ { "653", "EFFC", }, // Large Open Square Box
+ { "654", "25C7", }, // Open Diamond
+ { "655", "256E", }, // Box Draw Right Top Round Corner
+ { "656", "256F", }, // Box Draw Right Bottom Round Corner
+ { "657", "EFC3", }, // Bottom Segment Large Bottom Product
+ { "658", "EFC2", }, // Top Segment Large Top Product
+ { "659", "EFC1", }, // Top Segment Right Double Bracket
+ { "660", "EFC0", }, // Bottom Segment Right Double Bracket
+ { "661", "EFFB", }, // Large Solid Square Box
+ { "662", "25C6", }, // Solid Diamond
+ { "663", "220D", }, // Such That Symbol (rotated lc epsilon)
+ { "664", "2217", }, // Math Asterisk
+ { "665", "23AF", }, // Horizontal Arrow Extender (Horizontal Line Extension)
+ { "666", "EFCB", }, // Double Horizontal Arrow Extender
+ { "667", "EFCC", }, // Inverted Complement of 0xEFCF or MSL 617
+ { "668", "221F", }, // Right Angle Symbol
+ { "669", "220F", }, // Product Symbol (large Pi)
+ { "684", "25CA", }, // Lozenge, Diamond
+ { "1000", "2070", }, // Superior Numeral 0
+ { "1001", "2074", }, // Superior Numeral 4
+ { "1002", "2075", }, // Superior Numeral 5
+ { "1003", "2076", }, // Superior Numeral 6
+ { "1004", "2077", }, // Superior Numeral 7
+ { "1005", "2078", }, // Superior Numeral 8
+ { "1006", "2079", }, // Superior Numeral 9
+ { "1017", "201C", }, // Double Open Quote (6)
+ { "1018", "201D", }, // Double Close Quote (9)
+ { "1019", "201E", }, // Double Baseline Quote (9)
+ { "1020", "2003", }, // Em Space
+ { "1021", "2002", }, // En Space
+ { "1023", "2009", }, // Thin Space
+ { "1028", "2026", }, // Ellipsis
+ { "1030", "EFF1", }, // Uppercase Ogonek (Spacing)
+ { "1031", "017E", }, // Lowercase Z Hacek
+ { "1034", "2120", }, // Service Mark
+ { "1036", "211E", }, // Prescription Sign
+//{ "1040", "F001", }, // Lowercase FI Ligature
+ { "1040", "FB01", }, // Lowercase FI Ligature
+//{ "1041", "F002", }, // Lowercase FL Ligature
+ { "1041", "FB02", }, // Lowercase FL Ligature
+ { "1042", "FB00", }, // Lowercase FF Ligature
+ { "1043", "FB03", }, // Lowercase FFI Ligature
+ { "1044", "FB04", }, // Lowercase FFL Ligature
+ { "1045", "EFF0", }, // Uppercase Double Acute Accent (Spacing)
+ { "1047", "0133", }, // Lowercase IJ Ligature
+ { "1060", "2105", }, // Care Of Symbol
+ { "1061", "011E", }, // Uppercase G Breve
+ { "1062", "011F", }, // Lowercase G Breve
+ { "1063", "015E", }, // Uppercase S Cedilla
+ { "1064", "015F", }, // Lowercase S Cedilla
+ { "1065", "0130", }, // Uppercase I Overdot
+ { "1067", "201A", }, // Single Baseline Quote (9)
+ { "1068", "2030", }, // Per Mill Sign
+ { "1069", "20AC", }, // Euro
+ { "1084", "02C9", }, // Lowercase Macron Accent (Spacing)
+ { "1086", "02D8", }, // Lowercase Breve Accent (Spacing)
+ { "1088", "02D9", }, // Lowercase Overdot Accent (Spacing)
+ { "1090", "0153", }, // Lowercase OE Ligature
+ { "1091", "0152", }, // Uppercase OE Ligature
+ { "1092", "2039", }, // Left Pointing Single Angle Quote
+ { "1093", "203A", }, // Right Pointing Single Angle Quote
+ { "1094", "25A1", }, // Medium Open Square Box
+ { "1095", "0141", }, // Uppercase L-Stroke
+ { "1096", "0142", }, // Lowercase L-Stroke
+ { "1097", "02DD", }, // Lowercase Double Acute Accent (Spacing)
+ { "1098", "02DB", }, // Lowercase Ogonek (Spacing)
+ { "1099", "21B5", }, // Carriage Return Symbol
+ { "1100", "EFDB", }, // Full Size Serif Registered
+ { "1101", "EFDA", }, // Full Size Serif Copyright
+ { "1102", "EFD9", }, // Full Size Serif Trademark
+ { "1103", "EFD8", }, // Full Size Sans Registered
+ { "1104", "EFD7", }, // Full Size Sans Copyright
+ { "1105", "EFD6", }, // Full Size Sans Trademark
+ { "1106", "017D", }, // Uppercase Z Hacek
+ { "1107", "0132", }, // Uppercase IJ Ligature
+ { "1108", "25AB", }, // Small Open Square Box
+ { "1109", "25E6", }, // Small Open Round Bullet
+ { "1110", "25CB", }, // Medium Open Round Bullet
+ { "1111", "EFFA", }, // Large Solid Round Bullet
+ { "3812", "F000", }, // Ornament, Apple
+};
+
+// global constructor
+static struct hp_msl_to_unicode_init {
+ hp_msl_to_unicode_init();
+} _hp_msl_to_unicode_init;
+
+hp_msl_to_unicode_init::hp_msl_to_unicode_init() {
+ for (unsigned int i = 0;
+ i < sizeof(hp_msl_to_unicode_list)/sizeof(hp_msl_to_unicode_list[0]);
+ i++) {
+ hp_msl_to_unicode *ptu = new hp_msl_to_unicode[1];
+ ptu->value = (char *)hp_msl_to_unicode_list[i].value;
+ hp_msl_to_unicode_table.define(hp_msl_to_unicode_list[i].key, ptu);
+ }
+}
+
+const char *hp_msl_to_unicode_code(const char *s)
+{
+ hp_msl_to_unicode *result = hp_msl_to_unicode_table.lookup(s);
+ return result ? result->value : 0;
+}
diff --git a/src/utils/indxbib/eign b/src/utils/indxbib/eign
new file mode 100644
index 0000000..7718c8b
--- /dev/null
+++ b/src/utils/indxbib/eign
@@ -0,0 +1,133 @@
+a
+i
+the
+to
+of
+and
+in
+is
+it
+for
+that
+if
+you
+this
+be
+on
+with
+not
+have
+are
+or
+as
+from
+can
+but
+by
+at
+an
+will
+no
+all
+was
+do
+there
+my
+one
+so
+we
+they
+what
+would
+any
+which
+about
+get
+your
+use
+some
+me
+then
+name
+like
+out
+when
+up
+time
+other
+more
+only
+just
+end
+also
+know
+how
+new
+should
+been
+than
+them
+he
+who
+make
+may
+people
+these
+now
+their
+here
+into
+first
+could
+way
+had
+see
+work
+well
+were
+two
+very
+where
+while
+us
+because
+good
+same
+even
+much
+most
+many
+such
+long
+his
+over
+last
+since
+right
+before
+our
+without
+too
+those
+why
+must
+part
+being
+current
+back
+still
+go
+point
+value
+each
+did
+both
+true
+off
+say
+another
+state
+might
+under
+start
+try
diff --git a/src/utils/indxbib/indxbib.1.man b/src/utils/indxbib/indxbib.1.man
new file mode 100644
index 0000000..df02fcc
--- /dev/null
+++ b/src/utils/indxbib/indxbib.1.man
@@ -0,0 +1,347 @@
+.TH @g@indxbib @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+@g@indxbib \- make inverted index for bibliographic databases
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_indxbib_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY @g@indxbib
+.RB [ \-w ]
+.RB [ \-c\~\c
+.IR \%common-words-file ]
+.RB [ \-d\~\c
+.IR dir ]
+.RB [ \-f\~\c
+.IR \%list-file ]
+.RB [ \-h\~\c
+.IR \%min-hash-table-size ]
+.RB [ \-i\~\c
+.IR \%excluded-fields ]
+.RB [ \-k\~\c
+.IR \%max-keys-per-record ]
+.RB [ \-l\~\c
+.IR \%min-key-length ]
+.RB [ \-n\~\c
+.IR \%threshold ]
+.RB [ \-o\~\c
+.IR file ]
+.RB [ \-t\~\c
+.IR \%max-key-length ]
+.RI [ file\~ .\|.\|.]
+.YS
+.
+.
+.SY @g@indxbib
+.B \-\-help
+.YS
+.
+.
+.SY @g@indxbib
+.B \-v
+.
+.SY @g@indxbib
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I @g@indxbib
+makes an inverted index for the bibliographic databases in each
+.I file
+for use with
+.MR @g@refer @MAN1EXT@ ,
+.MR @g@lookbib @MAN1EXT@ ,
+and
+.MR lkbib @MAN1EXT@ .
+.
+Each created index is named
+.RI file @INDEX_SUFFIX@ ;
+writing is done to a temporary file which is then renamed to this.
+.
+If no
+.I file
+operands are given on the command line because the
+.B \-f
+option has been used,
+and no
+.B \-o
+option is given,
+the index will be named
+.IR \%@DEFAULT_INDEX_NAME@@INDEX_SUFFIX@ .
+.
+.
+.LP
+Bibliographic databases are divided into records by blank lines.
+.
+Within a record,
+each field starts with a
+.B %
+character at the beginning of a line.
+.
+Fields have a one letter name that follows the
+.B %
+character.
+.
+.
+.LP
+The values set by the
+.BR \-c ,
+.BR \-l ,
+.BR \-n ,
+and
+.B \-t
+options are stored in the index:
+when the index is searched,
+keys will be discarded and truncated in a
+manner appropriate to these options;
+the original keys will be used for verifying that any record
+found using the index actually contains the keys.
+.
+This means that a user of an index need not know whether these
+options were used in the creation of the index,
+provided that not all the keys to be searched for
+would have been discarded during indexing
+and that the user supplies at least the part of each key
+that would have remained after being truncated during indexing.
+.
+The value set by the
+.B \-i
+option is also stored in the index
+and will be used in verifying records found using the index.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-c\~ common-words-file
+Read the list of common words from
+.I common-words-file
+instead of
+.IR \%@COMMON_WORDS_FILE@ .
+.
+.
+.TP
+.BI \-d\~ dir
+Use
+.I dir
+as the name of the directory to store in the index,
+instead of that returned by
+.MR getcwd 2 .
+.
+Typically,
+.I dir
+will be a symbolic link whose target is the current working directory.
+.
+.
+.TP
+.BI \-f\~ list-file
+Read the files to be indexed from
+.IR list-file .
+.
+If
+.I list-file
+is
+.BR \- ,
+files will be read from the standard input stream.
+.
+The
+.B \-f
+option can be given at most once.
+.
+.
+.TP
+.BI \-h\~ min-hash-table-size
+Use the first prime number greater than or equal to
+the argument for the size of the hash table.
+.
+Larger values
+will usually make searching faster,
+but will make the index file larger
+and cause
+.I @g@indxbib
+to use more memory.
+.
+The default hash table size is 997.
+.
+.
+.TP
+.BI \-i\~ excluded-fields
+Don't index the contents of fields whose names are in
+.IR excluded-fields .
+.
+Field names are one character each.
+.
+If this option is not present,
+.I @g@indxbib
+excludes fields
+.BR X ,
+.BR Y ,
+and
+.BR Z .
+.
+.
+.TP
+.BI \-k\~ max-keys-per-record
+Use no more keys per input record than specified in the argument.
+.
+If this option is not present,
+the maximum is 100.
+.
+.
+.TP
+.BI \-l\~ min-key-length
+Discard any key whose length in characters is shorter than the value of
+the argument.
+.
+If this option is not present,
+the minimum key length
+is 3.
+.
+.
+.TP
+.BI \-n\~ threshold
+Discard the
+.I threshold
+most common words from the common words file.
+.
+If this option is not present,
+the 100 most common words are discarded.
+.
+.
+.TP
+.BI \-o\~ basename
+Name the index
+.RI basename @INDEX_SUFFIX@ .
+.
+.
+.TP
+.BI \-t\~ max-key-length
+Truncate keys to
+.I max-key-length
+in characters.
+.
+If this option is not present,
+keys are truncated to 6 characters.
+.
+.
+.TP
+.B \-w
+Index whole files.
+.
+Each file is a separate record.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.RI \%file @INDEX_SUFFIX@
+index for
+.I file
+.
+.
+.TP
+.I \%@DEFAULT_INDEX_NAME@@INDEX_SUFFIX@
+default index name
+.
+.
+.TP
+.I \%@COMMON_WORDS_FILE@
+contains the list of common words.
+.
+The traditional name,
+.RI \[lq] eign \[rq],
+is an abbreviation of \[lq]English ignored [word list]\[rq].
+.
+.
+.TP
+.IR \%indxbib XXXXXX
+temporary file
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+\[lq]Some Applications of Inverted Indexes on the Unix System\[rq],
+by M.\& E.\& Lesk,
+1978,
+AT&T Bell Laboratories Computing Science Technical Report No.\& 69.
+.
+.
+.LP
+.MR @g@refer @MAN1EXT@ ,
+.MR lkbib @MAN1EXT@ ,
+.MR @g@lookbib @MAN1EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_indxbib_1_man_C]
+.do rr *groff_indxbib_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/indxbib/indxbib.am b/src/utils/indxbib/indxbib.am
new file mode 100644
index 0000000..d2a7d5a
--- /dev/null
+++ b/src/utils/indxbib/indxbib.am
@@ -0,0 +1,57 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+indxbib_srcdir = $(top_srcdir)/src/utils/indxbib
+prefixexecbin_PROGRAMS += indxbib
+indxbib_SOURCES = \
+ src/utils/indxbib/indxbib.cpp \
+ src/utils/indxbib/signal.c
+src/utils/indxbib/indxbib.$(OBJEXT): defs.h
+indxbib_LDADD = libbib.a libgroff.a $(LIBM) lib/libgnu.a
+PREFIXMAN1 += src/utils/indxbib/indxbib.1
+EXTRA_DIST += \
+ src/utils/indxbib/indxbib.1.man \
+ src/utils/indxbib/eign
+
+install-data-local: install_indxbib
+install_indxbib: $(indxbib_srcdir)/eign
+ -test -d $(DESTDIR)$(datadir) \
+ || $(mkinstalldirs) $(DESTDIR)$(datadir)
+ -test -d $(DESTDIR)$(dataprogramdir) \
+ || $(mkinstalldirs) $(DESTDIR)$(dataprogramdir)
+ -test -d $(DESTDIR)$(datasubdir) \
+ || $(mkinstalldirs) $(DESTDIR)$(datasubdir)
+ if test -f /usr/lib/eign; then \
+ rm -f $(DESTDIR)$(common_words_file); \
+ ln -s /usr/lib/eign $(DESTDIR)$(common_words_file) 2>/dev/null \
+ || ln /usr/lib/eign $(DESTDIR)$(common_words_file) 2>/dev/null \
+ || cp /usr/lib/eign $(DESTDIR)$(common_words_file); \
+ else \
+ rm -f $(DESTDIR)$(common_words_file); \
+ $(INSTALL_DATA) $(indxbib_srcdir)/eign $(DESTDIR)$(common_words_file); \
+ fi
+
+uninstall-local: uninstall_indxbib
+uninstall_indxbib:
+ rm -f $(DESTDIR)$(common_words_file)
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/indxbib/indxbib.cpp b/src/utils/indxbib/indxbib.cpp
new file mode 100644
index 0000000..ad8bb0e
--- /dev/null
+++ b/src/utils/indxbib/indxbib.cpp
@@ -0,0 +1,803 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "stringclass.h"
+#include "cset.h"
+#include "cmap.h"
+
+#include "defs.h"
+#include "index.h"
+
+#include "nonposix.h"
+
+extern "C" const char *Version_string;
+
+#define DEFAULT_HASH_TABLE_SIZE 997
+#define TEMP_INDEX_TEMPLATE "indxbibXXXXXX"
+
+// (2^n - MALLOC_OVERHEAD) should be a good argument for malloc().
+
+#define MALLOC_OVERHEAD 16
+
+#ifdef BLOCK_SIZE
+#undef BLOCK_SIZE
+#endif
+
+const int BLOCK_SIZE = ((1024 - MALLOC_OVERHEAD - sizeof(struct block *)
+ - sizeof(int)) / sizeof(int));
+struct block {
+ block *next;
+ int used;
+ int v[BLOCK_SIZE];
+
+ block(block *p = 0) : next(p), used(0) { }
+};
+
+struct block;
+
+union table_entry {
+ block *ptr;
+ int count;
+};
+
+struct word_list {
+ word_list *next;
+ char *str;
+ int len;
+ word_list(const char *, int, word_list *);
+};
+
+table_entry *hash_table;
+int hash_table_size = DEFAULT_HASH_TABLE_SIZE;
+// We make this the same size as hash_table so we only have to do one
+// mod per key.
+static word_list **common_words_table = 0;
+char *key_buffer;
+
+FILE *indxfp;
+int ntags = 0;
+string filenames;
+char *temp_index_file = 0;
+
+const char *ignore_fields = "XYZ";
+const char *common_words_file = COMMON_WORDS_FILE;
+int n_ignore_words = 100;
+int truncate_len = 6;
+int shortest_len = 3;
+int max_keys_per_item = 100;
+
+static void usage(FILE *stream);
+static void write_hash_table();
+static void init_hash_table();
+static void read_common_words_file();
+static int store_key(char *s, int len);
+static void possibly_store_key(char *s, int len);
+static int do_whole_file(const char *filename);
+static int do_file(const char *filename);
+static void store_reference(int filename_index, int pos, int len);
+static void check_integer_arg(char opt, const char *arg, int min, int *res);
+static void store_filename(const char *);
+static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp);
+static char *get_cwd();
+
+extern "C" {
+ void cleanup();
+ void catch_fatal_signals();
+ void ignore_fatal_signals();
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+
+ const char *base_name = 0;
+ typedef int (*parser_t)(const char *);
+ parser_t parser = do_file;
+ const char *directory = 0;
+ const char *foption = 0;
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw",
+ long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'c':
+ common_words_file = optarg;
+ break;
+ case 'd':
+ directory = optarg;
+ break;
+ case 'f':
+ foption = optarg;
+ break;
+ case 'h':
+ {
+ int requested_hash_table_size;
+ check_integer_arg('h', optarg, 1, &requested_hash_table_size);
+ hash_table_size = requested_hash_table_size;
+ if ((hash_table_size > 2) && (hash_table_size % 2) == 0)
+ hash_table_size++;
+ while (!is_prime(hash_table_size))
+ hash_table_size += 2;
+ if (hash_table_size != requested_hash_table_size)
+ warning("requested hash table size %1 is not prime: using %2"
+ " instead", optarg, hash_table_size);
+ }
+ break;
+ case 'i':
+ ignore_fields = optarg;
+ break;
+ case 'k':
+ check_integer_arg('k', optarg, 1, &max_keys_per_item);
+ break;
+ case 'l':
+ check_integer_arg('l', optarg, 0, &shortest_len);
+ break;
+ case 'n':
+ check_integer_arg('n', optarg, 0, &n_ignore_words);
+ break;
+ case 'o':
+ base_name = optarg;
+ break;
+ case 't':
+ check_integer_arg('t', optarg, 1, &truncate_len);
+ break;
+ case 'w':
+ parser = do_whole_file;
+ break;
+ case 'v':
+ printf("GNU indxbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (optind >= argc && foption == 0)
+ fatal("no files and no -f option");
+ if (!directory) {
+ char *path = get_cwd();
+ store_filename(path);
+ delete[] path;
+ }
+ else
+ store_filename(directory);
+ init_hash_table();
+ store_filename(common_words_file);
+ store_filename(ignore_fields);
+ key_buffer = new char[truncate_len];
+ read_common_words_file();
+ if (!base_name)
+ base_name = optind < argc ? argv[optind] : DEFAULT_INDEX_NAME;
+ const char *p = strrchr(base_name, DIR_SEPS[0]), *p1;
+ const char *sep = &DIR_SEPS[1];
+ while (*sep) {
+ p1 = strrchr(base_name, *sep);
+ if (p1 && (!p || p1 > p))
+ p = p1;
+ sep++;
+ }
+ size_t name_max;
+ if (p) {
+ char *dir = strsave(base_name);
+ dir[p - base_name] = '\0';
+ name_max = file_name_max(dir);
+ delete[] dir;
+ }
+ else
+ name_max = file_name_max(".");
+ const char *filename = p ? p + 1 : base_name;
+ if (strlen(filename) + sizeof(INDEX_SUFFIX) - 1 > name_max)
+ fatal("'%1.%2' is too long for a filename", filename, INDEX_SUFFIX);
+ if (p) {
+ p++;
+ temp_index_file = new char[p - base_name + sizeof(TEMP_INDEX_TEMPLATE)];
+ memcpy(temp_index_file, base_name, p - base_name);
+ strcpy(temp_index_file + (p - base_name), TEMP_INDEX_TEMPLATE);
+ }
+ else {
+ temp_index_file = strsave(TEMP_INDEX_TEMPLATE);
+ }
+ catch_fatal_signals();
+ int fd = mkstemp(temp_index_file);
+ if (fd < 0)
+ fatal("can't create temporary index file: %1", strerror(errno));
+ indxfp = fdopen(fd, FOPEN_WB);
+ if (indxfp == 0)
+ fatal("fdopen failed");
+ if (fseek(indxfp, sizeof(index_header), 0) < 0)
+ fatal("can't seek past index header: %1", strerror(errno));
+ int failed = 0;
+ if (foption) {
+ FILE *fp = stdin;
+ if (strcmp(foption, "-") != 0) {
+ errno = 0;
+ fp = fopen(foption, "r");
+ if (!fp)
+ fatal("can't open '%1': %2", foption, strerror(errno));
+ }
+ string path;
+ int lineno = 1;
+ for (;;) {
+ int c;
+ for (c = getc(fp); c != '\n' && c != EOF; c = getc(fp)) {
+ if (c == '\0')
+ error_with_file_and_line(foption, lineno,
+ "nul character in pathname ignored");
+ else
+ path += c;
+ }
+ if (path.length() > 0) {
+ path += '\0';
+ if (!(*parser)(path.contents()))
+ failed = 1;
+ path.clear();
+ }
+ if (c == EOF)
+ break;
+ lineno++;
+ }
+ if (fp != stdin)
+ fclose(fp);
+ }
+ for (int i = optind; i < argc; i++)
+ if (!(*parser)(argv[i]))
+ failed = 1;
+ write_hash_table();
+ if (fclose(indxfp) < 0)
+ fatal("error closing temporary index file: %1", strerror(errno));
+ char *index_file = new char[strlen(base_name) + sizeof(INDEX_SUFFIX)];
+ strcpy(index_file, base_name);
+ strcat(index_file, INDEX_SUFFIX);
+#ifdef HAVE_RENAME
+#ifdef __EMX__
+ if (access(index_file, R_OK) == 0)
+ unlink(index_file);
+#endif /* __EMX__ */
+ if (rename(temp_index_file, index_file) < 0) {
+#ifdef __MSDOS__
+ // RENAME could fail on plain MS-DOS filesystems because
+ // INDEX_FILE is an invalid filename, e.g. it has multiple dots.
+ char *fname = p ? index_file + (p - base_name) : 0;
+ char *dot = 0;
+
+ // Replace the dot with an underscore and try again.
+ if (fname
+ && (dot = strchr(fname, '.')) != 0
+ && strcmp(dot, INDEX_SUFFIX) != 0)
+ *dot = '_';
+ if (rename(temp_index_file, index_file) < 0)
+#endif
+ fatal("can't rename temporary index file: %1", strerror(errno));
+ }
+#else /* not HAVE_RENAME */
+ ignore_fatal_signals();
+ if (unlink(index_file) < 0) {
+ if (errno != ENOENT)
+ fatal("can't unlink '%1': %2", index_file, strerror(errno));
+ }
+ if (link(temp_index_file, index_file) < 0)
+ fatal("can't link temporary index file: %1", strerror(errno));
+ if (unlink(temp_index_file) < 0)
+ fatal("can't unlink temporary index file: %1", strerror(errno));
+#endif /* not HAVE_RENAME */
+ temp_index_file = 0;
+ return failed;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-w] [-c common-words-file] [-d dir] [-f list-file]"
+" [-h min-hash-table-size] [-i excluded-fields]"
+" [-k max-keys-per-record] [-l min-key-length]"
+" [-n threshold] [-o file] [-t max-key-length] [file ...]\n"
+"usage: %s {-v | --version}\n"
+"usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+static void check_integer_arg(char opt, const char *arg, int min, int *res)
+{
+ char *ptr;
+ long n = strtol(arg, &ptr, 10);
+ if (n == 0 && ptr == arg)
+ error("argument to -%1 not an integer", opt);
+ else if (n < min)
+ error("argument to -%1 must not be less than %2", opt, min);
+ else {
+ if (n > INT_MAX)
+ error("argument to -%1 greater than maximum integer", opt);
+ else if (*ptr != '\0')
+ error("junk after integer argument to -%1", opt);
+ *res = int(n);
+ }
+}
+
+static char *get_cwd()
+{
+ char *buf;
+ int size = 12;
+
+ for (;;) {
+ buf = new char[size];
+ if (getcwd(buf, size))
+ break;
+ if (errno != ERANGE)
+ fatal("cannot get current working directory: %1", strerror(errno));
+ delete[] buf;
+ if (size == INT_MAX)
+ fatal("current working directory longer than INT_MAX");
+ if (size > INT_MAX/2)
+ size = INT_MAX;
+ else
+ size *= 2;
+ }
+ return buf;
+}
+
+word_list::word_list(const char *s, int n, word_list *p)
+: next(p), len(n)
+{
+ str = new char[n];
+ memcpy(str, s, n);
+}
+
+static void read_common_words_file()
+{
+ if (n_ignore_words <= 0)
+ return;
+ errno = 0;
+ FILE *fp = fopen(common_words_file, "r");
+ if (!fp)
+ fatal("can't open '%1': %2", common_words_file, strerror(errno));
+ common_words_table = new word_list * [hash_table_size];
+ for (int i = 0; i < hash_table_size; i++)
+ common_words_table[i] = 0;
+ int count = 0;
+ int key_len = 0;
+ for (;;) {
+ int c = getc(fp);
+ while (c != EOF && !csalnum(c))
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ do {
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = cmlower(c);
+ c = getc(fp);
+ } while (c != EOF && csalnum(c));
+ if (key_len >= shortest_len) {
+ int h = hash(key_buffer, key_len) % hash_table_size;
+ common_words_table[h] = new word_list(key_buffer, key_len,
+ common_words_table[h]);
+ }
+ if (++count >= n_ignore_words)
+ break;
+ key_len = 0;
+ if (c == EOF)
+ break;
+ }
+ n_ignore_words = count;
+ fclose(fp);
+}
+
+static int do_whole_file(const char *filename)
+{
+ errno = 0;
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ error("can't open '%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ int count = 0;
+ int key_len = 0;
+ int c;
+ while ((c = getc(fp)) != EOF) {
+ if (csalnum(c)) {
+ key_len = 1;
+ key_buffer[0] = c;
+ while ((c = getc(fp)) != EOF) {
+ if (!csalnum(c))
+ break;
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = c;
+ }
+ if (store_key(key_buffer, key_len)) {
+ if (++count >= max_keys_per_item)
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ }
+ store_reference(filenames.length(), 0, 0);
+ store_filename(filename);
+ fclose(fp);
+ return 1;
+}
+
+static int do_file(const char *filename)
+{
+ errno = 0;
+ // Need binary I/O for MS-DOS/MS-Windows, because indxbib relies on
+ // byte counts to be consistent with fseek.
+ FILE *fp = fopen(filename, FOPEN_RB);
+ if (fp == 0) {
+ error("can't open '%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ int filename_index = filenames.length();
+ store_filename(filename);
+
+ enum {
+ START, // at the start of the file; also in between references
+ BOL, // in the middle of a reference, at the beginning of the line
+ PERCENT, // seen a percent at the beginning of the line
+ IGNORE, // ignoring a field
+ IGNORE_BOL, // at the beginning of a line ignoring a field
+ KEY, // in the middle of a key
+ DISCARD, // after truncate_len bytes of a key
+ MIDDLE // in between keys
+ } state = START;
+
+ // In states START, BOL, IGNORE_BOL, space_count how many spaces at
+ // the beginning have been seen. In states PERCENT, IGNORE, KEY,
+ // MIDDLE space_count must be 0.
+ int space_count = 0;
+ int byte_count = 0; // bytes read
+ int key_len = 0;
+ int ref_start = -1; // position of start of current reference
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ // We opened the file in binary mode, so we need to skip
+ // every CR character before a Newline.
+ if (c == '\r') {
+ int peek = getc(fp);
+ if (peek == '\n') {
+ byte_count++;
+ c = peek;
+ }
+ else
+ ungetc(peek, fp);
+ }
+#if defined(__MSDOS__) || defined(_MSC_VER) || defined(__EMX__)
+ else if (c == 0x1a) // ^Z means EOF in text files
+ break;
+#endif
+ byte_count++;
+ switch (state) {
+ case START:
+ if (c == ' ' || c == '\t') {
+ space_count++;
+ break;
+ }
+ if (c == '\n') {
+ space_count = 0;
+ break;
+ }
+ ref_start = byte_count - space_count - 1;
+ space_count = 0;
+ if (c == '%')
+ state = PERCENT;
+ else if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else
+ state = MIDDLE;
+ break;
+ case BOL:
+ switch (c) {
+ case '%':
+ if (space_count > 0) {
+ space_count = 0;
+ state = MIDDLE;
+ }
+ else
+ state = PERCENT;
+ break;
+ case ' ':
+ case '\t':
+ space_count++;
+ break;
+ case '\n':
+ store_reference(filename_index, ref_start,
+ byte_count - 1 - space_count - ref_start);
+ state = START;
+ space_count = 0;
+ break;
+ default:
+ space_count = 0;
+ if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else
+ state = MIDDLE;
+ }
+ break;
+ case PERCENT:
+ if (strchr(ignore_fields, c) != 0)
+ state = IGNORE;
+ else if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ break;
+ case IGNORE:
+ if (c == '\n')
+ state = IGNORE_BOL;
+ break;
+ case IGNORE_BOL:
+ switch (c) {
+ case '%':
+ if (space_count > 0) {
+ state = IGNORE;
+ space_count = 0;
+ }
+ else
+ state = PERCENT;
+ break;
+ case ' ':
+ case '\t':
+ space_count++;
+ break;
+ case '\n':
+ store_reference(filename_index, ref_start,
+ byte_count - 1 - space_count - ref_start);
+ state = START;
+ space_count = 0;
+ break;
+ default:
+ space_count = 0;
+ state = IGNORE;
+ }
+ break;
+ case KEY:
+ if (csalnum(c)) {
+ if (key_len < truncate_len)
+ key_buffer[key_len++] = c;
+ else
+ state = DISCARD;
+ }
+ else {
+ possibly_store_key(key_buffer, key_len);
+ key_len = 0;
+ if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ }
+ break;
+ case DISCARD:
+ if (!csalnum(c)) {
+ possibly_store_key(key_buffer, key_len);
+ key_len = 0;
+ if (c == '\n')
+ state = BOL;
+ else
+ state = MIDDLE;
+ }
+ break;
+ case MIDDLE:
+ if (csalnum(c)) {
+ state = KEY;
+ key_buffer[0] = c;
+ key_len = 1;
+ }
+ else if (c == '\n')
+ state = BOL;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ switch (state) {
+ case START:
+ break;
+ case DISCARD:
+ case KEY:
+ possibly_store_key(key_buffer, key_len);
+ // fall through
+ case BOL:
+ case PERCENT:
+ case IGNORE_BOL:
+ case IGNORE:
+ case MIDDLE:
+ store_reference(filename_index, ref_start,
+ byte_count - ref_start - space_count);
+ break;
+ default:
+ assert(0);
+ }
+ fclose(fp);
+ return 1;
+}
+
+static void store_reference(int filename_index, int pos, int len)
+{
+ tag t;
+ t.filename_index = filename_index;
+ t.start = pos;
+ t.length = len;
+ fwrite_or_die(&t, sizeof(t), 1, indxfp);
+ ntags++;
+}
+
+static void store_filename(const char *fn)
+{
+ filenames += fn;
+ filenames += '\0';
+}
+
+static void init_hash_table()
+{
+ hash_table = new table_entry[hash_table_size];
+ for (int i = 0; i < hash_table_size; i++)
+ hash_table[i].ptr = 0;
+}
+
+static void possibly_store_key(char *s, int len)
+{
+ static int last_tagno = -1;
+ static int key_count;
+ if (last_tagno != ntags) {
+ last_tagno = ntags;
+ key_count = 0;
+ }
+ if (key_count < max_keys_per_item) {
+ if (store_key(s, len))
+ key_count++;
+ }
+}
+
+static int store_key(char *s, int len)
+{
+ if (len < shortest_len)
+ return 0;
+ int is_number = 1;
+ for (int i = 0; i < len; i++)
+ if (!csdigit(s[i])) {
+ is_number = 0;
+ s[i] = cmlower(s[i]);
+ }
+ if (is_number && !(len == 4 && s[0] == '1' && s[1] == '9'))
+ return 0;
+ int h = hash(s, len) % hash_table_size;
+ if (common_words_table) {
+ for (word_list *ptr = common_words_table[h]; ptr; ptr = ptr->next)
+ if (len == ptr->len && memcmp(s, ptr->str, len) == 0)
+ return 0;
+ }
+ table_entry *pp = hash_table + h;
+ if (!pp->ptr)
+ pp->ptr = new block;
+ else if (pp->ptr->v[pp->ptr->used - 1] == ntags)
+ return 1;
+ else if (pp->ptr->used >= BLOCK_SIZE)
+ pp->ptr = new block(pp->ptr);
+ pp->ptr->v[(pp->ptr->used)++] = ntags;
+ return 1;
+}
+
+static void write_hash_table()
+{
+ const int minus_one = -1;
+ int li = 0;
+ for (int i = 0; i < hash_table_size; i++) {
+ block *ptr = hash_table[i].ptr;
+ if (!ptr)
+ hash_table[i].count = -1;
+ else {
+ hash_table[i].count = li;
+ block *rev = 0;
+ while (ptr) {
+ block *tem = ptr;
+ ptr = ptr->next;
+ tem->next = rev;
+ rev = tem;
+ }
+ while (rev) {
+ fwrite_or_die(rev->v, sizeof(int), rev->used, indxfp);
+ li += rev->used;
+ block *tem = rev;
+ rev = rev->next;
+ delete tem;
+ }
+ fwrite_or_die(&minus_one, sizeof(int), 1, indxfp);
+ li += 1;
+ }
+ }
+ if (sizeof(table_entry) == sizeof(int))
+ fwrite_or_die(hash_table, sizeof(int), hash_table_size, indxfp);
+ else {
+ // write it out word by word
+ for (int i = 0; i < hash_table_size; i++)
+ fwrite_or_die(&hash_table[i].count, sizeof(int), 1, indxfp);
+ }
+ fwrite_or_die(filenames.contents(), 1, filenames.length(), indxfp);
+ if (fseek(indxfp, 0, 0) < 0)
+ fatal("error seeking on index file: %1", strerror(errno));
+ index_header h;
+ h.magic = INDEX_MAGIC;
+ h.version = INDEX_VERSION;
+ h.tags_size = ntags;
+ h.lists_size = li;
+ h.table_size = hash_table_size;
+ h.strings_size = filenames.length();
+ h.truncate = truncate_len;
+ h.shortest = shortest_len;
+ h.common = n_ignore_words;
+ fwrite_or_die(&h, sizeof(h), 1, indxfp);
+}
+
+static void fwrite_or_die(const void *ptr, int size, int nitems, FILE *fp)
+{
+ if (fwrite(ptr, size, nitems, fp) != (size_t)nitems)
+ fatal("fwrite failed: %1", strerror(errno));
+}
+
+void fatal_error_exit()
+{
+ cleanup();
+ exit(3);
+}
+
+extern "C" {
+
+void cleanup()
+{
+ if (temp_index_file)
+ unlink(temp_index_file);
+}
+
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/indxbib/signal.c b/src/utils/indxbib/signal.c
new file mode 100644
index 0000000..2231b64
--- /dev/null
+++ b/src/utils/indxbib/signal.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 1992-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Unfortunately vendors seem to have problems writing a <signal.h>
+that is correct for C++, so we implement all signal handling in C. */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prototype */
+void catch_fatal_signals(void);
+
+extern void cleanup(void);
+
+static RETSIGTYPE handle_fatal_signal(int signum)
+{
+ signal(signum, SIG_DFL);
+ cleanup();
+#ifdef HAVE_KILL
+ kill(getpid(), signum);
+#else
+ /* MS-DOS and Win32 don't have kill(); the best compromise is
+ probably to use exit() instead. */
+ exit(signum);
+#endif
+}
+
+void catch_fatal_signals(void)
+{
+#ifdef SIGHUP
+ signal(SIGHUP, handle_fatal_signal);
+#endif
+ signal(SIGINT, handle_fatal_signal);
+ signal(SIGTERM, handle_fatal_signal);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef HAVE_RENAME
+
+void ignore_fatal_signals()
+{
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+}
+
+#endif /* not HAVE_RENAME */
diff --git a/src/utils/lkbib/lkbib.1.man b/src/utils/lkbib/lkbib.1.man
new file mode 100644
index 0000000..59ef19f
--- /dev/null
+++ b/src/utils/lkbib/lkbib.1.man
@@ -0,0 +1,212 @@
+.TH lkbib @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+lkbib \- search bibliographic databases
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_lkbib_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY lkbib
+.RB [ \-n ]
+.RB [ \-i\~\c
+.IR fields ]
+.RB [ \-p\~\c
+.IR file ]
+\&.\|.\|.\&
+.RB [ \-t\~\c
+.IR n ]
+.I key
+\&.\|.\|.
+.YS
+.
+.
+.SY lkbib
+.B \-\-help
+.YS
+.
+.
+.SY lkbib
+.B \-v
+.
+.SY lkbib
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I \%lkbib
+searches bibliographic databases for references containing keywords
+.I key
+and writes any references found to the standard output
+stream.
+.
+It reads databases given by
+.B \-p
+options
+and then
+(unless
+.B \-n
+is given)
+a default database.
+.
+The default database is taken from the
+.I \%REFER
+environment variable if it is set,
+otherwise it is
+.IR @DEFAULT_INDEX@ .
+.
+For each database
+.I file
+to be searched,
+if an index
+.RI file @INDEX_SUFFIX@
+created by
+.MR @g@indxbib @MAN1EXT@
+exists,
+then it will be searched instead;
+each index can cover multiple databases.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-i\~ string
+When searching files for which no index exists,
+ignore the contents of fields whose names are in
+.IR string .
+.
+.
+.TP
+.B \-n
+Suppress search of default database.
+.
+.
+.TP
+.BI \-p\~ file
+Search
+.IR file .
+.
+Multiple
+.B \-p
+options can be used.
+.
+.
+.TP
+.BI \-t\~ n
+Require only the first
+.I n
+characters of keys to be given.
+.
+The default
+is\~6.
+.
+.
+.\" ====================================================================
+.SH Environment
+.\" ====================================================================
+.
+.TP
+.I REFER
+Default database.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.I \%@DEFAULT_INDEX@
+Default database to be used if the
+.I \%REFER
+environment variable is not set.
+.
+.
+.TP
+.RI file @INDEX_SUFFIX@
+Index files.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+\[lq]Some Applications of Inverted Indexes on the Unix System\[rq],
+by M.\& E.\& Lesk,
+1978,
+AT&T Bell Laboratories Computing Science Technical Report No.\& 69.
+.
+.
+.LP
+.MR @g@refer @MAN1EXT@ ,
+.MR @g@lookbib @MAN1EXT@ ,
+.MR @g@indxbib @MAN1EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_lkbib_1_man_C]
+.do rr *groff_lkbib_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/lkbib/lkbib.am b/src/utils/lkbib/lkbib.am
new file mode 100644
index 0000000..5f75596
--- /dev/null
+++ b/src/utils/lkbib/lkbib.am
@@ -0,0 +1,30 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+bin_PROGRAMS += lkbib
+man1_MANS += src/utils/lkbib/lkbib.1
+EXTRA_DIST += src/utils/lkbib/lkbib.1.man
+lkbib_LDADD = libbib.a libgroff.a $(LIBM) lib/libgnu.a
+lkbib_SOURCES = src/utils/lkbib/lkbib.cpp
+src/utils/lkbib/lkbib.$(OBJEXT): defs.h
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/lkbib/lkbib.cpp b/src/utils/lkbib/lkbib.cpp
new file mode 100644
index 0000000..946bd7d
--- /dev/null
+++ b/src/utils/lkbib/lkbib.cpp
@@ -0,0 +1,144 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "errarg.h"
+#include "error.h"
+
+#include "defs.h"
+#include "refid.h"
+#include "search.h"
+
+extern "C" const char *Version_string;
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-n] [-p database] [-i XYZ] [-t N] key ...\n"
+ "usage: %s {-v | --version}\n"
+ "usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int search_default = 1;
+ search_list list;
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "nvVi:t:p:", long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'V':
+ do_verify = true;
+ break;
+ case 'n':
+ search_default = 0;
+ break;
+ case 'i':
+ linear_ignore_fields = optarg;
+ break;
+ case 't':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg) {
+ error("bad integer '%1' in 't' option", optarg);
+ break;
+ }
+ if (n < 1)
+ n = 1;
+ linear_truncate_len = int(n);
+ break;
+ }
+ case 'v':
+ {
+ printf("GNU lkbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case 'p':
+ list.add_file(optarg);
+ break;
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc) {
+ usage(stderr);
+ exit(1);
+ }
+ char *filename = getenv("REFER");
+ if (filename)
+ list.add_file(filename);
+ else if (search_default)
+ list.add_file(DEFAULT_INDEX, 1);
+ if (list.nfiles() == 0)
+ fatal("no databases");
+ int total_len = 0;
+ int i;
+ for (i = optind; i < argc; i++)
+ total_len += strlen(argv[i]);
+ total_len += argc - optind - 1 + 1; // for spaces and '\0'
+ char *buffer = new char[total_len];
+ char *ptr = buffer;
+ for (i = optind; i < argc; i++) {
+ if (i > optind)
+ *ptr++ = ' ';
+ strcpy(ptr, argv[i]);
+ ptr = strchr(ptr, '\0');
+ }
+ search_list_iterator iter(&list, buffer);
+ const char *start;
+ int len;
+ int count;
+ for (count = 0; iter.next(&start, &len); count++) {
+ if (fwrite(start, 1, len, stdout) != (size_t)len)
+ fatal("write error on stdout: %1", strerror(errno));
+ // Can happen for last reference in file.
+ if (start[len - 1] != '\n')
+ putchar('\n');
+ putchar('\n');
+ }
+ return !count;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/lookbib/lookbib.1.man b/src/utils/lookbib/lookbib.1.man
new file mode 100644
index 0000000..5d43bbb
--- /dev/null
+++ b/src/utils/lookbib/lookbib.1.man
@@ -0,0 +1,166 @@
+.TH @g@lookbib @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+@g@lookbib \- search bibliographic databases
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_lookbib_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY @g@lookbib
+.RB [ \-i\~\c
+.IR string ]
+.RB [ \-t\~\c
+.IR n ]
+.I file
+\&.\|.\|.\&
+.YS
+.
+.
+.SY @g@lookbib
+.B \-\-help
+.YS
+.
+.
+.SY @g@lookbib
+.B \-v
+.
+.SY @g@lookbib
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I @g@lookbib
+writes a prompt to the standard error stream
+(unless the standard input stream is not
+a terminal),
+reads from the standard input a line containing a set of keywords,
+searches each bibliographic database
+.I file
+for references containing those keywords,
+writes any references found to the standard output stream,
+and repeats this process until the end of input.
+.
+For each database
+.I file
+to be searched,
+if an index
+.RI file @INDEX_SUFFIX@
+created by
+.MR @g@indxbib @MAN1EXT@
+exists,
+then it will be searched instead;
+each index can cover multiple databases.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-i\~ string
+When searching files for which no index exists,
+ignore the contents of fields whose names are in
+.IR string .
+.
+.
+.TP
+.BI \-t\~ n
+Require only the first
+.I n
+characters of keys to be given.
+.
+The default
+is\~6.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.RI file @INDEX_SUFFIX@
+Index files.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+\[lq]Some Applications of Inverted Indexes on the Unix System\[rq],
+by M.\& E.\& Lesk,
+1978,
+AT&T Bell Laboratories Computing Science Technical Report No.\& 69.
+.
+.
+.LP
+.MR @g@refer @MAN1EXT@ ,
+.MR lkbib @MAN1EXT@ ,
+.MR @g@indxbib @MAN1EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_lookbib_1_man_C]
+.do rr *groff_lookbib_1_man_C
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/lookbib/lookbib.am b/src/utils/lookbib/lookbib.am
new file mode 100644
index 0000000..75103c1
--- /dev/null
+++ b/src/utils/lookbib/lookbib.am
@@ -0,0 +1,29 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+prefixexecbin_PROGRAMS += lookbib
+PREFIXMAN1 += src/utils/lookbib/lookbib.1
+EXTRA_DIST += src/utils/lookbib/lookbib.1.man
+lookbib_LDADD = libbib.a libgroff.a $(LIBM) lib/libgnu.a
+lookbib_SOURCES = src/utils/lookbib/lookbib.cpp
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/lookbib/lookbib.cpp b/src/utils/lookbib/lookbib.cpp
new file mode 100644
index 0000000..d8556c6
--- /dev/null
+++ b/src/utils/lookbib/lookbib.cpp
@@ -0,0 +1,146 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+
+#include "refid.h"
+#include "search.h"
+
+/* for isatty() */
+#include "posix.h"
+#include "nonposix.h"
+
+extern "C" const char *Version_string;
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-i XYZ] [-t N] database ...\n"
+ "usage: %s {-v | --version}\n"
+ "usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ static char stderr_buf[BUFSIZ];
+ setbuf(stderr, stderr_buf);
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "vVi:t:", long_options, NULL)) != EOF)
+ switch (opt) {
+ case 'V':
+ do_verify = true;
+ break;
+ case 'i':
+ linear_ignore_fields = optarg;
+ break;
+ case 't':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 10);
+ if (n == 0 && ptr == optarg) {
+ error("bad integer '%1' in 't' option", optarg);
+ break;
+ }
+ if (n < 1)
+ n = 1;
+ linear_truncate_len = int(n);
+ break;
+ }
+ case 'v':
+ {
+ printf("GNU lookbib (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ default:
+ assert(0);
+ }
+ if (optind >= argc) {
+ usage(stderr);
+ exit(1);
+ }
+ search_list list;
+ for (int i = optind; i < argc; i++)
+ list.add_file(argv[i]);
+ if (list.nfiles() == 0)
+ fatal("no databases");
+ char line[1024];
+ int interactive = isatty(fileno(stdin));
+ for (;;) {
+ if (interactive) {
+ fputs("> ", stderr);
+ fflush(stderr);
+ }
+ if (!fgets(line, sizeof(line), stdin))
+ break;
+ char *ptr = line;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0')
+ continue;
+ search_list_iterator iter(&list, line);
+ const char *start;
+ int len;
+ int count;
+ for (count = 0; iter.next(&start, &len); count++) {
+ if (fwrite(start, 1, len, stdout) != (size_t)len)
+ fatal("write error on stdout: %1", strerror(errno));
+ // Can happen for last reference in file.
+ if (start[len - 1] != '\n')
+ putchar('\n');
+ putchar('\n');
+ }
+ fflush(stdout);
+ if (interactive) {
+ fprintf(stderr, "%d found\n", count);
+ fflush(stderr);
+ }
+ }
+ if (interactive)
+ putc('\n', stderr);
+ return 0;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/pfbtops/pfbtops.1.man b/src/utils/pfbtops/pfbtops.1.man
new file mode 100644
index 0000000..71140c6
--- /dev/null
+++ b/src/utils/pfbtops/pfbtops.1.man
@@ -0,0 +1,129 @@
+.TH pfbtops @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+pfbtops \- translate PostScript Printer Font Binary files to Printer
+Font ASCII
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_pfbtops_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY pfbtops
+.RI [ pfb-file ]
+.YS
+.
+.
+.SY pfbtops
+.B \-\-help
+.YS
+.
+.
+.SY pfbtops
+.B \-v
+.
+.SY pfbtops
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I pfbtops
+translates a PostScript Type\~1 font in Printer Font Binary (PFB) format
+to Printer Font ASCII (PFA) format,
+splitting overlong lines in text packets into smaller chunks.
+.
+If
+.I pfb-file
+is omitted,
+the PFB file will be read from the standard input stream.
+.
+The PFA font will be written on the standard output stream.
+.
+PostScript fonts for MS-DOS were historically supplied in PFB format.
+.
+Use of a PostScript Type\~1 font with
+.I groff
+requires conversion of its metrics
+(AFM file)
+to a
+.I groff
+font description file;
+see
+.MR afmtodit @MAN1EXT@ .
+.
+.
+.P
+The
+.B \-\-help
+option displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR grops @MAN1EXT@ ,
+.MR gropdf @MAN1EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_pfbtops_1_man_C]
+.do rr *groff_pfbtops_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/pfbtops/pfbtops.am b/src/utils/pfbtops/pfbtops.am
new file mode 100644
index 0000000..8b7fd71
--- /dev/null
+++ b/src/utils/pfbtops/pfbtops.am
@@ -0,0 +1,32 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+bin_PROGRAMS += pfbtops
+man1_MANS += src/utils/pfbtops/pfbtops.1
+EXTRA_DIST += src/utils/pfbtops/pfbtops.1.man
+pfbtops_SOURCES = src/utils/pfbtops/pfbtops.c
+pfbtops_LDADD = libgroff.a $(LIBM) lib/libgnu.a
+# We use the following trick to force the use of C++ compiler
+# See the Automake manual, "Libtool Convenience Libraries"
+nodist_EXTRA_pfbtops_SOURCES = src/utils/pfbtops/dummy.cpp
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/pfbtops/pfbtops.c b/src/utils/pfbtops/pfbtops.c
new file mode 100644
index 0000000..8fbe44a
--- /dev/null
+++ b/src/utils/pfbtops/pfbtops.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 1992-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This translates ps fonts in .pfb format to ASCII ps files. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define __GETOPT_PREFIX groff_
+
+#include <errno.h> // errno
+#include <stdio.h>
+#include <stdlib.h> // exit(), EXIT_FAILURE, EXIT_SUCCESS
+#include <string.h> // strerror()
+#include <limits.h>
+
+#include <getopt.h>
+
+#include "nonposix.h"
+
+/* Binary bytes per output line. */
+#define BYTES_PER_LINE (64/2)
+#define MAX_LINE_LENGTH 78
+#define HEX_DIGITS "0123456789abcdef"
+
+extern const char *Version_string;
+
+static char *program_name;
+
+static void error(const char *s)
+{
+ fprintf(stderr, "%s: error: %s\n", program_name, s);
+ exit(EXIT_FAILURE);
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream, "usage: %s [pfb-file]\n"
+ "usage: %s {-v | --version}\n"
+ "usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+static void get_text(int n)
+{
+ int c = 0, c1;
+ int in_string = 0;
+ int is_comment = 0;
+ int count = 0;
+
+ while (--n >= 0) {
+ c = getchar();
+ if (c == '(' && !is_comment)
+ in_string++;
+ else if (c == ')' && !is_comment)
+ in_string--;
+ else if (c == '%' && !in_string)
+ is_comment = 1;
+ else if (c == '\\' && in_string) {
+ count++;
+ putchar(c);
+ if (n-- == 0)
+ break;
+ c = getchar();
+ /* don't split octal character representations */
+ if (c >= '0' && c <= '7') {
+ count++;
+ putchar(c);
+ if (n-- == 0)
+ break;
+ c = getchar();
+ if (c >= '0' && c <= '7') {
+ count++;
+ putchar(c);
+ if (n-- == 0)
+ break;
+ c = getchar();
+ if (c >= '0' && c <= '7') {
+ count++;
+ putchar(c);
+ if (n-- == 0)
+ break;
+ c = getchar();
+ }
+ }
+ }
+ }
+ if (c == EOF)
+ error("end of file in text packet");
+ else if (c == '\r') {
+ if (n-- == 0)
+ break;
+ c1 = getchar();
+ if (c1 != '\n') {
+ ungetc(c1, stdin);
+ n++;
+ }
+ c = '\n';
+ }
+ if (c == '\n') {
+ count = 0;
+ is_comment = 0;
+ }
+ else if (count >= MAX_LINE_LENGTH) {
+ if (in_string > 0) {
+ count = 1;
+ putchar('\\');
+ putchar('\n');
+ }
+ else if (is_comment) {
+ count = 2;
+ putchar('\n');
+ putchar('%');
+ }
+ else {
+ /* split at the next whitespace character */
+ while (c != ' ' && c != '\t' && c != '\f') {
+ putchar(c);
+ if (n-- == 0)
+ break;
+ c = getchar();
+ }
+ count = 0;
+ putchar('\n');
+ continue;
+ }
+ }
+ count++;
+ putchar(c);
+ }
+ if (c != '\n')
+ putchar('\n');
+}
+
+static void get_binary(int n)
+{
+ int c;
+ int count = 0;
+
+ while (--n >= 0) {
+ c = getchar();
+ if (c == EOF)
+ error("end of file in binary packet");
+ if (count >= BYTES_PER_LINE) {
+ putchar('\n');
+ count = 0;
+ }
+ count++;
+ putchar(HEX_DIGITS[(c >> 4) & 0xf]);
+ putchar(HEX_DIGITS[c & 0xf]);
+ }
+ putchar('\n');
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ program_name = argv[0];
+
+ while ((opt = getopt_long(argc, argv, "v", long_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'v':
+ printf("GNU pfbtops (groff) version %s\n", Version_string);
+ exit(EXIT_SUCCESS);
+ break;
+ case CHAR_MAX + 1: /* --help */
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?':
+ usage(stderr);
+ exit(2);
+ break;
+ }
+ }
+
+ if (argc - optind > 1) {
+ usage(stderr);
+ exit(2);
+ }
+ const char *file = argv[optind];
+ if (argc > optind && !freopen(file, "r", stdin)) {
+ fprintf(stderr, "%s: error: unable to open file '%s': %s\n",
+ program_name, file, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ SET_BINARY(fileno(stdin));
+ for (;;) {
+ int type, c, i;
+ long n;
+
+ c = getchar();
+ if (c != 0x80)
+ error("first byte of packet not 0x80");
+ type = getchar();
+ if (type == 3)
+ break;
+ if (type != 1 && type != 2)
+ error("bad packet type");
+ n = 0;
+ for (i = 0; i < 4; i++) {
+ c = getchar();
+ if (c == EOF)
+ error("end of file in packet header");
+ n |= (long)c << (i << 3);
+ }
+ if (n < 0)
+ error("negative packet length");
+ if (type == 1)
+ get_text(n);
+ else
+ get_binary(n);
+ }
+ exit(EXIT_SUCCESS);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/tfmtodit/tfmtodit.1.man b/src/utils/tfmtodit/tfmtodit.1.man
new file mode 100644
index 0000000..0f21753
--- /dev/null
+++ b/src/utils/tfmtodit/tfmtodit.1.man
@@ -0,0 +1,415 @@
+.TH tfmtodit @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+tfmtodit \- adapt TeX Font Metrics files for use with
+.I groff
+and
+.I grodvi
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 1989-2020 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_tfmtodit_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.\" Definitions
+.\" ====================================================================
+.
+.ie t .ds tx T\h'-.1667m'\v'.224m'E\v'-.224m'\h'-.125m'X
+.el .ds tx TeX
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY tfmtodit
+.RB [ \-s ]
+.RB [ \-g\~\c
+.IR gf-file ]
+.RB [ \-k\~\c
+.IR skew-char ]
+.I tfm-file
+.I map-file
+.I font-description
+.YS
+.
+.
+.SY tfmtodit
+.B \-\-help
+.YS
+.
+.
+.SY tfmtodit
+.B \-v
+.
+.SY tfmtodit
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I tfmtodit
+creates a font description file for use with
+.MR groff @MAN1EXT@ 's
+.B dvi
+output device.
+.
+.I tfm-file
+is the name of the \*(tx font metric file for the font.
+.
+.I map-file
+assigns
+.I groff
+ordinary or special character identifiers to glyph indices in the font;
+it should consist of a sequence of lines of the form
+.
+.RS
+.IR "i c1" \~\&.\|.\|.\&\~ cn
+.RE
+.
+where
+.I i
+is a position of the glyph in the font in decimal,
+and
+.I c1
+through
+.I cn
+are glyph identifiers in the form used by
+.I groff
+font descriptions.
+.
+If a glyph has no
+.I groff
+names but exists in
+.I tfm-file,
+it is put in the
+.I groff
+font description file as an unnamed glyph.
+.
+Output is written in
+.MR groff_font @MAN5EXT@
+format to
+.I font-description,
+a file named for the intended
+.I groff
+font name.
+.
+.
+.P
+If the font is \[lq]special\[rq],
+meaning that
+.I groff
+should search it whenever a glyph is not found in the current font,
+use the
+.B \-s
+option and name
+.I font-description
+in the
+.B fonts
+directive in the output device's
+.I DESC
+file.
+.
+.
+.P
+To do a good job of math typesetting,
+.I groff
+requires font metric information not present in
+.I tfm-file.
+.
+This is because \*(tx has separate math italic fonts,
+whereas
+.I groff
+uses normal italic fonts for math.
+.
+The additional information required by
+.I groff
+is given by the two arguments to the
+.B math_fit
+macro in the Metafont programs for the Computer Modern fonts.
+.
+In a text font (a font for which
+.B math_fit
+is false),
+Metafont normally ignores these two arguments.
+.
+Metafont can be made to put this information into the GF
+(\[lq]generic font\[rq])
+files it produces by loading the following definition after
+.B cmbase
+when creating
+.IR cm.base .
+.
+.RS
+.EX
+def ignore_math_fit(expr left_adjustment,right_adjustment) =
+ special "adjustment";
+ numspecial left_adjustment*16/designsize;
+ numspecial right_adjustment*16/designsize;
+ enddef;
+.EE
+.RE
+.
+For the EC font family,
+load the following definition after
+.BR exbase ;
+consider patching
+.I exbase.mf
+locally.
+.
+.RS
+.EX
+def ignore_math_fit(expr left_adjustment,right_adjustment) =
+ ori_special "adjustment";
+ ori_numspecial left_adjustment*16/designsize;
+ ori_numspecial right_adjustment*16/designsize;
+ enddef;
+.EE
+.RE
+.
+The only difference from the previous example is the \[lq]ori_\[rq]
+prefix to \[lq]special\[rq] and \[lq]numspecial\[rq].
+.
+The GF file created using this modified
+.I cm.base
+or
+.I exbase.mf
+should be specified with the
+.B \-g
+option,
+which should
+.I not
+be given for a font for which
+.B math_fit
+is true.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-g \~gf-file
+Use the
+.I gf-file
+produced by Metafont containing
+.RB \[lq] special \[rq]
+and
+.RB \[lq] numspecial \[rq]
+commands to obtain additional font metric information.
+.
+.
+.TP
+.BI \-k \~skew-char
+The skew character of this font is at position
+.I skew-char.
+.
+.I skew-char
+should be an integer;
+it may be given in decimal,
+with a leading 0 in octal,
+or with a leading 0x in hexadecimal.
+.
+Any kerns whose second component is
+.I skew-char
+are ignored.
+.
+.
+.TP
+.B \-s
+Add the
+.B special
+directive to the font description file.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.I @FONTDIR@/\:\%devdvi/\:DESC
+describes the
+.B dvi
+output device.
+.
+.
+.TP
+.IR @FONTDIR@/\:\%devdvi/ F
+describes the font known
+.RI as\~ F
+on device
+.BR dvi .
+.
+.
+.TP
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%ec.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%msam.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%msbm.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%tc.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texb.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texex.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texi.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texitt.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texmi.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texr.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%texsy.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%textex.map
+.TQ
+.I @FONTDIR@/\:\%devdvi/\:\%generate/\:\%textt.map
+map glyph indices in \*[tx] fonts to
+.I groff
+ordinary and special character identifiers.
+.
+.I \%ec.map
+is used for
+.BR TREC ,
+.BR TIEC ,
+.BR TBEC ,
+.BR TBIEC ,
+.BR HREC ,
+.BR HIEC ,
+.BR HBEC ,
+.BR HBIEC ,
+.BR CWEC ,
+and
+.BR CWIEC ;
+.I \%msam.map
+for
+.BR SA ;
+.I \%msbm.map
+for
+.BR SB ;
+.I \%tc.map
+for
+.BR TRTC ,
+.BR TITC ,
+.BR TBTC ,
+.BR TBITC ,
+.BR HRTC ,
+.BR HITC ,
+.BR HBTC ,
+.BR HBITC ,
+.BR CWTC ,
+and
+.BR CWITC ;
+.I \%texb.map
+for
+.BR TB ,
+.BR HR ,
+.BR HI ,
+.BR HB ,
+and
+.BR HBI ;
+.I \%texex.map
+for
+.BR EX ;
+.I \%texi.map
+for
+.B TI
+and
+.BR TBI ;
+.I \%texitt.map
+for
+.BR CWI ;
+.I \%texmi.map
+for
+.BR MI ;
+.I \%texr.map
+for
+.BR TR ;
+.I \%texsy.map
+for
+.BR S ;
+.I \%textex.map
+for
+.BR SC ;
+and
+.I \%textt.map
+for
+.BR CW .
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.MR groff @MAN1EXT@ ,
+.MR grodvi @MAN1EXT@ ,
+.MR groff_font @MAN5EXT@
+.
+.
+.\" Clean up.
+.rm tx
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_tfmtodit_1_man_C]
+.do rr *groff_tfmtodit_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/tfmtodit/tfmtodit.am b/src/utils/tfmtodit/tfmtodit.am
new file mode 100644
index 0000000..758fad5
--- /dev/null
+++ b/src/utils/tfmtodit/tfmtodit.am
@@ -0,0 +1,29 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+bin_PROGRAMS += tfmtodit
+man1_MANS += src/utils/tfmtodit/tfmtodit.1
+EXTRA_DIST += src/utils/tfmtodit/tfmtodit.1.man
+tfmtodit_SOURCES = src/utils/tfmtodit/tfmtodit.cpp
+tfmtodit_LDADD = libgroff.a $(LIBM) lib/libgnu.a
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/tfmtodit/tfmtodit.cpp b/src/utils/tfmtodit/tfmtodit.cpp
new file mode 100644
index 0000000..3003733
--- /dev/null
+++ b/src/utils/tfmtodit/tfmtodit.cpp
@@ -0,0 +1,889 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
+but I haven't tested them. */
+
+/* Groff requires more font metric information than TeX. The reason
+for this is that TeX has separate Math Italic fonts, whereas groff
+uses normal italic fonts for math. The two additional pieces of
+information required by groff correspond to the two arguments to the
+math_fit() macro in the Metafont programs for the CM fonts. In the
+case of a font for which math_fitting is false, these two arguments
+are normally ignored by Metafont. We need to get hold of these two
+parameters and put them in the groff font file.
+
+We do this by loading this definition after cmbase when creating
+cm.base.
+
+def ignore_math_fit(expr left_adjustment,right_adjustment) =
+ special "adjustment";
+ numspecial left_adjustment*16/designsize;
+ numspecial right_adjustment*16/designsize;
+ enddef;
+
+This puts the two arguments to the math_fit macro into the gf file.
+(They will appear in the gf file immediately before the character to
+which they apply.) We then create a gf file using this cm.base. Then
+we run tfmtodit and specify this gf file with the -g option.
+
+This need only be done for a font for which math_fitting is false;
+When it's true, the left_correction and subscript_correction should
+both be zero. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "nonposix.h"
+
+extern "C" const char *Version_string;
+
+/* Values in the tfm file should be multiplied by this. */
+
+#define MULTIPLIER 1
+
+struct char_info_word {
+ unsigned char width_index;
+ unsigned char height_index;
+ unsigned char depth_index;
+ unsigned char italic_index;
+ unsigned char tag;
+ unsigned char remainder;
+};
+
+struct lig_kern_command {
+ unsigned char skip_byte;
+ unsigned char next_char;
+ unsigned char op_byte;
+ unsigned char remainder;
+};
+
+class tfm {
+ int bc;
+ int ec;
+ int nw;
+ int nh;
+ int nd;
+ int ni;
+ int nl;
+ int nk;
+ int np;
+ int cs;
+ int ds;
+ char_info_word *char_info;
+ int *width;
+ int *height;
+ int *depth;
+ int *italic;
+ lig_kern_command *lig_kern;
+ int *kern;
+ int *param;
+public:
+ tfm();
+ ~tfm();
+ int load(const char *);
+ int contains(int);
+ int get_width(int);
+ int get_height(int);
+ int get_depth(int);
+ int get_italic(int);
+ int get_param(int, int *);
+ int get_checksum();
+ int get_design_size();
+ int get_lig(unsigned char, unsigned char, unsigned char *);
+ friend class kern_iterator;
+};
+
+class kern_iterator {
+ tfm *t;
+ int c;
+ int i;
+public:
+ kern_iterator(tfm *);
+ int next(unsigned char *c1, unsigned char *c2, int *k);
+};
+
+
+kern_iterator::kern_iterator(tfm *p)
+: t(p), c(t->bc), i(-1)
+{
+}
+
+int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
+{
+ for (; c <= t->ec; c++)
+ if (t->char_info[c - t->bc].tag == 1) {
+ if (i < 0) {
+ i = t->char_info[c - t->bc].remainder;
+ if (t->lig_kern[i].skip_byte > 128)
+ i = (256*t->lig_kern[i].op_byte
+ + t->lig_kern[i].remainder);
+ }
+ for (;;) {
+ int skip = t->lig_kern[i].skip_byte;
+ if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
+ *c1 = c;
+ *c2 = t->lig_kern[i].next_char;
+ *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
+ + t->lig_kern[i].remainder];
+ if (skip == 128) {
+ c++;
+ i = -1;
+ }
+ else
+ i += skip + 1;
+ return 1;
+ }
+ if (skip >= 128)
+ break;
+ i += skip + 1;
+ }
+ i = -1;
+ }
+ return 0;
+}
+
+tfm::tfm()
+: char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
+ kern(0), param(0)
+{
+}
+
+int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
+{
+ if (contains(c1) && char_info[c1 - bc].tag == 1) {
+ int i = char_info[c1 - bc].remainder;
+ if (lig_kern[i].skip_byte > 128)
+ i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
+ for (;;) {
+ int skip = lig_kern[i].skip_byte;
+ if (skip > 128)
+ break;
+ // We are only interested in normal ligatures, for which
+ // op_byte == 0.
+ if (lig_kern[i].op_byte == 0
+ && lig_kern[i].next_char == c2) {
+ *cp = lig_kern[i].remainder;
+ return 1;
+ }
+ if (skip == 128)
+ break;
+ i += skip + 1;
+ }
+ }
+ return 0;
+}
+
+int tfm::contains(int i)
+{
+ return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
+}
+
+int tfm::get_width(int i)
+{
+ return width[char_info[i - bc].width_index];
+}
+
+int tfm::get_height(int i)
+{
+ return height[char_info[i - bc].height_index];
+}
+
+int tfm::get_depth(int i)
+{
+ return depth[char_info[i - bc].depth_index];
+}
+
+int tfm::get_italic(int i)
+{
+ return italic[char_info[i - bc].italic_index];
+}
+
+int tfm::get_param(int i, int *p)
+{
+ if (i <= 0 || i > np)
+ return 0;
+ else {
+ *p = param[i - 1];
+ return 1;
+ }
+}
+
+int tfm::get_checksum()
+{
+ return cs;
+}
+
+int tfm::get_design_size()
+{
+ return ds;
+}
+
+tfm::~tfm()
+{
+ delete[] char_info;
+ delete[] width;
+ delete[] height;
+ delete[] depth;
+ delete[] italic;
+ delete[] lig_kern;
+ delete[] kern;
+ delete[] param;
+}
+
+int read2(unsigned char *&s)
+{
+ int n;
+ n = *s++ << 8;
+ n |= *s++;
+ return n;
+}
+
+int read4(unsigned char *&s)
+{
+ int n;
+ n = *s++ << 24;
+ n |= *s++ << 16;
+ n |= *s++ << 8;
+ n |= *s++;
+ return n;
+}
+
+int tfm::load(const char *file)
+{
+ errno = 0;
+ FILE *fp = fopen(file, FOPEN_RB);
+ if (!fp) {
+ error("can't open '%1': %2", file, strerror(errno));
+ return 0;
+ }
+ int c1 = getc(fp);
+ int c2 = getc(fp);
+ if (c1 == EOF || c2 == EOF) {
+ fclose(fp);
+ error("unexpected end of file on '%1'", file);
+ return 0;
+ }
+ int lf = (c1 << 8) + c2;
+ int toread = lf*4 - 2;
+ unsigned char *buf = new unsigned char[toread];
+ if (fread(buf, 1, toread, fp) != (size_t)toread) {
+ if (feof(fp))
+ error("unexpected end of file on '%1'", file);
+ else
+ error("error on file '%1'", file);
+ delete[] buf;
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+ if (lf < 6) {
+ error("bad TFM file '%1': impossibly short", file);
+ delete[] buf;
+ return 0;
+ }
+ unsigned char *ptr = buf;
+ int lh = read2(ptr);
+ bc = read2(ptr);
+ ec = read2(ptr);
+ nw = read2(ptr);
+ nh = read2(ptr);
+ nd = read2(ptr);
+ ni = read2(ptr);
+ nl = read2(ptr);
+ nk = read2(ptr);
+ int ne = read2(ptr);
+ np = read2(ptr);
+ if ((6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np)
+ != lf) {
+ error("bad TFM file '%1': lengths do not sum", file);
+ delete[] buf;
+ return 0;
+ }
+ if (lh < 2) {
+ error("bad TFM file '%1': header too short", file);
+ delete[] buf;
+ return 0;
+ }
+ char_info = new char_info_word[ec - bc + 1];
+ width = new int[nw];
+ height = new int[nh];
+ depth = new int[nd];
+ italic = new int[ni];
+ lig_kern = new lig_kern_command[nl];
+ kern = new int[nk];
+ param = new int[np];
+ int i;
+ cs = read4(ptr);
+ ds = read4(ptr);
+ ptr += (lh-2)*4;
+ for (i = 0; i < ec - bc + 1; i++) {
+ char_info[i].width_index = *ptr++;
+ unsigned char tem = *ptr++;
+ char_info[i].depth_index = tem & 0xf;
+ char_info[i].height_index = tem >> 4;
+ tem = *ptr++;
+ char_info[i].italic_index = tem >> 2;
+ char_info[i].tag = tem & 3;
+ char_info[i].remainder = *ptr++;
+ }
+ for (i = 0; i < nw; i++)
+ width[i] = read4(ptr);
+ for (i = 0; i < nh; i++)
+ height[i] = read4(ptr);
+ for (i = 0; i < nd; i++)
+ depth[i] = read4(ptr);
+ for (i = 0; i < ni; i++)
+ italic[i] = read4(ptr);
+ for (i = 0; i < nl; i++) {
+ lig_kern[i].skip_byte = *ptr++;
+ lig_kern[i].next_char = *ptr++;
+ lig_kern[i].op_byte = *ptr++;
+ lig_kern[i].remainder = *ptr++;
+ }
+ for (i = 0; i < nk; i++)
+ kern[i] = read4(ptr);
+ ptr += ne*4;
+ for (i = 0; i < np; i++)
+ param[i] = read4(ptr);
+ assert(ptr == buf + lf*4 - 2);
+ delete[] buf;
+ return 1;
+}
+
+class gf {
+ int left[256];
+ int right[256];
+ static int sread4(int *p, FILE *fp);
+ static int uread3(int *p, FILE *fp);
+ static int uread2(int *p, FILE *fp);
+ static int skip(int n, FILE *fp);
+public:
+ gf();
+ int load(const char *file);
+ int get_left_adjustment(int i) { return left[i]; }
+ int get_right_adjustment(int i) { return right[i]; }
+};
+
+gf::gf()
+{
+ for (int i = 0; i < 256; i++)
+ left[i] = right[i] = 0;
+}
+
+int gf::load(const char *file)
+{
+ enum {
+ paint_0 = 0,
+ paint1 = 64,
+ boc = 67,
+ boc1 = 68,
+ eoc = 69,
+ skip0 = 70,
+ skip1 = 71,
+ new_row_0 = 74,
+ xxx1 = 239,
+ yyy = 243,
+ no_op = 244,
+ pre = 247,
+ post = 248
+ };
+ int got_an_adjustment = 0;
+ int pending_adjustment = 0;
+ int left_adj = 0, right_adj = 0; // pacify compiler
+ const int gf_id_byte = 131;
+ errno = 0;
+ FILE *fp = fopen(file, FOPEN_RB);
+ if (!fp) {
+ error("can't open '%1': %2", file, strerror(errno));
+ return 0;
+ }
+ if (getc(fp) != pre || getc(fp) != gf_id_byte) {
+ error("bad gf file");
+ return 0;
+ }
+ int n = getc(fp);
+ if (n == EOF)
+ goto eof;
+ if (!skip(n, fp))
+ goto eof;
+ for (;;) {
+ int op = getc(fp);
+ if (op == EOF)
+ goto eof;
+ if (op == post)
+ break;
+ if ((op >= paint_0 && op <= paint_0 + 63)
+ || (op >= new_row_0 && op <= new_row_0 + 164))
+ continue;
+ switch (op) {
+ case no_op:
+ case eoc:
+ case skip0:
+ break;
+ case paint1:
+ case skip1:
+ if (!skip(1, fp))
+ goto eof;
+ break;
+ case paint1 + 1:
+ case skip1 + 1:
+ if (!skip(2, fp))
+ goto eof;
+ break;
+ case paint1 + 2:
+ case skip1 + 2:
+ if (!skip(3, fp))
+ goto eof;
+ break;
+ case boc:
+ {
+ int code;
+ if (!sread4(&code, fp))
+ goto eof;
+ if (pending_adjustment) {
+ pending_adjustment = 0;
+ left[code & 0377] = left_adj;
+ right[code & 0377] = right_adj;
+ }
+ if (!skip(20, fp))
+ goto eof;
+ break;
+ }
+ case boc1:
+ {
+ int code = getc(fp);
+ if (code == EOF)
+ goto eof;
+ if (pending_adjustment) {
+ pending_adjustment = 0;
+ left[code] = left_adj;
+ right[code] = right_adj;
+ }
+ if (!skip(4, fp))
+ goto eof;
+ break;
+ }
+ case xxx1:
+ {
+ int len = getc(fp);
+ if (len == EOF)
+ goto eof;
+ char buf[256];
+ if (fread(buf, 1, len, fp) != (size_t)len)
+ goto eof;
+ if (len == 10 /* strlen("adjustment") */
+ && memcmp(buf, "adjustment", len) == 0) {
+ int c = getc(fp);
+ if (c != yyy) {
+ if (c != EOF)
+ ungetc(c, fp);
+ break;
+ }
+ if (!sread4(&left_adj, fp))
+ goto eof;
+ c = getc(fp);
+ if (c != yyy) {
+ if (c != EOF)
+ ungetc(c, fp);
+ break;
+ }
+ if (!sread4(&right_adj, fp))
+ goto eof;
+ got_an_adjustment = 1;
+ pending_adjustment = 1;
+ }
+ break;
+ }
+ case xxx1 + 1:
+ if (!uread2(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case xxx1 + 2:
+ if (!uread3(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case xxx1 + 3:
+ if (!sread4(&n, fp) || !skip(n, fp))
+ goto eof;
+ break;
+ case yyy:
+ if (!skip(4, fp))
+ goto eof;
+ break;
+ default:
+ fatal("unrecognized opcode '%1'", op);
+ break;
+ }
+ }
+ if (!got_an_adjustment)
+ warning("no adjustment specials found in gf file");
+ return 1;
+ eof:
+ error("unexpected end of file");
+ return 0;
+}
+
+int gf::sread4(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ if (*p >= 128)
+ *p -= 256;
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::uread3(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::uread2(int *p, FILE *fp)
+{
+ *p = getc(fp);
+ *p <<= 8;
+ *p |= getc(fp);
+ return !ferror(fp) && !feof(fp);
+}
+
+int gf::skip(int n, FILE *fp)
+{
+ while (--n >= 0)
+ if (getc(fp) == EOF)
+ return 0;
+ return 1;
+}
+
+
+struct char_list {
+ char *ch;
+ char_list *next;
+ char_list(const char *, char_list * = 0);
+};
+
+char_list::char_list(const char *s, char_list *p) : ch(strsave(s)),
+ next(p)
+{
+}
+
+
+int read_map(const char *file, char_list **table)
+{
+ errno = 0;
+ FILE *fp = fopen(file, "r");
+ if (!fp) {
+ error("can't open '%1': %2", file, strerror(errno));
+ return 0;
+ }
+ for (int i = 0; i < 256; i++)
+ table[i] = 0;
+ char buf[512];
+ int lineno = 0;
+ while (fgets(buf, int(sizeof(buf)), fp)) {
+ lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+ ptr = strtok(ptr, " \n\t");
+ if (!ptr)
+ continue;
+ int n;
+ if (sscanf(ptr, "%d", &n) != 1) {
+ error("%1:%2: bad map file", file, lineno);
+ fclose(fp);
+ return 0;
+ }
+ if (n < 0 || n > 255) {
+ error("%1:%2: code %3 out of range", file, lineno, n);
+ fclose(fp);
+ return 0;
+ }
+ ptr = strtok(0, " \n\t");
+ if (!ptr) {
+ error("%1:%2: missing names", file, lineno);
+ fclose(fp);
+ return 0;
+ }
+ for (; ptr; ptr = strtok(0, " \n\t"))
+ table[n] = new char_list(ptr, table[n]);
+ }
+ fclose(fp);
+ return 1;
+}
+
+
+/* Every character that can participate in a ligature appears in the
+lig_chars table. 'ch' gives the full-name of the character, 'name'
+gives the groff name of the character, 'i' gives its index in
+the encoding, which is filled in later (-1 if it does not appear). */
+
+struct S {
+ const char *ch;
+ int i;
+} lig_chars[] = {
+ { "f", -1 },
+ { "i", -1 },
+ { "l", -1 },
+ { "ff", -1 },
+ { "fi", -1 },
+ { "fl", -1 },
+ { "Fi", -1 },
+ { "Fl", -1 },
+};
+
+// Indices into lig_chars[].
+
+enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
+
+// Each possible ligature appears in this table.
+
+struct S2 {
+ unsigned char c1, c2, res;
+ const char *ch;
+} lig_table[] = {
+ { CH_f, CH_f, CH_ff, "ff" },
+ { CH_f, CH_i, CH_fi, "fi" },
+ { CH_f, CH_l, CH_fl, "fl" },
+ { CH_ff, CH_i, CH_ffi, "ffi" },
+ { CH_ff, CH_l, CH_ffl, "ffl" },
+ };
+
+static void usage(FILE *stream);
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+ int special_flag = 0;
+ int skewchar = -1;
+ int opt;
+ const char *gf_file = 0;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+ while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL))
+ != EOF)
+ switch (opt) {
+ case 'g':
+ gf_file = optarg;
+ break;
+ case 's':
+ special_flag = 1;
+ break;
+ case 'k':
+ {
+ char *ptr;
+ long n = strtol(optarg, &ptr, 0);
+ if ((n == 0 && ptr == optarg)
+ || *ptr != '\0'
+ || n < 0
+ || n > UCHAR_MAX)
+ error("invalid skew character position '%1'", optarg);
+ else
+ skewchar = (int)n;
+ break;
+ }
+ case 'v':
+ {
+ printf("GNU tfmtodit (groff) version %s\n", Version_string);
+ exit(0);
+ break;
+ }
+ case CHAR_MAX + 1: // --help
+ usage(stdout);
+ exit(0);
+ break;
+ case '?':
+ usage(stderr);
+ exit(1);
+ break;
+ case EOF:
+ assert(0 == "EOF encountered in option processing");
+ }
+ if (argc - optind != 3) {
+ error("insufficient arguments");
+ usage(stderr);
+ exit(1);
+ }
+ gf g;
+ if (gf_file) {
+ if (!g.load(gf_file))
+ return 1;
+ }
+ const char *tfm_file = argv[optind];
+ const char *map_file = argv[optind + 1];
+ const char *font_file = argv[optind + 2];
+ tfm t;
+ if (!t.load(tfm_file))
+ return 1;
+ char_list *table[256];
+ if (!read_map(map_file, table))
+ return 1;
+ errno = 0;
+ if (!freopen(font_file, "w", stdout)) {
+ error("can't open '%1' for writing: %2", font_file,
+ strerror(errno));
+ return 1;
+ }
+ printf("name %s\n", font_file);
+ if (special_flag)
+ fputs("special\n", stdout);
+ char *internal_name = strsave(argv[optind]);
+ int len = strlen(internal_name);
+ if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
+ internal_name[len - 4] = '\0';
+ // DIR_SEPS[] are possible directory separator characters, see
+ // nonposix.h. We want the rightmost separator of all possible ones.
+ // Example: d:/foo\\bar.
+ const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1;
+ const char *sep = &DIR_SEPS[1];
+ while (*sep)
+ {
+ s1 = strrchr(internal_name, *sep);
+ if (s1 && (!s || s1 > s))
+ s = s1;
+ sep++;
+ }
+ printf("internalname %s\n", s ? s + 1 : internal_name);
+ int n;
+ if (t.get_param(2, &n)) {
+ if (n > 0)
+ printf("spacewidth %d\n", n*MULTIPLIER);
+ }
+ if (t.get_param(1, &n) && n != 0)
+ printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI);
+ int xheight;
+ if (!t.get_param(5, &xheight))
+ xheight = 0;
+ unsigned int i;
+ // Print the list of ligatures.
+ // First find the indices of each character that can participate in
+ // a ligature.
+ size_t lig_char_entries = sizeof(lig_chars)/sizeof(lig_chars[0]);
+ size_t lig_table_entries = sizeof(lig_table)/sizeof(lig_table[0]);
+ for (i = 0; i < 256; i++)
+ for (unsigned int j = 0; j < lig_char_entries; j++)
+ for (char_list *p = table[i]; p; p = p->next)
+ if (strcmp(lig_chars[j].ch, p->ch) == 0)
+ lig_chars[j].i = i;
+ // For each possible ligature, if its participants all exist,
+ // and it appears as a ligature in the tfm file, include in
+ // the list of ligatures.
+ int started = 0;
+ for (i = 0; i < lig_table_entries; i++) {
+ int i1 = lig_chars[lig_table[i].c1].i;
+ int i2 = lig_chars[lig_table[i].c2].i;
+ int r = lig_chars[lig_table[i].res].i;
+ if (i1 >= 0 && i2 >= 0 && r >= 0) {
+ unsigned char c;
+ if (t.get_lig(i1, i2, &c) && c == r) {
+ if (!started) {
+ started = 1;
+ fputs("ligatures", stdout);
+ }
+ printf(" %s", lig_table[i].ch);
+ }
+ }
+ }
+ if (started)
+ fputs(" 0\n", stdout);
+ printf("checksum %d\n", t.get_checksum());
+ printf("designsize %d\n", t.get_design_size());
+ // Now print out the kerning information.
+ int had_kern = 0;
+ kern_iterator iter(&t);
+ unsigned char c1, c2;
+ int k;
+ while (iter.next(&c1, &c2, &k))
+ if (c2 != skewchar) {
+ k *= MULTIPLIER;
+ char_list *q = table[c2];
+ for (char_list *p1 = table[c1]; p1; p1 = p1->next)
+ for (char_list *p2 = q; p2; p2 = p2->next) {
+ if (!had_kern) {
+ printf("kernpairs\n");
+ had_kern = 1;
+ }
+ printf("%s %s %d\n", p1->ch, p2->ch, k);
+ }
+ }
+ printf("charset\n");
+ char_list unnamed("---");
+ for (i = 0; i < 256; i++)
+ if (t.contains(i)) {
+ char_list *p = table[i] ? table[i] : &unnamed;
+ int m[6];
+ m[0] = t.get_width(i);
+ m[1] = t.get_height(i);
+ m[2] = t.get_depth(i);
+ m[3] = t.get_italic(i);
+ m[4] = g.get_left_adjustment(i);
+ m[5] = g.get_right_adjustment(i);
+ printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
+ int j;
+ for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
+ if (m[j] != 0)
+ break;
+ for (k = 1; k <= j; k++)
+ printf(",%d", m[k]*MULTIPLIER);
+ int type = 0;
+ if (m[2] > 0)
+ type = 1;
+ if (m[1] > xheight)
+ type += 2;
+ printf("\t%d\t%04o\n", type, i);
+ for (p = p->next; p; p = p->next)
+ printf("%s\t\"\n", p->ch);
+ }
+ return 0;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+"usage: %s [-s] [-g gf-file] [-k skew-char] tfm-file map-file font\n"
+"usage: %s {-v | --version}\n"
+"usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/utils/xtotroff/xtotroff.1.man b/src/utils/xtotroff/xtotroff.1.man
new file mode 100644
index 0000000..17fb0db
--- /dev/null
+++ b/src/utils/xtotroff/xtotroff.1.man
@@ -0,0 +1,237 @@
+.TH xtotroff @MAN1EXT@ "@MDATE@" "groff @VERSION@"
+.SH Name
+xtotroff \- convert X font metrics into
+.I groff
+font metrics
+.
+.
+.\" ====================================================================
+.\" Legal Terms
+.\" ====================================================================
+.\"
+.\" Copyright (C) 2004-2022 Free Software Foundation, Inc.
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of
+.\" this manual under the conditions for verbatim copying, provided that
+.\" the entire resulting derived work is distributed under the terms of
+.\" a permission notice identical to this one.
+.\"
+.\" Permission is granted to copy and distribute translations of this
+.\" manual into another language, under the above conditions for
+.\" modified versions, except that this permission notice may be
+.\" included in translations approved by the Free Software Foundation
+.\" instead of in the original English.
+.
+.
+.\" Save and disable compatibility mode (for, e.g., Solaris 10/11).
+.do nr *groff_xtotroff_1_man_C \n[.cp]
+.cp 0
+.
+.\" Define fallback for groff 1.23's MR macro if the system lacks it.
+.nr do-fallback 0
+.if !\n(.f .nr do-fallback 1 \" mandoc
+.if \n(.g .if !d MR .nr do-fallback 1 \" older groff
+.if !\n(.g .nr do-fallback 1 \" non-groff *roff
+.if \n[do-fallback] \{\
+. de MR
+. ie \\n(.$=1 \
+. I \%\\$1
+. el \
+. IR \%\\$1 (\\$2)\\$3
+. .
+.\}
+.rr do-fallback
+.
+.
+.\" ====================================================================
+.SH Synopsis
+.\" ====================================================================
+.
+.SY xtotroff
+.RB [ \-d\~\c
+.IR destination-directory ]
+.RB [ \-r\~\c
+.IR resolution ]
+.RB [ \-s\~\c
+.IR type-size ]
+.I font-map
+.YS
+.
+.
+.SY xtotroff
+.B \-\-help
+.YS
+.
+.
+.SY xtotroff
+.B \-v
+.
+.SY xtotroff
+.B \-\-version
+.YS
+.
+.
+.\" ====================================================================
+.SH Description
+.\" ====================================================================
+.
+.I xtotroff
+uses
+.I font-map
+to create
+.MR groff @MAN1EXT@
+font description files from X11 fonts.
+.
+Each line in
+.I font-map
+consists of a series of lines of paired
+.I groff
+font names and X font names as X Logical Font Description (XLFD)
+patterns,
+with the pair members separated by spaces and/or tabs.
+.
+For example,
+an input
+.I font-map
+file consisting of the line
+.
+.RS
+.EX
+TB \-adobe\-times\-bold\-r\-normal\-\-*\-*\-*\-*\-p\-*\-iso8859\-1
+.EE
+.RE
+.
+maps the XLFD on the right to the
+.I groff
+font name
+.BR TB ,
+conventionally \[lq]Times bold\[rq].
+.
+.
+.PP
+.I xtotroff
+opens a connection to the running X server to query its font catalog,
+and aborts if it cannot.
+.
+If necessary,
+the wildcards in the XLFD patterns are populated with the arguments to
+the
+.B \-r
+and
+.B \-s
+options.
+.
+If a font name is still ambiguous,
+.I xtotroff
+aborts.
+.
+For each successful mapping,
+.I xtotroff
+creates a
+.I groff
+font description file in the current working directory
+(or that specified by the
+.B -d
+option)
+named for each
+.I groff
+font,
+and reports the mapping to the standard output stream.
+.
+.
+.\" ====================================================================
+.SH Options
+.\" ====================================================================
+.
+.B \-\-help
+displays a usage message,
+while
+.B \-v
+and
+.B \-\-version
+show version information;
+all exit afterward.
+.
+.
+.TP
+.BI \-d\~ destination-directory
+Write font descriptions to
+.I destination-directory
+rather than the current working directory.
+.
+.
+.TP
+.BI \-r\~ resolution
+Set the resolution for all font patterns in
+.IR font-map .
+.
+The value is used for both the horizontal and vertical motion quanta.
+.
+If not specified,
+a resolution of 75dpi is assumed.
+.
+.
+.TP
+.BI \-s\~ type-size
+Set the type size in points for all font patterns in
+.IR font-map .
+.
+If not specified,
+a size of 10 points is assumed.
+.
+.
+.\" ====================================================================
+.SH Files
+.\" ====================================================================
+.
+.TP
+.I @FONTDIR@/\:\%FontMap\-X11
+is the font mapping file used to produce the pre-generated font
+description files,
+supplied with
+.IR groff ,
+of X11 core fonts corresponding to the 13 base Type\~1 fonts for
+PostScript level 1.
+.
+.
+.\" ====================================================================
+.SH Bugs
+.\" ====================================================================
+.
+The only supported font encodings are \[lq]iso8859\-1\[rq] and
+\%\[lq]adobe\-\:fontspecific\[rq].
+.
+.
+.\" ====================================================================
+.SH "See also"
+.\" ====================================================================
+.
+.UR https://\:www\:.x\:.org/\:releases/\:X11R7.6/\:doc/\:xorg\-docs/\
+\:specs/\:XLFD/xlfd\:.html
+\[lq]X Logical Font Description Conventions\[rq]
+.UE ,
+by Jim Flowers and Stephen Gildea.
+.
+.
+.PP
+.MR X 7 ,
+.MR groff @MAN1EXT@ ,
+.MR gxditview @MAN1EXT@ ,
+.MR troff @MAN1EXT@ ,
+.MR groff_font @MAN5EXT@
+.
+.
+.\" Restore compatibility mode (for, e.g., Solaris 10/11).
+.cp \n[*groff_xtotroff_1_man_C]
+.do rr *groff_xtotroff_1_man_C
+.
+.
+.\" Local Variables:
+.\" fill-column: 72
+.\" mode: nroff
+.\" End:
+.\" vim: set filetype=groff textwidth=72:
diff --git a/src/utils/xtotroff/xtotroff.am b/src/utils/xtotroff/xtotroff.am
new file mode 100644
index 0000000..734d143
--- /dev/null
+++ b/src/utils/xtotroff/xtotroff.am
@@ -0,0 +1,41 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if WITHOUT_X11
+XTOTROFF_MAN1 =
+else
+XTOTROFF_MAN1 = src/utils/xtotroff/xtotroff.1
+bin_PROGRAMS += xtotroff
+man1_MANS += $(XTOTROFF_MAN1)
+xtotroff_SOURCES = src/utils/xtotroff/xtotroff.c
+XLIBS=$(LIBXUTIL) $(LIBGROFF)
+xtotroff_LDADD = libxutil.a libgroff.a $(X_LIBS) $(X_PRE_LIBS) \
+ -lXaw -lXt -lX11 $(X_EXTRA_LIBS) $(LIBM) lib/libgnu.a
+xtotroff_CPPFLAGS = $(AM_CPPFLAGS) $(X_CFLAGS)
+endif
+EXTRA_DIST += src/utils/xtotroff/xtotroff.1.man
+
+# Define variable needed only for the targets that regenerate
+# descriptions of X11 core fonts (used in "maintainer mode").
+xtotroff=$(top_builddir)/xtotroff
+
+
+# Local Variables:
+# fill-column: 72
+# mode: makefile-automake
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/utils/xtotroff/xtotroff.c b/src/utils/xtotroff/xtotroff.c
new file mode 100644
index 0000000..368761f
--- /dev/null
+++ b/src/utils/xtotroff/xtotroff.c
@@ -0,0 +1,368 @@
+/* Copyright (C) 1992-2022 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/*
+ * xtotroff
+ *
+ * convert X font metrics into troff font metrics
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define __GETOPT_PREFIX groff_
+
+#include <X11/Xlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include <getopt.h>
+
+#include "XFontName.h"
+#include "DviChar.h"
+
+#define charWidth(fi,c) \
+ ((fi)->per_char[(c) - (fi)->min_char_or_byte2].width)
+#define charHeight(fi,c) \
+ ((fi)->per_char[(c) - (fi)->min_char_or_byte2].ascent)
+#define charDepth(fi,c) \
+ ((fi)->per_char[(c) - (fi)->min_char_or_byte2].descent)
+#define charLBearing(fi,c) \
+ ((fi)->per_char[(c) - (fi)->min_char_or_byte2].lbearing)
+#define charRBearing(fi,c) \
+ ((fi)->per_char[(c) - (fi)->min_char_or_byte2].rbearing)
+
+extern const char *Version_string;
+static char *program_name;
+
+Display *dpy;
+unsigned resolution = 75;
+unsigned point_size = 10;
+char *destdir = NULL;
+
+static bool charExists(XFontStruct * fi, int c)
+{
+ XCharStruct *p;
+
+ /* 'c' is always >= 0 */
+ if ((unsigned int) c < fi->min_char_or_byte2
+ || (unsigned int) c > fi->max_char_or_byte2)
+ return false;
+ p = fi->per_char + (c - fi->min_char_or_byte2);
+ return p->lbearing != 0 || p->rbearing != 0 || p->width != 0
+ || p->ascent != 0 || p->descent != 0 || p->attributes != 0;
+}
+
+/* Canonicalize the font name by replacing scalable parts by *s. */
+
+static bool CanonicalizeFontName(char *font_name, char *canon_font_name)
+{
+ unsigned int attributes;
+ XFontName parsed;
+
+ if (!XParseFontName(font_name, &parsed, &attributes)) {
+ fprintf(stderr, "%s: not a standard font name: \"%s\"\n",
+ program_name, font_name);
+ return false;
+ }
+
+ attributes &= ~(FontNamePixelSize | FontNameAverageWidth
+ | FontNamePointSize
+ | FontNameResolutionX | FontNameResolutionY);
+ XFormatFontName(&parsed, attributes, canon_font_name);
+ return true;
+}
+
+static bool
+FontNamesAmbiguous(const char *font_name, char **names, int count)
+{
+ char name1[2048], name2[2048];
+ int i;
+
+ if (1 == count)
+ return false;
+
+ for (i = 0; i < count; i++) {
+ if (!CanonicalizeFontName(names[i], 0 == i ? name1 : name2)) {
+ fprintf(stderr, "%s: invalid font name: \"%s\"\n", program_name,
+ names[i]);
+ return true;
+ }
+ if (i > 0 && strcmp(name1, name2) != 0) {
+ fprintf(stderr, "%s: ambiguous font name: \"%s\"", program_name,
+ font_name);
+ fprintf(stderr, " matches \"%s\"", names[0]);
+ fprintf(stderr, " and \"%s\"", names[i]);
+ return true;
+ }
+ }
+ return false;
+}
+
+static void xtotroff_exit(int status)
+{
+ free(destdir);
+ exit(status);
+}
+
+static bool MapFont(char *font_name, const char *troff_name)
+{
+ XFontStruct *fi;
+ int count;
+ char **names;
+ FILE *out;
+ unsigned int c;
+ unsigned int attributes;
+ XFontName parsed;
+ int j, k;
+ DviCharNameMap *char_map;
+ /* 'encoding' needs to hold a CharSetRegistry (256), a CharSetEncoding
+ (256) [both from XFontName.h], a dash, and a null terminator. */
+ char encoding[256 * 2 + 1 + 1];
+ char *s;
+ int wid;
+ char name_string[2048];
+
+ if (!XParseFontName(font_name, &parsed, &attributes)) {
+ fprintf(stderr, "%s: not a standard font name: \"%s\"\n",
+ program_name, font_name);
+ return false;
+ }
+
+ attributes &= ~(FontNamePixelSize | FontNameAverageWidth);
+ attributes |= FontNameResolutionX;
+ attributes |= FontNameResolutionY;
+ attributes |= FontNamePointSize;
+ parsed.ResolutionX = resolution;
+ parsed.ResolutionY = resolution;
+ parsed.PointSize = point_size * 10;
+ XFormatFontName(&parsed, attributes, name_string);
+
+ names = XListFonts(dpy, name_string, 100000, &count);
+ if (count < 1) {
+ fprintf(stderr, "%s: invalid font name: \"%s\"\n", program_name,
+ font_name);
+ return false;
+ }
+
+ if (FontNamesAmbiguous(font_name, names, count))
+ return false;
+
+ XParseFontName(names[0], &parsed, &attributes);
+ size_t sz = sizeof encoding;
+ snprintf(encoding, sz, "%s-%s", parsed.CharSetRegistry,
+ parsed.CharSetEncoding);
+ for (s = encoding; *s; s++)
+ if (isupper(*s))
+ *s = tolower(*s);
+ char_map = DviFindMap(encoding);
+ if (!char_map) {
+ fprintf(stderr, "%s: not a standard encoding: \"%s\"\n",
+ program_name, encoding);
+ return false;
+ }
+
+ fi = XLoadQueryFont(dpy, names[0]);
+ if (!fi) {
+ fprintf(stderr, "%s: font does not exist: \"%s\"\n", program_name,
+ names[0]);
+ return false;
+ }
+
+ printf("%s -> %s\n", names[0], troff_name);
+ char *file_name = (char *)troff_name;
+ size_t dirlen = strlen(destdir);
+
+ if (dirlen > 0) {
+ size_t baselen = strlen(troff_name);
+ file_name = malloc(dirlen + baselen + 2 /* '/' and '\0' */);
+ if (NULL == file_name) {
+ fprintf(stderr, "%s: fatal error: unable to allocate memory\n",
+ program_name);
+ xtotroff_exit(EXIT_FAILURE);
+ }
+ (void) strcpy(file_name, destdir);
+ file_name[dirlen] = '/';
+ (void) strcpy((file_name + dirlen + 1), troff_name);
+ }
+
+ { /* Avoid race while opening file */
+ int fd;
+ (void) unlink(file_name);
+ fd = open(file_name, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ out = fdopen(fd, "w");
+ }
+
+ if (NULL == out) {
+ fprintf(stderr, "%s: unable to create '%s': %s\n", program_name,
+ file_name, strerror(errno));
+ free(file_name);
+ return false;
+ }
+ fprintf(out, "name %s\n", troff_name);
+ if (!strcmp(char_map->encoding, "adobe-fontspecific"))
+ fprintf(out, "special\n");
+ if (charExists(fi, ' ')) {
+ int w = charWidth(fi, ' ');
+ if (w > 0)
+ fprintf(out, "spacewidth %d\n", w);
+ }
+ fprintf(out, "charset\n");
+ for (c = fi->min_char_or_byte2; c <= fi->max_char_or_byte2; c++) {
+ const char *name = DviCharName(char_map, c, 0);
+ if (charExists(fi, c)) {
+ int param[5];
+
+ wid = charWidth(fi, c);
+
+ fprintf(out, "%s\t%d", name ? name : "---", wid);
+ param[0] = charHeight(fi, c);
+ param[1] = charDepth(fi, c);
+ param[2] = 0; /* charRBearing (fi, c) - wid */
+ param[3] = 0; /* charLBearing (fi, c) */
+ param[4] = 0; /* XXX */
+ for (j = 0; j < 5; j++)
+ if (param[j] < 0)
+ param[j] = 0;
+ for (j = 4; j >= 0; j--)
+ if (param[j] != 0)
+ break;
+ for (k = 0; k <= j; k++)
+ fprintf(out, ",%d", param[k]);
+ fprintf(out, "\t0\t0%o\n", c);
+
+ if (name) {
+ for (k = 1; DviCharName(char_map, c, k); k++) {
+ fprintf(out, "%s\t\"\n", DviCharName(char_map, c, k));
+ }
+ }
+ }
+ }
+ XUnloadFont(dpy, fi->fid);
+ fclose(out);
+ free(file_name);
+ return true;
+}
+
+static void usage(FILE *stream)
+{
+ fprintf(stream,
+ "usage: %s [-d destination-directory] [-r resolution]"
+ " [-s type-size] font-map\n"
+ "usage: %s {-v | --version}\n"
+ "usage: %s --help\n",
+ program_name, program_name, program_name);
+}
+
+int main(int argc, char **argv)
+{
+ char troff_name[1024];
+ char font_name[1024];
+ char line[1024];
+ char *a, *b, c;
+ FILE *map;
+ int opt;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, CHAR_MAX + 1 },
+ { "version", no_argument, 0, 'v' },
+ { NULL, 0, 0, 0 }
+ };
+
+ program_name = argv[0];
+
+ while ((opt = getopt_long(argc, argv, "d:gr:s:v", long_options,
+ NULL)) != EOF) {
+ switch (opt) {
+ case 'd':
+ destdir = strdup(optarg);
+ break;
+ case 'g':
+ /* unused; just for compatibility */
+ break;
+ case 'r':
+ sscanf(optarg, "%u", &resolution);
+ break;
+ case 's':
+ sscanf(optarg, "%u", &point_size);
+ break;
+ case 'v':
+ printf("GNU xtotroff (groff) version %s\n", Version_string);
+ xtotroff_exit(EXIT_SUCCESS);
+ break;
+ case CHAR_MAX + 1: /* --help */
+ usage(stdout);
+ xtotroff_exit(EXIT_SUCCESS);
+ break;
+ case '?':
+ usage(stderr);
+ xtotroff_exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ if (argc - optind != 1) {
+ usage(stderr);
+ xtotroff_exit(EXIT_FAILURE);
+ }
+
+ dpy = XOpenDisplay(0);
+ if (!dpy) {
+ fprintf(stderr, "%s: fatal error: can't connect to the X server;"
+ " make sure the DISPLAY environment variable is set"
+ " correctly\n", program_name);
+ xtotroff_exit(EXIT_FAILURE);
+ }
+
+ map = fopen(argv[optind], "r");
+ if (NULL == map) {
+ fprintf(stderr, "%s: fatal error: unable to open map file '%s':"
+ " %s\n", program_name, argv[optind], strerror(errno));
+ xtotroff_exit(EXIT_FAILURE);
+ }
+
+ while (fgets(line, sizeof(line), map)) {
+ for (a = line, b = troff_name; *a; a++, b++) {
+ c = (*b = *a);
+ if (' ' == c || '\t' == c)
+ break;
+ }
+ *b = '\0';
+ while (*a && (' ' == *a || '\t' == *a))
+ ++a;
+ for (b = font_name; *a; a++, b++)
+ if ((*b = *a) == '\n')
+ break;
+ *b = '\0';
+ if (!MapFont(font_name, troff_name))
+ xtotroff_exit(EXIT_FAILURE);
+ }
+ xtotroff_exit(EXIT_SUCCESS);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72: