Initial work on PS and PDF output. Because these two backends share
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Apr 2004 13:17:48 +0000 (13:17 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Tue, 13 Apr 2004 13:17:48 +0000 (13:17 +0000)
an enormous amount of preprocessing and differ only in their final
output form, I've introduced a new type of layer called a
`pre-backend' (bk_paper.c is one). This takes all the information
passed to a normal backend and returns an arbitrary void *, which is
cached by the front end and passed on to any backend(s) which state
a desire for the output of that particular pre-backend. Thus, all
the page layout is done only once, and the PS and PDF backends
process the same data structures into two output files.
Note that these backends are _very_ unfinished; all sorts of vital
things such as section numbers, list markers, and title formatting
are missing, the paragraph justification doesn't quite work, and
advanced stuff like indexes and PDF interactive features haven't
even been started. But this basic framework generates valid output
files and is a good starting point, so I'm checking it in.

git-svn-id: svn://svn.tartarus.org/sgt/halibut@4058 cda61777-01e9-0310-a592-d414129be87e

16 files changed:
.cvsignore
Makefile
bk_info.c
bk_man.c
bk_paper.c [new file with mode: 0644]
bk_pdf.c [new file with mode: 0644]
bk_ps.c [new file with mode: 0644]
bk_text.c
bk_whlp.c
bk_xhtml.c
halibut.h
help.c
main.c
misc.c
paper.h [new file with mode: 0644]
psdata.c [new file with mode: 0644]

index 45aea9d..f04d00c 100644 (file)
@@ -5,3 +5,4 @@ build
 *.log
 *.1
 *.info *.info-*
+*.ps *.pdf
index 3802c58..22fa715 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -100,8 +100,8 @@ SRC := ../
 
 MODULES := main malloc ustring error help licence version misc tree234
 MODULES += input keywords contents index style biblio
-MODULES += bk_text bk_xhtml bk_whlp bk_man bk_info
-MODULES += winhelp
+MODULES += bk_text bk_xhtml bk_whlp bk_man bk_info bk_paper bk_ps bk_pdf
+MODULES += winhelp psdata
 
 OBJECTS := $(addsuffix .o,$(MODULES))
 DEPS := $(addsuffix .d,$(MODULES))
index 23f8628..0d7c032 100644 (file)
--- a/bk_info.c
+++ b/bk_info.c
@@ -119,7 +119,7 @@ paragraph *info_config_filename(char *filename)
 }
 
 void info_backend(paragraph *sourceform, keywordlist *keywords,
-                 indexdata *idx) {
+                 indexdata *idx, void *unused) {
     paragraph *p;
     infoconfig conf;
     word *prefix, *body, *wp;
@@ -140,8 +140,7 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
     int width = 70, listindentbefore = 1, listindentafter = 3;
     int indent_code = 2, index_width = 40;
 
-    IGNORE(keywords);                 /* we don't happen to need this */
-    IGNORE(idx);                      /* or this */
+    IGNORE(unused);
 
     conf = info_configure(sourceform);
 
@@ -839,12 +838,14 @@ static int info_width_internal(word *words, int xrefs) {
     return 0;                         /* should never happen */
 }
 
-static int info_width_noxrefs(word *words)
+static int info_width_noxrefs(void *ctx, word *words)
 {
+    IGNORE(ctx);
     return info_width_internal(words, FALSE);
 }
-static int info_width_xrefs(word *words)
+static int info_width_xrefs(void *ctx, word *words)
 {
+    IGNORE(ctx);
     return info_width_internal(words, TRUE);
 }
 
@@ -866,7 +867,8 @@ static void info_heading(rdstringc *text, word *tprefix,
     firstlinewidth = width - length;
     wrapwidth = width;
 
-    wrapping = wrap_para(words, firstlinewidth, wrapwidth, info_width_noxrefs);
+    wrapping = wrap_para(words, firstlinewidth, wrapwidth,
+                        info_width_noxrefs, NULL, 0);
     for (p = wrapping; p; p = p->next) {
        info_rdaddwc(&t, p->begin, p->end, FALSE);
        length = (t.text ? strlen(t.text) : 0);
@@ -930,7 +932,8 @@ static void info_para(rdstringc *text, word *prefix, char *prefixextra,
     } else
        e = indent + extraindent;
 
-    wrapping = wrap_para(words, firstlinewidth, width, info_width_xrefs);
+    wrapping = wrap_para(words, firstlinewidth, width, info_width_xrefs,
+                        NULL, 0);
     for (p = wrapping; p; p = p->next) {
        for (i = 0; i < e; i++)
            rdaddc(text, ' ');
index a773b60..60157fa 100644 (file)
--- a/bk_man.c
+++ b/bk_man.c
@@ -94,13 +94,14 @@ paragraph *man_config_filename(char *filename)
 #define QUOTE_QUOTES   2 /* quote double quotes by doubling them */
 
 void man_backend(paragraph *sourceform, keywordlist *keywords,
-                indexdata *idx) {
+                indexdata *idx, void *unused) {
     paragraph *p;
     FILE *fp;
     manconfig conf;
 
-    IGNORE(keywords);                 /* we don't happen to need this */
-    IGNORE(idx);                      /* or this */
+    IGNORE(unused);
+    IGNORE(keywords);
+    IGNORE(idx);
 
     conf = man_configure(sourceform);
 
diff --git a/bk_paper.c b/bk_paper.c
new file mode 100644 (file)
index 0000000..0bf3ce1
--- /dev/null
@@ -0,0 +1,884 @@
+/*
+ * Paper printing pre-backend for Halibut.
+ * 
+ * This module does all the processing common to both PostScript
+ * and PDF output: selecting fonts, line wrapping and page breaking
+ * in accordance with font metrics, laying out the contents and
+ * index pages, generally doing all the page layout. After this,
+ * bk_ps.c and bk_pdf.c should only need to do linear translations
+ * into their literal output format.
+ */
+
+/*
+ * To be done:
+ * 
+ *  - Text wrapping is suspicious in both PS and PDF: the space
+ *    adjust seems to be _approximately_ working, but not exactly.
+ *    I bet some rounding error compensation is required.
+ * 
+ *  - set up contents section now we know what sections begin on
+ *    which pages
+ * 
+ *  - do cross-reference rectangles
+ * 
+ *  - do PDF outline
+ * 
+ *  - all the missing features in text rendering (code paragraphs,
+ *    list bullets, indentation, section heading styles)
+ * 
+ *  - index
+ * 
+ * That should bring us to the same level of functionality that
+ * original-Halibut had, and the same in PDF plus the obvious
+ * interactive navigation features. After that, in future work:
+ * 
+ *  - linearised PDF, perhaps?
+ * 
+ *  - I'm uncertain of whether I need to include a ToUnicode CMap
+ *    in each of my font definitions in PDF. Currently things (by
+ *    which I mean cut and paste out of acroread) seem to be
+ *    working fairly happily without it, but I don't know.
+ * 
+ *  - configurability
+ * 
+ *  - title pages
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "halibut.h"
+#include "paper.h"
+
+static font_data *make_std_font(font_list *fontlist, char const *name);
+static void wrap_paragraph(para_data *pdata, word *words,
+                          int w, int i1, int i2);
+static page_data *page_breaks(line_data *first, line_data *last,
+                             int page_height);
+static void render_line(line_data *ldata, int left_x, int top_y);
+
+void *paper_pre_backend(paragraph *sourceform, keywordlist *keywords,
+                       indexdata *idx) {
+    paragraph *p;
+    document *doc;
+    int indent, extra_indent, firstline_indent;
+    para_data *pdata;
+    line_data *ldata, *firstline, *lastline;
+    font_data *tr, *ti, *cr;
+    page_data *pages;
+    font_list *fontlist;
+
+    /*
+     * FIXME: All these things ought to become configurable.
+     */
+    int paper_width = 595 * 4096;
+    int paper_height = 841 * 4096;
+    int left_margin = 72 * 4096;
+    int top_margin = 72 * 4096;
+    int right_margin = 72 * 4096;
+    int bottom_margin = 108 * 4096;
+    int indent_list_bullet = 6 * 4096;
+    int indent_list = 24 * 4096;
+    int indent_quote = 18 * 4096;
+    int base_leading = 4096;
+    int base_para_spacing = 10 * 4096;
+
+    int base_width = paper_width - left_margin - right_margin;
+    int page_height = paper_height - top_margin - bottom_margin;
+
+    IGNORE(keywords);                 /* FIXME */
+    IGNORE(idx);                      /* FIXME */
+    IGNORE(indent_list_bullet);               /* FIXME */
+
+    /*
+     * First, set up some font structures.
+     */
+    fontlist = mknew(font_list);
+    fontlist->head = fontlist->tail = NULL;
+    tr = make_std_font(fontlist, "Times-Roman");
+    ti = make_std_font(fontlist, "Times-Italic");
+    cr = make_std_font(fontlist, "Courier");
+
+    /*
+     * Go through and break up each paragraph into lines.
+     */
+    indent = 0;
+    firstline = lastline = NULL;
+    for (p = sourceform; p; p = p->next) {
+       p->private_data = NULL;
+
+       switch (p->type) {
+           /*
+            * These paragraph types are either invisible or don't
+            * define text in the normal sense. Either way, they
+            * don't require wrapping.
+            */
+         case para_IM:
+         case para_BR:
+         case para_Rule:
+         case para_Biblio:
+         case para_NotParaType:
+         case para_Config:
+         case para_VersionID:
+         case para_NoCite:
+           break;
+
+           /*
+            * These paragraph types don't require wrapping, but
+            * they do affect the line width to which we wrap the
+            * rest of the paragraphs, so we need to pay attention.
+            */
+         case para_LcontPush:
+           indent += indent_list; break;
+         case para_LcontPop:
+           indent -= indent_list; assert(indent >= 0); break;
+         case para_QuotePush:
+           indent += indent_quote; break;
+         case para_QuotePop:
+           indent -= indent_quote; assert(indent >= 0); break;
+
+           /*
+            * This paragraph type is special. Process it
+            * specially.
+            */
+         case para_Code:
+           /* FIXME */
+           break;
+
+           /*
+            * All of these paragraph types require wrapping in the
+            * ordinary way. So we must supply a set of fonts, a
+            * line width and auxiliary information (e.g. bullet
+            * text) for each one.
+            */
+         case para_Chapter:
+         case para_Appendix:
+         case para_UnnumberedChapter:
+         case para_Heading:
+         case para_Subsect:
+         case para_Normal:
+         case para_BiblioCited:
+         case para_Bullet:
+         case para_NumberedList:
+         case para_DescribedThing:
+         case para_Description:
+         case para_Copyright:
+         case para_Title:
+           pdata = mknew(para_data);
+
+           /*
+            * FIXME: Subsidiary switch on paragraph type to decide
+            * what font set to use for this paragraph.
+            */
+           pdata->fonts[FONT_NORMAL] = tr;
+           pdata->sizes[FONT_NORMAL] = 12;
+           pdata->fonts[FONT_EMPH] = ti;
+           pdata->sizes[FONT_EMPH] = 12;
+           pdata->fonts[FONT_CODE] = cr;
+           pdata->sizes[FONT_CODE] = 12;
+
+           /*
+            * FIXME: Also select an indentation level depending on
+            * the paragraph type (list paragraphs other than
+            * para_DescribedThing need extra indent).
+            * 
+            * Perhaps at some point we might even arrange for the
+            * user to be able to request indented first lines in
+            * paragraphs.
+            */
+           extra_indent = 0;
+           firstline_indent = 0;
+
+           wrap_paragraph(pdata, p->words, base_width,
+                          indent + firstline_indent,
+                          indent + extra_indent);
+
+           /*
+            * FIXME: Also find the auxiliary data for this
+            * paragraph. For para_Bullet it's a bullet; for
+            * para_NumberedList it's the number; for some section
+            * headings (depending on the style of section heading
+            * selected) it's the section number.
+            * 
+            * Assign into pdata->first->aux_*.
+            */
+
+           p->private_data = pdata;
+
+           /*
+            * Set the line spacing for each line in this paragraph.
+            */
+           for (ldata = pdata->first; ldata; ldata = ldata->next) {
+               if (ldata == pdata->first)
+                   ldata->space_before = base_para_spacing / 2;
+               else
+                   ldata->space_before = base_leading / 2;
+               if (ldata == pdata->last)
+                   ldata->space_after = base_para_spacing / 2;
+               else
+                   ldata->space_after = base_leading / 2;
+               ldata->page_break = FALSE;
+           }
+
+           /*
+            * FIXME: some kinds of section heading do require a
+            * page break before them.
+            */
+
+           break;
+       }
+
+       /*
+        * Link all line structures together into a big list.
+        */
+       if (p->private_data) {
+           pdata = (para_data *)p->private_data;
+           if (pdata->first) {
+               if (lastline) {
+                   lastline->next = pdata->first;
+                   pdata->first->prev = lastline;
+               } else {
+                   firstline = pdata->first;
+                   pdata->first->prev = NULL;
+               }
+               lastline = pdata->last;
+           }
+       }
+    }
+
+    /*
+     * Now we have an enormous linked list of every line of text in
+     * the document. Break it up into pages.
+     */
+    pages = page_breaks(firstline, lastline, page_height);
+
+    /*
+     * Now we're ready to actually lay out the pages. We do this by
+     * looping over _paragraphs_, since we may need to track cross-
+     * references between lines and even across pages.
+     */
+    for (p = sourceform; p; p = p->next) {
+       pdata = (para_data *)p->private_data;
+
+       if (pdata) {
+           for (ldata = pdata->first; ldata; ldata = ldata->next) {
+               render_line(ldata, left_margin, paper_height - top_margin);
+               if (ldata == pdata->last)
+                   break;
+           }
+       }
+    }
+
+    doc = mknew(document);
+    doc->fonts = fontlist;
+    doc->pages = pages;
+    doc->paper_width = paper_width;
+    doc->paper_height = paper_height;
+    return doc;
+}
+
+static font_encoding *new_font_encoding(font_data *font)
+{
+    font_encoding *fe;
+    int i;
+
+    fe = mknew(font_encoding);
+    fe->next = NULL;
+
+    if (font->list->tail)
+       font->list->tail->next = fe;
+    else
+       font->list->head = fe;
+    font->list->tail = fe;
+
+    fe->font = font;
+    fe->free_pos = 0x21;
+
+    for (i = 0; i < 256; i++) {
+       fe->vector[i] = NULL;
+       fe->indices[i] = -1;
+       fe->to_unicode[i] = 0xFFFF;
+    }
+
+    return fe;
+}
+
+static font_data *make_std_font(font_list *fontlist, char const *name)
+{
+    const int *widths;
+    int nglyphs;
+    font_data *f;
+    font_encoding *fe;
+    int i;
+
+    widths = ps_std_font_widths(name);
+    if (!widths)
+       return NULL;
+
+    for (nglyphs = 0; ps_std_glyphs[nglyphs] != NULL; nglyphs++);
+
+    f = mknew(font_data);
+
+    f->list = fontlist;
+    f->name = name;
+    f->nglyphs = nglyphs;
+    f->glyphs = ps_std_glyphs;
+    f->widths = widths;
+    f->subfont_map = mknewa(subfont_map_entry, nglyphs);
+
+    /*
+     * Our first subfont will contain all of US-ASCII. This isn't
+     * really necessary - we could just create custom subfonts
+     * precisely as the whim of render_string dictated - but
+     * instinct suggests that it might be nice to have the text in
+     * the output files look _marginally_ recognisable.
+     */
+    fe = new_font_encoding(f);
+    fe->free_pos = 0xA1;              /* only the top half is free */
+    f->latest_subfont = fe;
+
+    for (i = 0; i < (int)lenof(f->bmp); i++)
+       f->bmp[i] = 0xFFFF;
+
+    for (i = 0; i < nglyphs; i++) {
+       wchar_t ucs;
+       ucs = ps_glyph_to_unicode(f->glyphs[i]);
+       assert(ucs != 0xFFFF);
+       f->bmp[ucs] = i;
+       if (ucs >= 0x20 && ucs <= 0x7E) {
+           fe->vector[ucs] = f->glyphs[i];
+           fe->indices[ucs] = i;
+           fe->to_unicode[ucs] = ucs;
+           f->subfont_map[i].subfont = fe;
+           f->subfont_map[i].position = ucs;
+       } else {
+           /*
+            * This character is not yet assigned to a subfont.
+            */
+           f->subfont_map[i].subfont = NULL;
+           f->subfont_map[i].position = 0;
+       }
+    }
+
+    return f;
+}
+
+static int string_width(font_data *font, wchar_t const *string, int *errs)
+{
+    int width = 0;
+
+    if (errs)
+       *errs = 0;
+
+    for (; *string; string++) {
+       int index;
+
+       index = font->bmp[(unsigned short)*string];
+       if (index == 0xFFFF) {
+           if (errs)
+               *errs = 1;
+       } else {
+           width += font->widths[index];
+       }
+    }
+
+    return width;
+}
+
+static int paper_width(void *vctx, word *word);
+
+struct paper_width_ctx {
+    int minspacewidth;
+    para_data *pdata;
+};
+
+static int paper_width_list(void *vctx, word *text, word *end) {
+    int w = 0;
+    while (text) {
+       w += paper_width(vctx, text);
+       if (text == end)
+           break;
+       text = text->next;
+    }
+    return w;
+}
+
+static int paper_width(void *vctx, word *word)
+{
+    struct paper_width_ctx *ctx = (struct paper_width_ctx *)vctx;
+    int style, type, findex, width, errs;
+    wchar_t *str;
+
+    switch (word->type) {
+      case word_HyperLink:
+      case word_HyperEnd:
+      case word_UpperXref:
+      case word_LowerXref:
+      case word_XrefEnd:
+      case word_IndexRef:
+       return 0;
+    }
+
+    style = towordstyle(word->type);
+    type = removeattr(word->type);
+
+    findex = (style == word_Normal ? FONT_NORMAL :
+             style == word_Emph ? FONT_EMPH :
+             FONT_CODE);
+
+    if (type == word_Normal) {
+       str = word->text;
+    } else if (type == word_WhiteSpace) {
+       if (findex != FONT_CODE)
+           return ctx->minspacewidth;
+       else
+           str = L" ";
+    } else /* if (type == word_Quote) */ {
+       if (word->aux == quote_Open)
+           str = L"\x2018";           /* FIXME: configurability! */
+       else
+           str = L"\x2019";           /* FIXME: configurability! */
+    }
+
+    width = string_width(ctx->pdata->fonts[findex], str, &errs);
+
+    if (errs && word->alt)
+       return paper_width_list(vctx, word->alt, NULL);
+    else
+       return ctx->pdata->sizes[findex] * width;
+}
+
+static void wrap_paragraph(para_data *pdata, word *words,
+                          int w, int i1, int i2)
+{
+    wrappedline *wrapping, *p;
+    int spacewidth;
+    struct paper_width_ctx ctx;
+    int line_height;
+
+    /*
+     * We're going to need to store the line height in every line
+     * structure we generate.
+     */
+    {
+       int i;
+       line_height = 0;
+       for (i = 0; i < NFONTS; i++)
+           if (line_height < pdata->sizes[i])
+               line_height = pdata->sizes[i];
+       line_height *= 4096;
+    }
+
+    spacewidth = (pdata->sizes[FONT_NORMAL] *
+                 string_width(pdata->fonts[FONT_NORMAL], L" ", NULL));
+    if (spacewidth == 0) {
+       /*
+        * A font without a space?! Disturbing. I hope this never
+        * comes up, but I'll make a random guess anyway and set my
+        * space width to half the point size.
+        */
+       spacewidth = pdata->sizes[FONT_NORMAL] * 4096 / 2;
+    }
+
+    /*
+     * I'm going to set the _minimum_ space width to 3/5 of the
+     * standard one, and use the standard one as the optimum.
+     */
+    ctx.minspacewidth = spacewidth * 3 / 5;
+    ctx.pdata = pdata;
+
+    wrapping = wrap_para(words, w - i1, w - i2, paper_width, &ctx, spacewidth);
+
+    /*
+     * Having done the wrapping, we now concoct a set of line_data
+     * structures.
+     */
+    pdata->first = pdata->last = NULL;
+
+    for (p = wrapping; p; p = p->next) {
+       line_data *ldata;
+       word *wd;
+       int len, wid, spaces;
+
+       ldata = mknew(line_data);
+
+       ldata->pdata = pdata;
+       ldata->first = p->begin;
+       ldata->last = p->end;
+       ldata->line_height = line_height;
+
+       ldata->xpos = (p == wrapping ? i1 : i2);
+
+       if (pdata->last) {
+           pdata->last->next = ldata;
+           ldata->prev = pdata->last;
+       } else {
+           pdata->first = ldata;
+           ldata->prev = NULL;
+       }
+       ldata->next = NULL;
+       pdata->last = ldata;
+
+       len = paper_width_list(&ctx, ldata->first, ldata->last);
+       wid = (p == wrapping ? w - i1 : w - i2);
+       spaces = 0;
+       wd = ldata->first;
+       while (wd) {
+#if 0
+           switch (wd->type) {
+             case word_HyperLink:
+             case word_HyperEnd:
+             case word_UpperXref:
+             case word_LowerXref:
+             case word_XrefEnd:
+             case word_IndexRef:
+               break;
+
+             default:
+               if (removeattr(wd->type) == word_Normal)
+                   printf("%ls", wd->text);
+               else if (removeattr(wd->type) == word_WhiteSpace)
+                   printf(" ");
+               else if (removeattr(wd->type) == word_Quote)
+                   printf(wd->aux == quote_Open ? "`" : "'");
+               break;
+           }
+#endif
+           if (removeattr(wd->type) == word_WhiteSpace)
+               spaces++;
+           if (wd == ldata->last)
+               break;
+           wd = wd->next;
+       }
+
+       if (spaces) {
+           ldata->space_adjust = (wid - len) / spaces;
+           /*
+            * This tells us how much the space width needs to
+            * change from _min_spacewidth. But we want to store
+            * its difference from the _natural_ space width, to
+            * make the text rendering easier.
+            */
+           ldata->space_adjust += ctx.minspacewidth;
+           ldata->space_adjust -= spacewidth;
+           /*
+            * Special case: on the last line of a paragraph, we
+            * never stretch spaces.
+            */
+           if (ldata->space_adjust > 0 && !p->next)
+               ldata->space_adjust = 0;
+       } else {
+           ldata->space_adjust = 0;
+       }
+
+       ldata->aux_text = NULL;
+       ldata->aux_left_indent = 0;
+    }
+
+}
+
+static page_data *page_breaks(line_data *first, line_data *last,
+                             int page_height)
+{
+    line_data *l, *m;
+    page_data *ph, *pt;
+
+    /*
+     * Page breaking is done by a close analogue of the optimal
+     * paragraph wrapping algorithm used by wrap_para(). We work
+     * backwards from the end of the document line by line; for
+     * each line, we contemplate every possible number of lines we
+     * could put on a page starting with that line, determine a
+     * cost function for each one, add it to the pre-computed cost
+     * function for optimally page-breaking everything after that
+     * page, and pick the best option.
+     * 
+     * Since my line_data structures are only used for this
+     * purpose, I might as well just store the algorithm data
+     * directly in them.
+     */
+
+    for (l = last; l; l = l->prev) {
+       int minheight, text = 0, space = 0;
+       int cost;
+
+       l->bestcost = -1;
+       for (m = l; m; m = m->next) {
+           if (m != l && m->page_break)
+               break;                 /* we've gone as far as we can */
+
+           if (m != l)
+               space += m->prev->space_after;
+           if (m != l || m->page_break)
+               space += m->space_before;
+           text += m->line_height;
+           minheight = text + space;
+
+           if (m != l && minheight > page_height)
+               break;
+
+           /*
+            * Compute the cost of this arrangement, as the square
+            * of the amount of wasted space on the page.
+            * Exception: if this is the last page before a
+            * mandatory break or the document end, we don't
+            * penalise a large blank area.
+            */
+           if (m->next && !m->next->page_break)
+           {
+               int x = page_height - minheight;
+               int xf;
+
+               xf = x & 0xFF;
+               x >>= 8;
+
+               cost = x*x;
+               cost += (x * xf) >> 8;
+           } else
+               cost = 0;
+
+           /*
+            * FIXME: here I should introduce penalties for
+            * breaking in mid-paragraph, particularly very close
+            * to one end of a paragraph and particularly in code
+            * paragraphs.
+            */
+
+           if (m->next && !m->next->page_break)
+               cost += m->next->bestcost;
+
+           if (l->bestcost == -1 || l->bestcost > cost) {
+               /*
+                * This is the best option yet for this starting
+                * point.
+                */
+               l->bestcost = cost;
+               if (m->next && !m->next->page_break)
+                   l->shortfall = page_height - minheight;
+               else
+                   l->shortfall = 0;
+               l->text = text;
+               l->space = space;
+               l->page_last = m;
+           }
+       }
+    }
+
+    /*
+     * Now go through the line list forwards and assemble the
+     * actual pages.
+     */
+    ph = pt = NULL;
+
+    l = first;
+    while (l) {
+       page_data *page;
+       int text, space;
+
+       page = mknew(page_data);
+       page->next = NULL;
+       page->prev = pt;
+       if (pt)
+           pt->next = page;
+       else
+           ph = page;
+       pt = page;
+
+       page->first_line = l;
+       page->last_line = l->page_last;
+
+       page->first_text = page->last_text = NULL;
+
+       /*
+        * Now assign a y-coordinate to each line on the page.
+        */
+       text = space = 0;
+       for (l = page->first_line; l; l = l->next) {
+           if (l != page->first_line)
+               space += l->prev->space_after;
+           if (l != page->first_line || l->page_break)
+               space += l->space_before;
+           text += l->line_height;
+
+           l->page = page;
+           l->ypos = text + space +
+               space * (float)page->first_line->shortfall /
+               page->first_line->space;
+
+           if (l == page->last_line)
+               break;
+       }
+
+       l = page->last_line->next;
+    }
+
+    return ph;
+}
+
+static void add_string_to_page(page_data *page, int x, int y,
+                              font_encoding *fe, int size, char *text)
+{
+    text_fragment *frag;
+
+    frag = mknew(text_fragment);
+    frag->next = NULL;
+
+    if (page->last_text)
+       page->last_text->next = frag;
+    else
+       page->first_text = frag;
+    page->last_text = frag;
+
+    frag->x = x;
+    frag->y = y;
+    frag->fe = fe;
+    frag->fontsize = size;
+    frag->text = dupstr(text);
+}
+
+/*
+ * Returns the updated x coordinate.
+ */
+static int render_string(page_data *page, font_data *font, int fontsize,
+                        int x, int y, wchar_t *str)
+{
+    char *text;
+    int textpos, textwid, glyph;
+    font_encoding *subfont = NULL, *sf;
+
+    text = mknewa(char, 1 + ustrlen(str));
+    textpos = textwid = 0;
+
+    while (*str) {
+       glyph = font->bmp[*str];
+
+       if (glyph == 0xFFFF)
+           continue;                  /* nothing more we can do here */
+
+       /*
+        * Find which subfont this character is going in.
+        */
+       sf = font->subfont_map[glyph].subfont;
+
+       if (!sf) {
+           int c;
+
+           /*
+            * This character is not yet in a subfont. Assign one.
+            */
+           if (font->latest_subfont->free_pos >= 0x100)
+               font->latest_subfont = new_font_encoding(font);
+
+           c = font->latest_subfont->free_pos++;
+           if (font->latest_subfont->free_pos == 0x7F)
+               font->latest_subfont->free_pos = 0xA1;
+
+           font->subfont_map[glyph].subfont = font->latest_subfont;
+           font->subfont_map[glyph].position = c;
+           font->latest_subfont->vector[c] = font->glyphs[glyph];
+           font->latest_subfont->indices[c] = glyph;
+           font->latest_subfont->to_unicode[c] = *str;
+
+           sf = font->latest_subfont;
+       }
+
+       if (!subfont || sf != subfont) {
+           if (subfont) {
+               text[textpos] = '\0';
+               add_string_to_page(page, x, y, subfont, fontsize, text);
+               x += textwid;
+           } else {
+               assert(textpos == 0);
+           }
+           textpos = 0;
+           subfont = sf;
+       }
+
+       text[textpos++] = font->subfont_map[glyph].position;
+       textwid += font->widths[glyph] * fontsize;
+
+       str++;
+    }
+
+    if (textpos > 0) {
+       text[textpos] = '\0';
+       add_string_to_page(page, x, y, subfont, fontsize, text);
+       x += textwid;
+    }
+
+    return x;
+}
+
+/*
+ * Returns the updated x coordinate.
+ */
+static int render_text(page_data *page, para_data *pdata, int x, int y,
+                      word *text, word *text_end, int space_adjust)
+{
+    while (text) {
+       int style, type, findex, errs;
+       wchar_t *str;
+
+       switch (text->type) {
+         case word_HyperLink:
+         case word_HyperEnd:
+         case word_UpperXref:
+         case word_LowerXref:
+         case word_XrefEnd:
+         case word_IndexRef:
+           goto nextword;
+           /*
+            * FIXME: we should do something with all of these!
+            * Hyperlinks and xrefs have meaning in PDF, and this
+            * is probably the right place to nail down the index
+            * references too.
+            */
+       }
+
+       style = towordstyle(text->type);
+       type = removeattr(text->type);
+
+       findex = (style == word_Normal ? FONT_NORMAL :
+                 style == word_Emph ? FONT_EMPH :
+                 FONT_CODE);
+
+       if (type == word_Normal) {
+           str = text->text;
+       } else if (type == word_WhiteSpace) {
+           x += pdata->sizes[findex] *
+               string_width(pdata->fonts[findex], L" ", NULL);
+           x += space_adjust;
+           goto nextword;
+       } else /* if (type == word_Quote) */ {
+           if (text->aux == quote_Open)
+               str = L"\x2018";               /* FIXME: configurability! */
+           else
+               str = L"\x2019";               /* FIXME: configurability! */
+       }
+
+       (void) string_width(pdata->fonts[findex], str, &errs);
+
+       if (errs && text->alt)
+           x = render_text(page, pdata, x, y, text->alt, NULL, space_adjust);
+       else
+           x = render_string(page, pdata->fonts[findex],
+                             pdata->sizes[findex], x, y, str);
+
+       nextword:
+       if (text == text_end)
+           break;
+       text = text->next;
+    }
+
+    return x;
+}
+
+static void render_line(line_data *ldata, int left_x, int top_y)
+{
+    if (ldata->aux_text)
+       render_text(ldata->page, ldata->pdata, left_x + ldata->aux_left_indent,
+                   top_y - ldata->ypos, ldata->aux_text, NULL, 0);
+    render_text(ldata->page, ldata->pdata, left_x + ldata->xpos,
+               top_y - ldata->ypos, ldata->first, ldata->last,
+               ldata->space_adjust);
+}
diff --git a/bk_pdf.c b/bk_pdf.c
new file mode 100644 (file)
index 0000000..44f7486
--- /dev/null
+++ b/bk_pdf.c
@@ -0,0 +1,467 @@
+/*
+ * PDF backend for Halibut
+ */
+
+#include <assert.h>
+#include "halibut.h"
+#include "paper.h"
+
+#define TREE_BRANCH 2                 /* max branching factor in page tree */
+
+paragraph *pdf_config_filename(char *filename)
+{
+    paragraph *p;
+    wchar_t *ufilename, *up;
+    int len;
+
+    p = mknew(paragraph);
+    memset(p, 0, sizeof(*p));
+    p->type = para_Config;
+    p->next = NULL;
+    p->fpos.filename = "<command line>";
+    p->fpos.line = p->fpos.col = -1;
+
+    ufilename = ufroma_dup(filename);
+    len = ustrlen(ufilename) + 2 + lenof(L"pdf-filename");
+    p->keyword = mknewa(wchar_t, len);
+    up = p->keyword;
+    ustrcpy(up, L"pdf-filename");
+    up = uadv(up);
+    ustrcpy(up, ufilename);
+    up = uadv(up);
+    *up = L'\0';
+    assert(up - p->keyword < len);
+    sfree(ufilename);
+
+    return p;
+}
+
+typedef struct object_Tag object;
+typedef struct objlist_Tag objlist;
+
+struct object_Tag {
+    objlist *list;
+    object *next;
+    int number;
+    rdstringc main, stream;
+    int size, fileoff;
+    char *final;
+};
+
+struct objlist_Tag {
+    int number;
+    object *head, *tail;
+};
+
+static object *new_object(objlist *list);
+static void objtext(object *o, char const *text);
+static void objstream(object *o, char const *text);
+static void objref(object *o, object *dest);
+
+static void make_pages_node(object *node, object *parent, page_data *first,
+                           page_data *last, object *resources);
+
+void pdf_backend(paragraph *sourceform, keywordlist *keywords,
+                indexdata *idx, void *vdoc) {
+    document *doc = (document *)vdoc;
+    int font_index;
+    font_encoding *fe;
+    page_data *page;
+    int pageno;
+    FILE *fp;
+    char *filename;
+    paragraph *p;
+    objlist olist;
+    object *o, *cat, *outlines, *pages, *resources;
+    int fileoff;
+
+    IGNORE(keywords);
+    IGNORE(idx);
+
+    filename = dupstr("output.pdf");
+    for (p = sourceform; p; p = p->next) {
+       p->private_data = NULL;
+       if (p->type == para_Config && p->parent) {
+           if (!ustricmp(p->keyword, L"pdf-filename")) {
+               sfree(filename);
+               filename = utoa_dup(uadv(p->keyword));
+           }
+       }
+    }
+
+    olist.head = olist.tail = NULL;
+    olist.number = 1;
+
+    cat = new_object(&olist);
+    outlines = new_object(&olist);
+    pages = new_object(&olist);
+    resources = new_object(&olist);
+
+    /*
+     * We currently don't support outlines, so here's a null
+     * outlines dictionary.
+     */
+    objtext(outlines, "<<\n/Type Outlines\n/Count 0\n>>\n");
+
+    /*
+     * The catalogue just contains references to the outlines and
+     * pages objects.
+     */
+    objtext(cat, "<<\n/Type /Catalog\n/Outlines ");
+    objref(cat, outlines);
+    objtext(cat, "\n/Pages ");
+    objref(cat, pages);
+    objtext(cat, "\n>>\n");
+
+    /*
+     * Set up the resources dictionary, which mostly means
+     * providing all the font objects and names to call them by.
+     */
+    font_index = 0;
+    objtext(resources, "<<\n/Font <<\n");
+    for (fe = doc->fonts->head; fe; fe = fe->next) {
+       char fname[40];
+       int i;
+       object *font;
+
+       sprintf(fname, "f%d", font_index++);
+       fe->name = dupstr(fname);
+
+       font = new_object(&olist);
+
+       objtext(resources, "/");
+       objtext(resources, fe->name);
+       objtext(resources, " ");
+       objref(resources, font);
+       objtext(resources, "\n");
+
+       objtext(font, "<<\n/Type /Font\n/Subtype /Type1\n/Name /");
+       objtext(font, fe->name);
+       objtext(font, "\n/BaseFont /");
+       objtext(font, fe->font->name);
+       objtext(font, "\n/Encoding <<\n/Type /Encoding\n/Differences [");
+
+       for (i = 0; i < 256; i++) {
+           char buf[20];
+           if (!fe->vector[i])
+               continue;
+           sprintf(buf, "\n%d /", i);
+           objtext(font, buf);
+           objtext(font, fe->vector[i] ? fe->vector[i] : ".notdef");
+       }
+
+       objtext(font, "\n]\n>>\n");
+
+       {
+           object *widths = new_object(&olist);
+           objtext(font, "/FirstChar 0\n/LastChar 255\n/Widths ");
+           objref(font, widths);
+           objtext(font, "\n");
+           objtext(widths, "[\n");
+           for (i = 0; i < 256; i++) {
+               char buf[80];
+               double width;
+               if (fe->indices[i] < 0)
+                   width = 0.0;
+               else
+                   width = fe->font->widths[fe->indices[i]];
+               sprintf(buf, "%g\n", 1000.0 * width / 4096.0);
+               objtext(widths, buf);
+           }
+           objtext(widths, "]\n");
+       }
+
+       objtext(font, ">>\n");
+    }
+    objtext(resources, ">>\n>>\n");
+
+    /*
+     * Define the page objects for each page, and get each one
+     * ready to have a `Parent' specification added to it.
+     */
+    for (page = doc->pages; page; page = page->next) {
+       object *opage;
+
+       opage = new_object(&olist);
+       page->spare = opage;
+       objtext(opage, "<<\n/Type /Page\n");
+    }
+
+    /*
+     * Recursively build the page tree.
+     */
+    make_pages_node(pages, NULL, doc->pages, NULL, resources);
+
+    /*
+     * Create and render the individual pages.
+     */
+    pageno = 0;
+    for (page = doc->pages; page; page = page->next) {
+       object *opage, *cstr;
+       text_fragment *frag;
+       char buf[256];
+
+       opage = (object *)page->spare;
+       /*
+        * At this point the page dictionary is already
+        * half-written, with /Type and /Parent already present. We
+        * continue from there.
+        */
+
+       /*
+        * The PDF spec says /Resources is required, but also says
+        * that it's inheritable and may be omitted if it's present
+        * in a Pages node. In our case it is: it's present in the
+        * topmost /Pages node because we carefully put it there.
+        * So we don't need a /Resources entry here.
+        */
+       sprintf(buf, "/MediaBox [0 0 %g %g]\n",
+               doc->paper_width / 4096.0, doc->paper_height / 4096.0);
+       objtext(opage, buf);
+
+       /*
+        * Now we're ready to define a content stream containing
+        * the actual text on the page.
+        */
+       cstr = new_object(&olist);
+       objtext(opage, "/Contents ");
+       objref(opage, cstr);
+       objtext(opage, "\n");
+
+       objstream(cstr, "BT\n");
+       for (frag = page->first_text; frag; frag = frag->next) {
+           char *c;
+
+           objstream(cstr, "/");
+           objstream(cstr, frag->fe->name);
+           sprintf(buf, " %d Tf 1 0 0 1 %g %g Tm (", frag->fontsize,
+                   frag->x/4096.0, frag->y/4096.0);
+           objstream(cstr, buf);
+
+           for (c = frag->text; *c; c++) {
+               if (*c == '(' || *c == ')' || *c == '\\')
+                   objstream(cstr, "\\");
+               buf[0] = *c;
+               buf[1] = '\0';
+               objstream(cstr, buf);
+           }
+
+           objstream(cstr, ") Tj\n");
+       }
+       objstream(cstr, "ET");
+
+       objtext(opage, ">>\n");
+    }
+
+    /*
+     * Assemble the final linear form of every object.
+     */
+    for (o = olist.head; o; o = o->next) {
+       rdstringc rs = {0, 0, NULL};
+       char text[80];
+
+       sprintf(text, "%d 0 obj\n", o->number);
+       rdaddsc(&rs, text);
+
+       if (!o->main.text && o->stream.text) {
+           sprintf(text, "<<\n/Length %d\n>>\n", o->stream.pos);
+           rdaddsc(&o->main, text);
+       }
+
+       assert(o->main.text);
+       rdaddsc(&rs, o->main.text);
+       sfree(o->main.text);
+
+       if (rs.text[rs.pos-1] != '\n')
+           rdaddc(&rs, '\n');
+
+       if (o->stream.text) {
+           /*
+            * FIXME: If we ever start compressing stream data then
+            * it will have zero bytes in it, so we'll have to be
+            * more careful than this.
+            */
+           rdaddsc(&rs, "stream\n");
+           rdaddsc(&rs, o->stream.text);
+           rdaddsc(&rs, "\nendstream\n");
+           sfree(o->stream.text);
+       }
+
+       rdaddsc(&rs, "endobj\n");
+
+       o->final = rs.text;
+       o->size = rs.pos;
+    }
+
+    /*
+     * Write out the PDF file.
+     */
+
+    fp = fopen(filename, "wb");
+    if (!fp) {
+       error(err_cantopenw, filename);
+       return;
+    }
+
+    /*
+     * Header
+     */
+    fileoff = fprintf(fp, "%%PDF-1.3\n");
+
+    /*
+     * Body
+     */
+    for (o = olist.head; o; o = o->next) {
+       o->fileoff = fileoff;
+       fwrite(o->final, 1, o->size, fp);
+       fileoff += o->size;
+    }
+
+    /*
+     * Cross-reference table
+     */
+    fprintf(fp, "xref\n");
+    assert(olist.head->number == 1);
+    fprintf(fp, "0 %d\n", olist.tail->number + 1);
+    fprintf(fp, "0000000000 65535 f \n");
+    for (o = olist.head; o; o = o->next) {
+       char entry[40];
+       sprintf(entry, "%010d 00000 n \n", o->fileoff);
+       assert(strlen(entry) == 20);
+       fputs(entry, fp);
+    }
+
+    /*
+     * Trailer
+     */
+    fprintf(fp, "trailer\n<<\n/Size %d\n/Root %d 0 R\n>>\n",
+           olist.tail->number + 1, cat->number);
+    fprintf(fp, "startxref\n%d\n%%%%EOF\n", fileoff);
+
+    fclose(fp);
+
+    sfree(filename);
+}
+
+static object *new_object(objlist *list)
+{
+    object *obj = mknew(object);
+
+    obj->list = list;
+
+    obj->main.text = NULL;
+    obj->main.pos = obj->main.size = 0;
+    obj->stream.text = NULL;
+    obj->stream.pos = obj->stream.size = 0;
+
+    obj->number = list->number++;
+
+    obj->next = NULL;
+    if (list->tail)
+       list->tail->next = obj;
+    else
+       list->head = obj;
+    list->tail = obj;
+
+    obj->size = 0;
+    obj->final = NULL;
+
+    return obj;
+}
+
+static void objtext(object *o, char const *text)
+{
+    rdaddsc(&o->main, text);
+}
+
+static void objstream(object *o, char const *text)
+{
+    rdaddsc(&o->stream, text);
+}
+
+static void objref(object *o, object *dest)
+{
+    char buf[40];
+    sprintf(buf, "%d 0 R", dest->number);
+    rdaddsc(&o->main, buf);
+}
+
+static void make_pages_node(object *node, object *parent, page_data *first,
+                           page_data *last, object *resources)
+{
+    int count;
+    page_data *page;
+    char buf[80];
+
+    objtext(node, "<<\n/Type /Pages\n");
+    if (parent) {
+       objtext(node, "/Parent ");
+       objref(node, parent);
+       objtext(node, "\n");
+    }
+
+    /*
+     * Count the pages in this stretch, to see if there are few
+     * enough to reference directly.
+     */
+    count = 0;
+    for (page = first; page; page = page->next) {
+       count++;
+       if (page == last)
+           break;
+    }
+
+    sprintf(buf, "/Count %d\n/Kids [\n", count);
+    objtext(node, buf);
+
+    if (count > TREE_BRANCH) {
+       int i;
+       page_data *thisfirst, *thislast;
+
+       page = first;
+
+       for (i = 0; i < TREE_BRANCH; i++) {
+           int number = (i+1) * count / TREE_BRANCH - i * count / TREE_BRANCH;
+           thisfirst = page;
+           while (number--) {
+               thislast = page;
+               page = page->next;
+           }
+
+           if (thisfirst == thislast) {
+               objref(node, (object *)thisfirst->spare);
+               objtext((object *)thisfirst->spare, "/Parent ");
+               objref((object *)thisfirst->spare, node);
+               objtext((object *)thisfirst->spare, "\n");
+           } else {
+               object *newnode = new_object(node->list);
+               make_pages_node(newnode, node, thisfirst, thislast, NULL);
+               objref(node, newnode);
+           }
+           objtext(node, "\n");
+       }
+
+       assert(thislast == last || page == NULL);
+
+    } else {
+       for (page = first; page; page = page->next) {
+           objref(node, (object *)page->spare);
+           objtext(node, "\n");
+           objtext((object *)page->spare, "/Parent ");
+           objref((object *)page->spare, node);
+           objtext((object *)page->spare, "\n");
+           if (page == last)
+               break;
+       }
+    }
+
+    objtext(node, "]\n");
+
+    if (resources) {
+       objtext(node, "/Resources ");
+       objref(node, resources);
+       objtext(node, "\n");
+    }
+
+    objtext(node, ">>\n");
+}
diff --git a/bk_ps.c b/bk_ps.c
new file mode 100644 (file)
index 0000000..8fe61c4
--- /dev/null
+++ b/bk_ps.c
@@ -0,0 +1,144 @@
+/*
+ * PostScript backend for Halibut
+ */
+
+#include <assert.h>
+#include "halibut.h"
+#include "paper.h"
+
+paragraph *ps_config_filename(char *filename)
+{
+    paragraph *p;
+    wchar_t *ufilename, *up;
+    int len;
+
+    p = mknew(paragraph);
+    memset(p, 0, sizeof(*p));
+    p->type = para_Config;
+    p->next = NULL;
+    p->fpos.filename = "<command line>";
+    p->fpos.line = p->fpos.col = -1;
+
+    ufilename = ufroma_dup(filename);
+    len = ustrlen(ufilename) + 2 + lenof(L"ps-filename");
+    p->keyword = mknewa(wchar_t, len);
+    up = p->keyword;
+    ustrcpy(up, L"ps-filename");
+    up = uadv(up);
+    ustrcpy(up, ufilename);
+    up = uadv(up);
+    *up = L'\0';
+    assert(up - p->keyword < len);
+    sfree(ufilename);
+
+    return p;
+}
+
+void ps_backend(paragraph *sourceform, keywordlist *keywords,
+               indexdata *idx, void *vdoc) {
+    document *doc = (document *)vdoc;
+    int font_index;
+    font_encoding *fe;
+    page_data *page;
+    int pageno;
+    FILE *fp;
+    char *filename;
+    paragraph *p;
+
+    IGNORE(keywords);
+    IGNORE(idx);
+
+    filename = dupstr("output.ps");
+    for (p = sourceform; p; p = p->next) {
+       p->private_data = NULL;
+       if (p->type == para_Config && p->parent) {
+           if (!ustricmp(p->keyword, L"ps-filename")) {
+               sfree(filename);
+               filename = utoa_dup(uadv(p->keyword));
+           }
+       }
+    }
+
+    fp = fopen(filename, "w");
+    if (!fp) {
+       error(err_cantopenw, filename);
+       return;
+    }
+
+    fprintf(fp, "%%!PS-Adobe-1.0\n");
+    for (pageno = 0, page = doc->pages; page; page = page->next)
+       pageno++;
+    fprintf(fp, "%%%%Pages: %d\n", pageno);
+    fprintf(fp, "%%%%EndComments\n");
+
+    fprintf(fp, "%%%%BeginProlog\n");
+    fprintf(fp, "%%%%EndProlog\n");
+
+    fprintf(fp, "%%%%BeginSetup\n");
+    /*
+     * Re-encode and re-metric the fonts.
+     */
+    font_index = 0;
+    for (fe = doc->fonts->head; fe; fe = fe->next) {
+       char fname[40];
+       int i;
+
+       sprintf(fname, "f%d", font_index++);
+       fe->name = dupstr(fname);
+
+       fprintf(fp, "/%s findfont dup length dict begin\n", fe->font->name);
+       fprintf(fp, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
+       fprintf(fp, "/Encoding [\n");
+       for (i = 0; i < 256; i++)
+           fprintf(fp, "/%s\n", fe->vector[i] ? fe->vector[i] : ".notdef");
+       fprintf(fp, "] def /Metrics 256 dict dup begin\n");
+       for (i = 0; i < 256; i++) {
+           if (fe->indices[i] >= 0) {
+               double width = fe->font->widths[fe->indices[i]];
+               fprintf(fp, "/%s %g def\n", fe->vector[i],
+                       1000.0 * width / 4096.0);
+           }
+       }
+       fprintf(fp, "end def currentdict end\n");
+       fprintf(fp, "/fontname-%s exch definefont /%s exch def\n\n",
+              fe->name, fe->name);
+    }
+    fprintf(fp, "%%%%EndSetup\n");
+
+    /*
+     * Output the text.
+     */
+    pageno = 0;
+    for (page = doc->pages; page; page = page->next) {
+       text_fragment *frag;
+
+       pageno++;
+       fprintf(fp, "%%%%Page: %d %d\n", pageno, pageno);
+       fprintf(fp, "%%%%BeginPageSetup\n");
+       fprintf(fp, "%%%%EndPageSetup\n");
+
+       for (frag = page->first_text; frag; frag = frag->next) {
+           char *c;
+
+           fprintf(fp, "%s %d scalefont setfont %g %g moveto (",
+                  frag->fe->name, frag->fontsize,
+                  frag->x/4096.0, frag->y/4096.0);
+
+           for (c = frag->text; *c; c++) {
+               if (*c == '(' || *c == ')' || *c == '\\')
+                   fputc('\\', fp);
+               fputc(*c, fp);
+           }
+
+           fprintf(fp, ") show\n");
+       }
+
+       fprintf(fp, "showpage\n");
+    }
+
+    fprintf(fp, "%%%%EOF\n");
+
+    fclose(fp);
+
+    sfree(filename);
+}
index fdb1055..359ea25 100644 (file)
--- a/bk_text.c
+++ b/bk_text.c
@@ -209,7 +209,7 @@ paragraph *text_config_filename(char *filename)
 }
 
 void text_backend(paragraph *sourceform, keywordlist *keywords,
-                 indexdata *idx) {
+                 indexdata *idx, void *unused) {
     paragraph *p;
     textconfig conf;
     word *prefix, *body, *wp;
@@ -219,6 +219,7 @@ void text_backend(paragraph *sourceform, keywordlist *keywords,
     int nesting, nestindent;
     int indentb, indenta;
 
+    IGNORE(unused);
     IGNORE(keywords);                 /* we don't happen to need this */
     IGNORE(idx);                      /* or this */
 
@@ -472,18 +473,20 @@ static void text_rdaddwc(rdstringc *rs, word *text, word *end) {
     }
 }
 
-static int text_width(word *);
+static int text_width(void *, word *);
 
-static int text_width_list(word *text) {
+static int text_width_list(void *ctx, word *text) {
     int w = 0;
     while (text) {
-       w += text_width(text);
+       w += text_width(ctx, text);
        text = text->next;
     }
     return w;
 }
 
-static int text_width(word *text) {
+static int text_width(void *ctx, word *text) {
+    IGNORE(ctx);
+
     switch (text->type) {
       case word_HyperLink:
       case word_HyperEnd:
@@ -504,7 +507,7 @@ static int text_width(word *text) {
                 : 0) +
                (text_convert(text->text, NULL) ?
                 ustrlen(text->text) :
-                text_width_list(text->alt)));
+                text_width_list(ctx, text->alt)));
 
       case word_WhiteSpace:
       case word_EmphSpace:
@@ -560,7 +563,7 @@ static void text_heading(FILE *fp, word *tprefix, word *nprefix, word *text,
        wrapwidth = indent + width;
     }
 
-    wrapping = wrap_para(text, firstlinewidth, wrapwidth, text_width);
+    wrapping = wrap_para(text, firstlinewidth, wrapwidth, text_width, NULL, 0);
     for (p = wrapping; p; p = p->next) {
        text_rdaddwc(&t, p->begin, p->end);
        length = (t.text ? strlen(t.text) : 0);
@@ -628,7 +631,7 @@ static void text_para(FILE *fp, word *prefix, char *prefixextra, word *text,
     } else
        e = indent + extraindent;
 
-    wrapping = wrap_para(text, firstlinewidth, width, text_width);
+    wrapping = wrap_para(text, firstlinewidth, width, text_width, NULL, 0);
     for (p = wrapping; p; p = p->next) {
        rdstringc t = { 0, 0, NULL };
        text_rdaddwc(&t, p->begin, p->end);
index 9575673..9655b1f 100644 (file)
--- a/bk_whlp.c
+++ b/bk_whlp.c
@@ -72,7 +72,7 @@ paragraph *whlp_config_filename(char *filename)
 }
 
 void whlp_backend(paragraph *sourceform, keywordlist *keywords,
-                 indexdata *idx) {
+                 indexdata *idx, void *unused) {
     WHLP h;
     char *filename, *cntname;
     paragraph *p, *lastsect;
@@ -83,6 +83,8 @@ void whlp_backend(paragraph *sourceform, keywordlist *keywords,
     indexentry *ie;
     int done_contents_topic = FALSE;
 
+    IGNORE(unused);
+
     h = state.h = whlp_new();
     state.keywords = keywords;
     state.idx = idx;
index 5a10137..439f9f9 100644 (file)
@@ -744,13 +744,15 @@ static void xhtml_free_file(xhtmlfile* xfile)
  * Main function.
  */
 void xhtml_backend(paragraph *sourceform, keywordlist *in_keywords,
-                  indexdata *in_idx)
+                  indexdata *in_idx, void *unused)
 {
 /*  int i;*/
   indexentry *ientry;
   int ti;
   xhtmlsection *xsect;
 
+  IGNORE(unused);    
+
   sourceparas = sourceform;
   conf = xhtml_configure(sourceform);
   keywords = in_keywords;
index fd93408..f9d22a2 100644 (file)
--- a/halibut.h
+++ b/halibut.h
@@ -326,7 +326,7 @@ struct tagWrappedLine {
     int nspaces;                      /* number of whitespaces in line */
     int shortfall;                    /* how much shorter than max width */
 };
-wrappedline *wrap_para(word *, int, int, int (*)(word *));
+wrappedline *wrap_para(word *, int, int, int (*)(void *, word *), void *, int);
 void wrap_free(wrappedline *);
 
 /*
@@ -422,31 +422,48 @@ struct userstyle_Tag {
 /*
  * bk_text.c
  */
-void text_backend(paragraph *, keywordlist *, indexdata *);
+void text_backend(paragraph *, keywordlist *, indexdata *, void *);
 paragraph *text_config_filename(char *filename);
 
 /*
  * bk_xhtml.c
  */
-void xhtml_backend(paragraph *, keywordlist *, indexdata *);
+void xhtml_backend(paragraph *, keywordlist *, indexdata *, void *);
 paragraph *xhtml_config_filename(char *filename);
 
 /*
  * bk_whlp.c
  */
-void whlp_backend(paragraph *, keywordlist *, indexdata *);
+void whlp_backend(paragraph *, keywordlist *, indexdata *, void *);
 paragraph *whlp_config_filename(char *filename);
 
 /*
  * bk_man.c
  */
-void man_backend(paragraph *, keywordlist *, indexdata *);
+void man_backend(paragraph *, keywordlist *, indexdata *, void *);
 paragraph *man_config_filename(char *filename);
 
 /*
  * bk_info.c
  */
-void info_backend(paragraph *, keywordlist *, indexdata *);
+void info_backend(paragraph *, keywordlist *, indexdata *, void *);
 paragraph *info_config_filename(char *filename);
 
+/*
+ * bk_paper.c
+ */
+void *paper_pre_backend(paragraph *, keywordlist *, indexdata *);
+
+/*
+ * bk_ps.c
+ */
+void ps_backend(paragraph *, keywordlist *, indexdata *, void *);
+paragraph *ps_config_filename(char *filename);
+
+/*
+ * bk_pdf.c
+ */
+void pdf_backend(paragraph *, keywordlist *, indexdata *, void *);
+paragraph *pdf_config_filename(char *filename);
+
 #endif
diff --git a/help.c b/help.c
index 3762a30..8104006 100644 (file)
--- a/help.c
+++ b/help.c
@@ -12,6 +12,8 @@ static char *helptext[] = {
     "         --winhelp[=filename]  generate Windows Help output",
     "         --man[=filename]      generate man page output",
     "         --info[=filename]     generate GNU info output",
+    "         --ps[=filename]       generate PostScript output",
+    "         --pdf[=filename]      generate PDF output",
     "         -Cfoo:bar:baz         append \\cfg{foo}{bar}{baz} to input",
     "         --precise             report column numbers in error messages",
     "         --help                display this text",
diff --git a/main.c b/main.c
index 4dec663..9af17b3 100644 (file)
--- a/main.c
+++ b/main.c
@@ -11,20 +11,29 @@ static void dbg_prtsource(paragraph *sourceform);
 static void dbg_prtwordlist(int level, word *w);
 static void dbg_prtkws(keywordlist *kws);
 
+static const struct pre_backend {
+    void *(*func)(paragraph *, keywordlist *, indexdata *);
+    int bitfield;
+} pre_backends[] = {
+    {paper_pre_backend, 0x0001}
+};
+
 static const struct backend {
     char *name;
-    void (*func)(paragraph *, keywordlist *, indexdata *);
+    void (*func)(paragraph *, keywordlist *, indexdata *, void *);
     paragraph *(*filename)(char *filename);
-    int bitfield;
+    int bitfield, prebackend_bitfield;
 } backends[] = {
-    {"text", text_backend, text_config_filename, 0x0001},
-    {"xhtml", xhtml_backend, xhtml_config_filename, 0x0002},
-    {"html", xhtml_backend, xhtml_config_filename, 0x0002},
-    {"hlp", whlp_backend, whlp_config_filename, 0x0004},
-    {"whlp", whlp_backend, whlp_config_filename, 0x0004},
-    {"winhelp", whlp_backend, whlp_config_filename, 0x0004},
-    {"man", man_backend, man_config_filename, 0x0008},
-    {"info", info_backend, info_config_filename, 0x0010},
+    {"text", text_backend, text_config_filename, 0x0001, 0},
+    {"xhtml", xhtml_backend, xhtml_config_filename, 0x0002, 0},
+    {"html", xhtml_backend, xhtml_config_filename, 0x0002, 0},
+    {"hlp", whlp_backend, whlp_config_filename, 0x0004, 0},
+    {"whlp", whlp_backend, whlp_config_filename, 0x0004, 0},
+    {"winhelp", whlp_backend, whlp_config_filename, 0x0004, 0},
+    {"man", man_backend, man_config_filename, 0x0008, 0},
+    {"info", info_backend, info_config_filename, 0x0010, 0},
+    {"ps", ps_backend, ps_config_filename, 0x0020, 0x0001},
+    {"pdf", pdf_backend, pdf_config_filename, 0x0040, 0x0001},
 };
 
 int main(int argc, char **argv) {
@@ -34,9 +43,10 @@ int main(int argc, char **argv) {
     int errs;
     int reportcols;
     int debug;
-    int backendbits;
+    int backendbits, prebackbits;
     int k, b;
     paragraph *cfg, *cfg_tail;
+    void *pre_backend_data[16];
 
     /*
      * Set up initial (default) parameters.
@@ -307,13 +317,39 @@ int main(int argc, char **argv) {
        }
 
        /*
+        * Select and run the pre-backends.
+        */
+       prebackbits = 0;
+       for (k = 0; k < (int)lenof(backends); k++)
+           if (backendbits == 0 || (backendbits & backends[k].bitfield))
+               prebackbits |= backends[k].prebackend_bitfield;
+       for (k = 0; k < (int)lenof(pre_backends); k++)
+           if (prebackbits & pre_backends[k].bitfield) {
+               assert(k < (int)lenof(pre_backend_data));
+               pre_backend_data[k] =
+                   pre_backends[k].func(sourceform, keywords, idx);
+           }
+
+       /*
         * Run the selected set of backends.
         */
        for (k = b = 0; k < (int)lenof(backends); k++)
            if (b != backends[k].bitfield) {
                b = backends[k].bitfield;
-               if (backendbits == 0 || (backendbits & b))
-                   backends[k].func(sourceform, keywords, idx);
+               if (backendbits == 0 || (backendbits & b)) {
+                   void *pbd = NULL;
+                   int pbb = backends[k].prebackend_bitfield;
+                   int m;
+
+                   for (m = 0; m < (int)lenof(pre_backends); m++)
+                       if (pbb & pre_backends[m].bitfield) {
+                           assert(m < (int)lenof(pre_backend_data));
+                           pbd = pre_backend_data[m];
+                           break;
+                       }
+                           
+                   backends[k].func(sourceform, keywords, idx, pbd);
+               }
            }
 
        free_para_list(sourceform);
diff --git a/misc.c b/misc.c
index 6d5497b..6f4ddd4 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -228,8 +228,39 @@ void mark_attr_ends(paragraph *sourceform) {
     }
 }
 
+/*
+ * This function implements the optimal paragraph wrapping
+ * algorithm, pretty much as used in TeX. A cost function is
+ * defined for each line of the wrapped paragraph (typically some
+ * convex function of the difference between the line's length and
+ * its desired length), and a dynamic programming approach is used
+ * to optimise globally across all possible layouts of the
+ * paragraph to find the one with the minimum total cost.
+ * 
+ * The function as implemented here gives a choice of two options
+ * for the cost function:
+ * 
+ *  - If `natural_space' is zero, then the algorithm attempts to
+ *    make each line the maximum possible width (either `width' or
+ *    `subsequentwidth' depending on whether it's the first line of
+ *    the paragraph or not), and the cost function is simply the
+ *    square of the unused space at the end of each line. This is a
+ *    simple mechanism suitable for use in fixed-pitch environments
+ *    such as plain text displayed on a terminal.
+ * 
+ *  - However, if `natural_space' is positive, the algorithm
+ *    assumes the medium is fully graphical and that the width of
+ *    space characters can be adjusted finely, and it attempts to
+ *    make each _space character_ the width given in
+ *    `natural_space'. (The provided width function should return
+ *    the _minimum_ acceptable width of a space character in this
+ *    case.) Therefore, the cost function for a line is dependent
+ *    on the number of spaces on that line as well as the amount by
+ *    which the line width differs from the optimum.
+ */
 wrappedline *wrap_para(word *text, int width, int subsequentwidth,
-                      int (*widthfn)(word *)) {
+                      int (*widthfn)(void *, word *), void *ctx,
+                      int natural_space) {
     wrappedline *head = NULL, **ptr = &head;
     int nwords, wordsize;
     struct wrapword {
@@ -254,7 +285,7 @@ wrappedline *wrap_para(word *text, int width, int subsequentwidth,
        wrapwords[nwords].width = 0;
        wrapwords[nwords].begin = text;
        while (text) {
-           wrapwords[nwords].width += widthfn(text);
+           wrapwords[nwords].width += widthfn(ctx, text);
            wrapwords[nwords].end = text->next;
            if (text->next && (text->next->type == word_WhiteSpace ||
                               text->next->type == word_EmphSpace ||
@@ -264,7 +295,7 @@ wrappedline *wrap_para(word *text, int width, int subsequentwidth,
        }
        if (text && text->next && (text->next->type == word_WhiteSpace ||
                           text->next->type == word_EmphSpace)) {
-           wrapwords[nwords].spacewidth = widthfn(text->next);
+           wrapwords[nwords].spacewidth = widthfn(ctx, text->next);
            text = text->next;
        } else {
            wrapwords[nwords].spacewidth = 0;
@@ -283,18 +314,20 @@ wrappedline *wrap_para(word *text, int width, int subsequentwidth,
        int best = -1;
        int bestcost = 0;
        int cost;
-       int linelen = 0, spacewidth = 0;
-       int seenspace;
+       int linelen = 0, spacewidth = 0, minspacewidth = 0;
+       int nspaces;
        int thiswidth = (i == 0 ? width : subsequentwidth);
 
        j = 0;
-       seenspace = 0;
+       nspaces = 0;
        while (i+j < nwords) {
            /*
             * See what happens if we put j+1 words on this line.
             */
-           if (spacewidth)
-               seenspace = 1;
+           if (spacewidth) {
+               nspaces++;
+               minspacewidth = spacewidth;
+           }
            linelen += spacewidth + wrapwords[i+j].width;
            spacewidth = wrapwords[i+j].spacewidth;
            j++;
@@ -308,17 +341,80 @@ wrappedline *wrap_para(word *text, int width, int subsequentwidth,
                if (best > 0)
                    break;
            }
-           if (i+j == nwords) {
-               /*
-                * Special case: if we're at the very end of the
-                * paragraph, we don't score penalty points for the
-                * white space left on the line.
-                */
-               cost = 0;
+
+           /*
+            * Compute the cost of this line. The method of doing
+            * this differs hugely depending on whether
+            * natural_space is nonzero or not.
+            */
+           if (natural_space) {
+               if (!nspaces && linelen > thiswidth) {
+                   /*
+                    * Special case: if there are no spaces at all
+                    * on the line because one single word is too
+                    * long for its line, cost is zero because
+                    * there's nothing we can do about it anyway.
+                    */
+                   cost = 0;
+               } else {
+                   int shortfall = thiswidth - linelen;
+                   int spaceextra = shortfall / (nspaces ? nspaces : 1);
+                   int spaceshortfall = natural_space -
+                       (minspacewidth + spaceextra);
+
+                   if (i+j == nwords && spaceshortfall < 0) {
+                       /*
+                        * Special case: on the very last line of
+                        * the paragraph, we don't score penalty
+                        * points for having to _stretch_ the line,
+                        * since we won't stretch it anyway.
+                        * However, we score penalties as normal
+                        * for having to squeeze it.
+                        */
+                       cost = 0;
+                   } else {
+                       /*
+                        * Squaring this number is tricky since
+                        * it's liable to be quite big. Let's
+                        * divide it through by 256.
+                        */
+                       int x = spaceshortfall >> 8;
+                       int xf = spaceshortfall & 0xFF;
+
+                       /*
+                        * Not counting strange variable-fixed-
+                        * point oddities, we are computing
+                        * 
+                        *   (x+xf)^2 = x^2 + 2*x*xf + xf*xf
+                        * 
+                        * except that _our_ xf is 256 times the
+                        * one listed there.
+                        */
+
+                       cost = x * x;
+                       cost += (2 * x * xf) >> 8;
+                   }
+               }
            } else {
-               cost = (thiswidth-linelen) * (thiswidth-linelen);
-               cost += wrapwords[i+j].cost;
+               if (i+j == nwords) {
+                   /*
+                    * Special case: if we're at the very end of the
+                    * paragraph, we don't score penalty points for the
+                    * white space left on the line.
+                    */
+                   cost = 0;
+               } else {
+                   cost = (thiswidth-linelen) * (thiswidth-linelen);
+               }
            }
+
+           /*
+            * Add in the cost of wrapping all lines after this
+            * point too.
+            */
+           if (i+j < nwords)
+               cost += wrapwords[i+j].cost;
+
            /*
             * We compare bestcost >= cost, not bestcost > cost,
             * because in cases where the costs are identical we
diff --git a/paper.h b/paper.h
new file mode 100644 (file)
index 0000000..6a4e353
--- /dev/null
+++ b/paper.h
@@ -0,0 +1,235 @@
+/*
+ * Paper printing definitions.
+ * 
+ * This header file defines data structures and constants which are
+ * shared between bk_paper.c and its clients bk_ps.c and bk_pdf.c.
+ */
+
+#ifndef HALIBUT_PAPER_H
+#define HALIBUT_PAPER_H
+
+typedef struct document_Tag document;
+typedef struct font_data_Tag font_data;
+typedef struct font_encoding_Tag font_encoding;
+typedef struct font_list_Tag font_list;
+typedef struct para_data_Tag para_data;
+typedef struct line_data_Tag line_data;
+typedef struct page_data_Tag page_data;
+typedef struct subfont_map_entry_Tag subfont_map_entry;
+typedef struct text_fragment_Tag text_fragment;
+
+/*
+ * This data structure represents the overall document, in the form
+ * it will be given to the client backends.
+ */
+struct document_Tag {
+    int paper_width, paper_height;
+    font_list *fonts;
+    page_data *pages;
+};
+
+/*
+ * This data structure represents a particular font.
+ */
+struct font_data_Tag {
+    /*
+     * Specify the PostScript name of the font and its point size.
+     */
+    const char *name;
+    /*
+     * An array of pointers to the available glyph names, and their
+     * corresponding character widths. These two arrays have
+     * parallel indices.
+     */
+    int nglyphs;
+    const char *const *glyphs;
+    const int *widths;
+    /*
+     * For reasonably speedy lookup, we set up a 65536-element
+     * table representing the Unicode BMP (I can conveniently
+     * restrict myself to the BMP for the moment since I happen to
+     * know that no glyph in the Adobe Glyph List falls outside
+     * it), whose elements are indices into the above two arrays.
+     */
+    unsigned short bmp[65536];
+    /*
+     * At some point I'm going to divide the font into sub-fonts
+     * with largely non-overlapping encoding vectors. This array
+     * will track which glyphs go into which subfonts. Also here I
+     * keep track of the latest subfont of any given font, so I can
+     * go back and extend its encoding.
+     */
+    subfont_map_entry *subfont_map;
+    font_encoding *latest_subfont;
+    /*
+     * The font list to which this font belongs.
+     */
+    font_list *list;
+};
+
+struct subfont_map_entry_Tag {
+    font_encoding *subfont;
+    unsigned char position;
+};
+
+/*
+ * This data structure represents a sub-font: a font with an
+ * encoding vector.
+ */
+struct font_encoding_Tag {
+    font_encoding *next;
+
+    char *name;                               /* used by client backends */
+
+    font_data *font;                  /* the parent font structure */
+    const char *vector[256];          /* the actual encoding vector */
+    int indices[256];                 /* indices back into main font struct */
+    wchar_t to_unicode[256];          /* PDF will want to know this */
+    int free_pos;                     /* space left to extend encoding */
+};
+
+/*
+ * This data structure represents the overall list of sub-fonts in
+ * the whole document.
+ */
+struct font_list_Tag {
+    font_encoding *head;
+    font_encoding *tail;
+};
+
+/*
+ * Constants defining array indices for the various fonts used in a
+ * paragraph.
+ */
+enum {
+    FONT_NORMAL,
+    FONT_EMPH,
+    FONT_CODE,
+    NFONTS
+};
+
+/*
+ * This is the data structure which is stored in the private_data
+ * field of each paragraph. It divides the paragraph up into a
+ * linked list of lines, while at the same time providing for those
+ * lines to be linked together into a much longer list spanning the
+ * whole document for page-breaking purposes.
+ */
+
+struct para_data_Tag {
+    /*
+     * Data about the fonts used in this paragraph. Indices are the
+     * FONT_* constants defined above.
+     */
+    font_data *fonts[NFONTS];
+    int sizes[NFONTS];
+    /*
+     * Pointers to the first and last line of the paragraph. The
+     * line structures are linked into a list, which runs from
+     * `first' to `last' as might be expected. However, the list
+     * does not terminate there: first->prev will end up pointing
+     * to the last line of the previous paragraph in most cases,
+     * and likewise last->next will point to the first line of the
+     * next paragraph.
+     */
+    line_data *first;                 /* first line in paragraph */
+    line_data *last;                  /* last line in paragraph */
+};
+
+struct line_data_Tag {
+    /*
+     * The parent paragraph.
+     */
+    para_data *pdata;
+    /*
+     * Pointers to join lines into a linked list.
+     */
+    line_data *prev;
+    line_data *next;
+    /*
+     * The extent of the text displayed on this line. Also mention
+     * its starting x position, and by how much the width of spaces
+     * needs to be adjusted for paragraph justification.
+     * 
+     * (`last' may be NULL if it's more convenient.)
+     */
+    word *first;
+    word *last;
+    int xpos;
+    int space_adjust;                 /* for justifying paragraphs */
+    /*
+     * Auxiliary text: a section number in a margin, or a list item
+     * bullet or number. Also mention where to display this text
+     * relative to the left margin.
+     */
+    word *aux_text;
+    int aux_left_indent;
+    /*
+     * This line might have a non-negotiable page break before it.
+     * Also there will be space required above and below it; also I
+     * store the physical line height (defined as the maximum of
+     * the heights of the three fonts in the pdata) because it's
+     * easier than looking it up repeatedly during page breaking.
+     */
+    int page_break;
+    int space_before;
+    int space_after;
+    int line_height;
+    /*
+     * These fields are used in the page breaking algorithm.
+     */
+    int bestcost;
+    int shortfall, text, space;
+    line_data *page_last;             /* last line on a page starting here */
+    /*
+     * After page breaking, we can assign an actual y-coordinate on
+     * the page to each line. Also we store a pointer back to the
+     * page structure itself.
+     */
+    int ypos;
+    page_data *page;
+};
+
+/*
+ * This data structure is constructed to describe each page of the
+ * printed output.
+ */
+struct page_data_Tag {
+    /*
+     * Pointers to join pages into a linked list.
+     */
+    page_data *prev;
+    page_data *next;
+    /*
+     * The set of lines displayed on this page.
+     */
+    line_data *first_line;
+    line_data *last_line;
+    /*
+     * After text rendering: the set of actual pieces of text
+     * needing to be displayed on this page.
+     */
+    text_fragment *first_text;
+    text_fragment *last_text;
+    /*
+     * This spare pointer field is for use by the client backends.
+     */
+    void *spare;
+};
+
+struct text_fragment_Tag {
+    text_fragment *next;
+    int x, y;
+    font_encoding *fe;
+    int fontsize;
+    char *text;
+};
+
+/*
+ * Functions and data exported from psdata.c.
+ */
+wchar_t ps_glyph_to_unicode(char const *glyph);
+extern const char *const ps_std_glyphs[];
+const int *ps_std_font_widths(char const *fontname);
+
+#endif
diff --git a/psdata.c b/psdata.c
new file mode 100644 (file)
index 0000000..aa5fe10
--- /dev/null
+++ b/psdata.c
@@ -0,0 +1,2114 @@
+/*
+ * Data, and supporting functions, for PostScript-based output
+ * formats.
+ */
+
+#include "halibut.h"
+
+/* ----------------------------------------------------------------------
+ * Mapping between PS character names (/aacute, /zcaron etc) and
+ * Unicode code points.
+ * 
+ * Generated from the Adobe Glyph List at
+ * 
+ *   http://partners.adobe.com/asn/tech/type/glyphlist.txt
+ * 
+ * by a succession of Perl/sh fragments, quoted alongside each
+ * array.
+ */
+
+/*
+
+grep '^[^#;][^;]*;[^ ][^ ][^ ][^ ]$' glyphlist.txt | sort -t\; +0 -1 | \
+    cut -f1 -d\; | perl -ne 'chomp; print "\"$_\", "' | \
+    fold -s -w68 | sed 's/^/    /'; echo
+
+ */
+static const char *const ps_glyphs_alphabetic[] = {
+    "A", "AE", "AEacute", "AEmacron", "AEsmall", "Aacute", 
+    "Aacutesmall", "Abreve", "Abreveacute", "Abrevecyrillic", 
+    "Abrevedotbelow", "Abrevegrave", "Abrevehookabove", "Abrevetilde", 
+    "Acaron", "Acircle", "Acircumflex", "Acircumflexacute", 
+    "Acircumflexdotbelow", "Acircumflexgrave", "Acircumflexhookabove", 
+    "Acircumflexsmall", "Acircumflextilde", "Acute", "Acutesmall", 
+    "Acyrillic", "Adblgrave", "Adieresis", "Adieresiscyrillic", 
+    "Adieresismacron", "Adieresissmall", "Adotbelow", "Adotmacron", 
+    "Agrave", "Agravesmall", "Ahookabove", "Aiecyrillic", 
+    "Ainvertedbreve", "Alpha", "Alphatonos", "Amacron", "Amonospace", 
+    "Aogonek", "Aring", "Aringacute", "Aringbelow", "Aringsmall", 
+    "Asmall", "Atilde", "Atildesmall", "Aybarmenian", "B", "Bcircle", 
+    "Bdotaccent", "Bdotbelow", "Becyrillic", "Benarmenian", "Beta", 
+    "Bhook", "Blinebelow", "Bmonospace", "Brevesmall", "Bsmall", 
+    "Btopbar", "C", "Caarmenian", "Cacute", "Caron", "Caronsmall", 
+    "Ccaron", "Ccedilla", "Ccedillaacute", "Ccedillasmall", "Ccircle", 
+    "Ccircumflex", "Cdot", "Cdotaccent", "Cedillasmall", "Chaarmenian", 
+    "Cheabkhasiancyrillic", "Checyrillic", 
+    "Chedescenderabkhasiancyrillic", "Chedescendercyrillic", 
+    "Chedieresiscyrillic", "Cheharmenian", "Chekhakassiancyrillic", 
+    "Cheverticalstrokecyrillic", "Chi", "Chook", "Circumflexsmall", 
+    "Cmonospace", "Coarmenian", "Csmall", "D", "DZ", "DZcaron", 
+    "Daarmenian", "Dafrican", "Dcaron", "Dcedilla", "Dcircle", 
+    "Dcircumflexbelow", "Dcroat", "Ddotaccent", "Ddotbelow", 
+    "Decyrillic", "Deicoptic", "Delta", "Deltagreek", "Dhook", 
+    "Dieresis", "DieresisAcute", "DieresisGrave", "Dieresissmall", 
+    "Digammagreek", "Djecyrillic", "Dlinebelow", "Dmonospace", 
+    "Dotaccentsmall", "Dslash", "Dsmall", "Dtopbar", "Dz", "Dzcaron", 
+    "Dzeabkhasiancyrillic", "Dzecyrillic", "Dzhecyrillic", "E", 
+    "Eacute", "Eacutesmall", "Ebreve", "Ecaron", "Ecedillabreve", 
+    "Echarmenian", "Ecircle", "Ecircumflex", "Ecircumflexacute", 
+    "Ecircumflexbelow", "Ecircumflexdotbelow", "Ecircumflexgrave", 
+    "Ecircumflexhookabove", "Ecircumflexsmall", "Ecircumflextilde", 
+    "Ecyrillic", "Edblgrave", "Edieresis", "Edieresissmall", "Edot", 
+    "Edotaccent", "Edotbelow", "Efcyrillic", "Egrave", "Egravesmall", 
+    "Eharmenian", "Ehookabove", "Eightroman", "Einvertedbreve", 
+    "Eiotifiedcyrillic", "Elcyrillic", "Elevenroman", "Emacron", 
+    "Emacronacute", "Emacrongrave", "Emcyrillic", "Emonospace", 
+    "Encyrillic", "Endescendercyrillic", "Eng", "Enghecyrillic", 
+    "Enhookcyrillic", "Eogonek", "Eopen", "Epsilon", "Epsilontonos", 
+    "Ercyrillic", "Ereversed", "Ereversedcyrillic", "Escyrillic", 
+    "Esdescendercyrillic", "Esh", "Esmall", "Eta", "Etarmenian", 
+    "Etatonos", "Eth", "Ethsmall", "Etilde", "Etildebelow", "Euro", 
+    "Ezh", "Ezhcaron", "Ezhreversed", "F", "Fcircle", "Fdotaccent", 
+    "Feharmenian", "Feicoptic", "Fhook", "Fitacyrillic", "Fiveroman", 
+    "Fmonospace", "Fourroman", "Fsmall", "G", "GBsquare", "Gacute", 
+    "Gamma", "Gammaafrican", "Gangiacoptic", "Gbreve", "Gcaron", 
+    "Gcedilla", "Gcircle", "Gcircumflex", "Gcommaaccent", "Gdot", 
+    "Gdotaccent", "Gecyrillic", "Ghadarmenian", 
+    "Ghemiddlehookcyrillic", "Ghestrokecyrillic", "Gheupturncyrillic", 
+    "Ghook", "Gimarmenian", "Gjecyrillic", "Gmacron", "Gmonospace", 
+    "Grave", "Gravesmall", "Gsmall", "Gsmallhook", "Gstroke", "H", 
+    "H18533", "H18543", "H18551", "H22073", "HPsquare", 
+    "Haabkhasiancyrillic", "Hadescendercyrillic", "Hardsigncyrillic", 
+    "Hbar", "Hbrevebelow", "Hcedilla", "Hcircle", "Hcircumflex", 
+    "Hdieresis", "Hdotaccent", "Hdotbelow", "Hmonospace", "Hoarmenian", 
+    "Horicoptic", "Hsmall", "Hungarumlaut", "Hungarumlautsmall", 
+    "Hzsquare", "I", "IAcyrillic", "IJ", "IUcyrillic", "Iacute", 
+    "Iacutesmall", "Ibreve", "Icaron", "Icircle", "Icircumflex", 
+    "Icircumflexsmall", "Icyrillic", "Idblgrave", "Idieresis", 
+    "Idieresisacute", "Idieresiscyrillic", "Idieresissmall", "Idot", 
+    "Idotaccent", "Idotbelow", "Iebrevecyrillic", "Iecyrillic", 
+    "Ifraktur", "Igrave", "Igravesmall", "Ihookabove", "Iicyrillic", 
+    "Iinvertedbreve", "Iishortcyrillic", "Imacron", "Imacroncyrillic", 
+    "Imonospace", "Iniarmenian", "Iocyrillic", "Iogonek", "Iota", 
+    "Iotaafrican", "Iotadieresis", "Iotatonos", "Ismall", "Istroke", 
+    "Itilde", "Itildebelow", "Izhitsacyrillic", 
+    "Izhitsadblgravecyrillic", "J", "Jaarmenian", "Jcircle", 
+    "Jcircumflex", "Jecyrillic", "Jheharmenian", "Jmonospace", 
+    "Jsmall", "K", "KBsquare", "KKsquare", "Kabashkircyrillic", 
+    "Kacute", "Kacyrillic", "Kadescendercyrillic", "Kahookcyrillic", 
+    "Kappa", "Kastrokecyrillic", "Kaverticalstrokecyrillic", "Kcaron", 
+    "Kcedilla", "Kcircle", "Kcommaaccent", "Kdotbelow", "Keharmenian", 
+    "Kenarmenian", "Khacyrillic", "Kheicoptic", "Khook", "Kjecyrillic", 
+    "Klinebelow", "Kmonospace", "Koppacyrillic", "Koppagreek", 
+    "Ksicyrillic", "Ksmall", "L", "LJ", "LL", "Lacute", "Lambda", 
+    "Lcaron", "Lcedilla", "Lcircle", "Lcircumflexbelow", 
+    "Lcommaaccent", "Ldot", "Ldotaccent", "Ldotbelow", 
+    "Ldotbelowmacron", "Liwnarmenian", "Lj", "Ljecyrillic", 
+    "Llinebelow", "Lmonospace", "Lslash", "Lslashsmall", "Lsmall", "M", 
+    "MBsquare", "Macron", "Macronsmall", "Macute", "Mcircle", 
+    "Mdotaccent", "Mdotbelow", "Menarmenian", "Mmonospace", "Msmall", 
+    "Mturned", "Mu", "N", "NJ", "Nacute", "Ncaron", "Ncedilla", 
+    "Ncircle", "Ncircumflexbelow", "Ncommaaccent", "Ndotaccent", 
+    "Ndotbelow", "Nhookleft", "Nineroman", "Nj", "Njecyrillic", 
+    "Nlinebelow", "Nmonospace", "Nowarmenian", "Nsmall", "Ntilde", 
+    "Ntildesmall", "Nu", "O", "OE", "OEsmall", "Oacute", "Oacutesmall", 
+    "Obarredcyrillic", "Obarreddieresiscyrillic", "Obreve", "Ocaron", 
+    "Ocenteredtilde", "Ocircle", "Ocircumflex", "Ocircumflexacute", 
+    "Ocircumflexdotbelow", "Ocircumflexgrave", "Ocircumflexhookabove", 
+    "Ocircumflexsmall", "Ocircumflextilde", "Ocyrillic", "Odblacute", 
+    "Odblgrave", "Odieresis", "Odieresiscyrillic", "Odieresissmall", 
+    "Odotbelow", "Ogoneksmall", "Ograve", "Ogravesmall", "Oharmenian", 
+    "Ohm", "Ohookabove", "Ohorn", "Ohornacute", "Ohorndotbelow", 
+    "Ohorngrave", "Ohornhookabove", "Ohorntilde", "Ohungarumlaut", 
+    "Oi", "Oinvertedbreve", "Omacron", "Omacronacute", "Omacrongrave", 
+    "Omega", "Omegacyrillic", "Omegagreek", "Omegaroundcyrillic", 
+    "Omegatitlocyrillic", "Omegatonos", "Omicron", "Omicrontonos", 
+    "Omonospace", "Oneroman", "Oogonek", "Oogonekmacron", "Oopen", 
+    "Oslash", "Oslashacute", "Oslashsmall", "Osmall", "Ostrokeacute", 
+    "Otcyrillic", "Otilde", "Otildeacute", "Otildedieresis", 
+    "Otildesmall", "P", "Pacute", "Pcircle", "Pdotaccent", 
+    "Pecyrillic", "Peharmenian", "Pemiddlehookcyrillic", "Phi", 
+    "Phook", "Pi", "Piwrarmenian", "Pmonospace", "Psi", "Psicyrillic", 
+    "Psmall", "Q", "Qcircle", "Qmonospace", "Qsmall", "R", 
+    "Raarmenian", "Racute", "Rcaron", "Rcedilla", "Rcircle", 
+    "Rcommaaccent", "Rdblgrave", "Rdotaccent", "Rdotbelow", 
+    "Rdotbelowmacron", "Reharmenian", "Rfraktur", "Rho", "Ringsmall", 
+    "Rinvertedbreve", "Rlinebelow", "Rmonospace", "Rsmall", 
+    "Rsmallinverted", "Rsmallinvertedsuperior", "S", "SF010000", 
+    "SF020000", "SF030000", "SF040000", "SF050000", "SF060000", 
+    "SF070000", "SF080000", "SF090000", "SF100000", "SF110000", 
+    "SF190000", "SF200000", "SF210000", "SF220000", "SF230000", 
+    "SF240000", "SF250000", "SF260000", "SF270000", "SF280000", 
+    "SF360000", "SF370000", "SF380000", "SF390000", "SF400000", 
+    "SF410000", "SF420000", "SF430000", "SF440000", "SF450000", 
+    "SF460000", "SF470000", "SF480000", "SF490000", "SF500000", 
+    "SF510000", "SF520000", "SF530000", "SF540000", "Sacute", 
+    "Sacutedotaccent", "Sampigreek", "Scaron", "Scarondotaccent", 
+    "Scaronsmall", "Scedilla", "Schwa", "Schwacyrillic", 
+    "Schwadieresiscyrillic", "Scircle", "Scircumflex", "Scommaaccent", 
+    "Sdotaccent", "Sdotbelow", "Sdotbelowdotaccent", "Seharmenian", 
+    "Sevenroman", "Shaarmenian", "Shacyrillic", "Shchacyrillic", 
+    "Sheicoptic", "Shhacyrillic", "Shimacoptic", "Sigma", "Sixroman", 
+    "Smonospace", "Softsigncyrillic", "Ssmall", "Stigmagreek", "T", 
+    "Tau", "Tbar", "Tcaron", "Tcedilla", "Tcircle", "Tcircumflexbelow", 
+    "Tcommaaccent", "Tdotaccent", "Tdotbelow", "Tecyrillic", 
+    "Tedescendercyrillic", "Tenroman", "Tetsecyrillic", "Theta", 
+    "Thook", "Thorn", "Thornsmall", "Threeroman", "Tildesmall", 
+    "Tiwnarmenian", "Tlinebelow", "Tmonospace", "Toarmenian", 
+    "Tonefive", "Tonesix", "Tonetwo", "Tretroflexhook", "Tsecyrillic", 
+    "Tshecyrillic", "Tsmall", "Twelveroman", "Tworoman", "U", "Uacute", 
+    "Uacutesmall", "Ubreve", "Ucaron", "Ucircle", "Ucircumflex", 
+    "Ucircumflexbelow", "Ucircumflexsmall", "Ucyrillic", "Udblacute", 
+    "Udblgrave", "Udieresis", "Udieresisacute", "Udieresisbelow", 
+    "Udieresiscaron", "Udieresiscyrillic", "Udieresisgrave", 
+    "Udieresismacron", "Udieresissmall", "Udotbelow", "Ugrave", 
+    "Ugravesmall", "Uhookabove", "Uhorn", "Uhornacute", 
+    "Uhorndotbelow", "Uhorngrave", "Uhornhookabove", "Uhorntilde", 
+    "Uhungarumlaut", "Uhungarumlautcyrillic", "Uinvertedbreve", 
+    "Ukcyrillic", "Umacron", "Umacroncyrillic", "Umacrondieresis", 
+    "Umonospace", "Uogonek", "Upsilon", "Upsilon1", 
+    "Upsilonacutehooksymbolgreek", "Upsilonafrican", "Upsilondieresis", 
+    "Upsilondieresishooksymbolgreek", "Upsilonhooksymbol", 
+    "Upsilontonos", "Uring", "Ushortcyrillic", "Usmall", 
+    "Ustraightcyrillic", "Ustraightstrokecyrillic", "Utilde", 
+    "Utildeacute", "Utildebelow", "V", "Vcircle", "Vdotbelow", 
+    "Vecyrillic", "Vewarmenian", "Vhook", "Vmonospace", "Voarmenian", 
+    "Vsmall", "Vtilde", "W", "Wacute", "Wcircle", "Wcircumflex", 
+    "Wdieresis", "Wdotaccent", "Wdotbelow", "Wgrave", "Wmonospace", 
+    "Wsmall", "X", "Xcircle", "Xdieresis", "Xdotaccent", "Xeharmenian", 
+    "Xi", "Xmonospace", "Xsmall", "Y", "Yacute", "Yacutesmall", 
+    "Yatcyrillic", "Ycircle", "Ycircumflex", "Ydieresis", 
+    "Ydieresissmall", "Ydotaccent", "Ydotbelow", "Yericyrillic", 
+    "Yerudieresiscyrillic", "Ygrave", "Yhook", "Yhookabove", 
+    "Yiarmenian", "Yicyrillic", "Yiwnarmenian", "Ymonospace", "Ysmall", 
+    "Ytilde", "Yusbigcyrillic", "Yusbigiotifiedcyrillic", 
+    "Yuslittlecyrillic", "Yuslittleiotifiedcyrillic", "Z", 
+    "Zaarmenian", "Zacute", "Zcaron", "Zcaronsmall", "Zcircle", 
+    "Zcircumflex", "Zdot", "Zdotaccent", "Zdotbelow", "Zecyrillic", 
+    "Zedescendercyrillic", "Zedieresiscyrillic", "Zeta", "Zhearmenian", 
+    "Zhebrevecyrillic", "Zhecyrillic", "Zhedescendercyrillic", 
+    "Zhedieresiscyrillic", "Zlinebelow", "Zmonospace", "Zsmall", 
+    "Zstroke", "a", "aabengali", "aacute", "aadeva", "aagujarati", 
+    "aagurmukhi", "aamatragurmukhi", "aarusquare", 
+    "aavowelsignbengali", "aavowelsigndeva", "aavowelsigngujarati", 
+    "abbreviationmarkarmenian", "abbreviationsigndeva", "abengali", 
+    "abopomofo", "abreve", "abreveacute", "abrevecyrillic", 
+    "abrevedotbelow", "abrevegrave", "abrevehookabove", "abrevetilde", 
+    "acaron", "acircle", "acircumflex", "acircumflexacute", 
+    "acircumflexdotbelow", "acircumflexgrave", "acircumflexhookabove", 
+    "acircumflextilde", "acute", "acutebelowcmb", "acutecmb", 
+    "acutecomb", "acutedeva", "acutelowmod", "acutetonecmb", 
+    "acyrillic", "adblgrave", "addakgurmukhi", "adeva", "adieresis", 
+    "adieresiscyrillic", "adieresismacron", "adotbelow", "adotmacron", 
+    "ae", "aeacute", "aekorean", "aemacron", "afii00208", "afii08941", 
+    "afii10017", "afii10018", "afii10019", "afii10020", "afii10021", 
+    "afii10022", "afii10023", "afii10024", "afii10025", "afii10026", 
+    "afii10027", "afii10028", "afii10029", "afii10030", "afii10031", 
+    "afii10032", "afii10033", "afii10034", "afii10035", "afii10036", 
+    "afii10037", "afii10038", "afii10039", "afii10040", "afii10041", 
+    "afii10042", "afii10043", "afii10044", "afii10045", "afii10046", 
+    "afii10047", "afii10048", "afii10049", "afii10050", "afii10051", 
+    "afii10052", "afii10053", "afii10054", "afii10055", "afii10056", 
+    "afii10057", "afii10058", "afii10059", "afii10060", "afii10061", 
+    "afii10062", "afii10063", "afii10064", "afii10065", "afii10066", 
+    "afii10067", "afii10068", "afii10069", "afii10070", "afii10071", 
+    "afii10072", "afii10073", "afii10074", "afii10075", "afii10076", 
+    "afii10077", "afii10078", "afii10079", "afii10080", "afii10081", 
+    "afii10082", "afii10083", "afii10084", "afii10085", "afii10086", 
+    "afii10087", "afii10088", "afii10089", "afii10090", "afii10091", 
+    "afii10092", "afii10093", "afii10094", "afii10095", "afii10096", 
+    "afii10097", "afii10098", "afii10099", "afii10100", "afii10101", 
+    "afii10102", "afii10103", "afii10104", "afii10105", "afii10106", 
+    "afii10107", "afii10108", "afii10109", "afii10110", "afii10145", 
+    "afii10146", "afii10147", "afii10148", "afii10192", "afii10193", 
+    "afii10194", "afii10195", "afii10196", "afii10831", "afii10832", 
+    "afii10846", "afii299", "afii300", "afii301", "afii57381", 
+    "afii57388", "afii57392", "afii57393", "afii57394", "afii57395", 
+    "afii57396", "afii57397", "afii57398", "afii57399", "afii57400", 
+    "afii57401", "afii57403", "afii57407", "afii57409", "afii57410", 
+    "afii57411", "afii57412", "afii57413", "afii57414", "afii57415", 
+    "afii57416", "afii57417", "afii57418", "afii57419", "afii57420", 
+    "afii57421", "afii57422", "afii57423", "afii57424", "afii57425", 
+    "afii57426", "afii57427", "afii57428", "afii57429", "afii57430", 
+    "afii57431", "afii57432", "afii57433", "afii57434", "afii57440", 
+    "afii57441", "afii57442", "afii57443", "afii57444", "afii57445", 
+    "afii57446", "afii57448", "afii57449", "afii57450", "afii57451", 
+    "afii57452", "afii57453", "afii57454", "afii57455", "afii57456", 
+    "afii57457", "afii57458", "afii57470", "afii57505", "afii57506", 
+    "afii57507", "afii57508", "afii57509", "afii57511", "afii57512", 
+    "afii57513", "afii57514", "afii57519", "afii57534", "afii57636", 
+    "afii57645", "afii57658", "afii57664", "afii57665", "afii57666", 
+    "afii57667", "afii57668", "afii57669", "afii57670", "afii57671", 
+    "afii57672", "afii57673", "afii57674", "afii57675", "afii57676", 
+    "afii57677", "afii57678", "afii57679", "afii57680", "afii57681", 
+    "afii57682", "afii57683", "afii57684", "afii57685", "afii57686", 
+    "afii57687", "afii57688", "afii57689", "afii57690", "afii57694", 
+    "afii57695", "afii57700", "afii57705", "afii57716", "afii57717", 
+    "afii57718", "afii57723", "afii57793", "afii57794", "afii57795", 
+    "afii57796", "afii57797", "afii57798", "afii57799", "afii57800", 
+    "afii57801", "afii57802", "afii57803", "afii57804", "afii57806", 
+    "afii57807", "afii57839", "afii57841", "afii57842", "afii57929", 
+    "afii61248", "afii61289", "afii61352", "afii61573", "afii61574", 
+    "afii61575", "afii61664", "afii63167", "afii64937", "agrave", 
+    "agujarati", "agurmukhi", "ahiragana", "ahookabove", "aibengali", 
+    "aibopomofo", "aideva", "aiecyrillic", "aigujarati", "aigurmukhi", 
+    "aimatragurmukhi", "ainarabic", "ainfinalarabic", 
+    "aininitialarabic", "ainmedialarabic", "ainvertedbreve", 
+    "aivowelsignbengali", "aivowelsigndeva", "aivowelsigngujarati", 
+    "akatakana", "akatakanahalfwidth", "akorean", "alef", "alefarabic", 
+    "alefdageshhebrew", "aleffinalarabic", "alefhamzaabovearabic", 
+    "alefhamzaabovefinalarabic", "alefhamzabelowarabic", 
+    "alefhamzabelowfinalarabic", "alefhebrew", "aleflamedhebrew", 
+    "alefmaddaabovearabic", "alefmaddaabovefinalarabic", 
+    "alefmaksuraarabic", "alefmaksurafinalarabic", 
+    "alefmaksurainitialarabic", "alefmaksuramedialarabic", 
+    "alefpatahhebrew", "alefqamatshebrew", "aleph", "allequal", 
+    "alpha", "alphatonos", "amacron", "amonospace", "ampersand", 
+    "ampersandmonospace", "ampersandsmall", "amsquare", "anbopomofo", 
+    "angbopomofo", "angkhankhuthai", "angle", "anglebracketleft", 
+    "anglebracketleftvertical", "anglebracketright", 
+    "anglebracketrightvertical", "angleleft", "angleright", "angstrom", 
+    "anoteleia", "anudattadeva", "anusvarabengali", "anusvaradeva", 
+    "anusvaragujarati", "aogonek", "apaatosquare", "aparen", 
+    "apostrophearmenian", "apostrophemod", "apple", "approaches", 
+    "approxequal", "approxequalorimage", "approximatelyequal", 
+    "araeaekorean", "araeakorean", "arc", "arighthalfring", "aring", 
+    "aringacute", "aringbelow", "arrowboth", "arrowdashdown", 
+    "arrowdashleft", "arrowdashright", "arrowdashup", "arrowdblboth", 
+    "arrowdbldown", "arrowdblleft", "arrowdblright", "arrowdblup", 
+    "arrowdown", "arrowdownleft", "arrowdownright", "arrowdownwhite", 
+    "arrowheaddownmod", "arrowheadleftmod", "arrowheadrightmod", 
+    "arrowheadupmod", "arrowhorizex", "arrowleft", "arrowleftdbl", 
+    "arrowleftdblstroke", "arrowleftoverright", "arrowleftwhite", 
+    "arrowright", "arrowrightdblstroke", "arrowrightheavy", 
+    "arrowrightoverleft", "arrowrightwhite", "arrowtableft", 
+    "arrowtabright", "arrowup", "arrowupdn", "arrowupdnbse", 
+    "arrowupdownbase", "arrowupleft", "arrowupleftofdown", 
+    "arrowupright", "arrowupwhite", "arrowvertex", "asciicircum", 
+    "asciicircummonospace", "asciitilde", "asciitildemonospace", 
+    "ascript", "ascriptturned", "asmallhiragana", "asmallkatakana", 
+    "asmallkatakanahalfwidth", "asterisk", "asteriskaltonearabic", 
+    "asteriskarabic", "asteriskmath", "asteriskmonospace", 
+    "asterisksmall", "asterism", "asuperior", "asymptoticallyequal", 
+    "at", "atilde", "atmonospace", "atsmall", "aturned", "aubengali", 
+    "aubopomofo", "audeva", "augujarati", "augurmukhi", 
+    "aulengthmarkbengali", "aumatragurmukhi", "auvowelsignbengali", 
+    "auvowelsigndeva", "auvowelsigngujarati", "avagrahadeva", 
+    "aybarmenian", "ayin", "ayinaltonehebrew", "ayinhebrew", "b", 
+    "babengali", "backslash", "backslashmonospace", "badeva", 
+    "bagujarati", "bagurmukhi", "bahiragana", "bahtthai", "bakatakana", 
+    "bar", "barmonospace", "bbopomofo", "bcircle", "bdotaccent", 
+    "bdotbelow", "beamedsixteenthnotes", "because", "becyrillic", 
+    "beharabic", "behfinalarabic", "behinitialarabic", "behiragana", 
+    "behmedialarabic", "behmeeminitialarabic", "behmeemisolatedarabic", 
+    "behnoonfinalarabic", "bekatakana", "benarmenian", "bet", "beta", 
+    "betasymbolgreek", "betdagesh", "betdageshhebrew", "bethebrew", 
+    "betrafehebrew", "bhabengali", "bhadeva", "bhagujarati", 
+    "bhagurmukhi", "bhook", "bihiragana", "bikatakana", 
+    "bilabialclick", "bindigurmukhi", "birusquare", "blackcircle", 
+    "blackdiamond", "blackdownpointingtriangle", 
+    "blackleftpointingpointer", "blackleftpointingtriangle", 
+    "blacklenticularbracketleft", "blacklenticularbracketleftvertical", 
+    "blacklenticularbracketright", 
+    "blacklenticularbracketrightvertical", "blacklowerlefttriangle", 
+    "blacklowerrighttriangle", "blackrectangle", 
+    "blackrightpointingpointer", "blackrightpointingtriangle", 
+    "blacksmallsquare", "blacksmilingface", "blacksquare", "blackstar", 
+    "blackupperlefttriangle", "blackupperrighttriangle", 
+    "blackuppointingsmalltriangle", "blackuppointingtriangle", "blank", 
+    "blinebelow", "block", "bmonospace", "bobaimaithai", "bohiragana", 
+    "bokatakana", "bparen", "bqsquare", "braceex", "braceleft", 
+    "braceleftbt", "braceleftmid", "braceleftmonospace", 
+    "braceleftsmall", "bracelefttp", "braceleftvertical", "braceright", 
+    "bracerightbt", "bracerightmid", "bracerightmonospace", 
+    "bracerightsmall", "bracerighttp", "bracerightvertical", 
+    "bracketleft", "bracketleftbt", "bracketleftex", 
+    "bracketleftmonospace", "bracketlefttp", "bracketright", 
+    "bracketrightbt", "bracketrightex", "bracketrightmonospace", 
+    "bracketrighttp", "breve", "brevebelowcmb", "brevecmb", 
+    "breveinvertedbelowcmb", "breveinvertedcmb", 
+    "breveinverteddoublecmb", "bridgebelowcmb", 
+    "bridgeinvertedbelowcmb", "brokenbar", "bstroke", "bsuperior", 
+    "btopbar", "buhiragana", "bukatakana", "bullet", "bulletinverse", 
+    "bulletoperator", "bullseye", "c", "caarmenian", "cabengali", 
+    "cacute", "cadeva", "cagujarati", "cagurmukhi", "calsquare", 
+    "candrabindubengali", "candrabinducmb", "candrabindudeva", 
+    "candrabindugujarati", "capslock", "careof", "caron", 
+    "caronbelowcmb", "caroncmb", "carriagereturn", "cbopomofo", 
+    "ccaron", "ccedilla", "ccedillaacute", "ccircle", "ccircumflex", 
+    "ccurl", "cdot", "cdotaccent", "cdsquare", "cedilla", "cedillacmb", 
+    "cent", "centigrade", "centinferior", "centmonospace", 
+    "centoldstyle", "centsuperior", "chaarmenian", "chabengali", 
+    "chadeva", "chagujarati", "chagurmukhi", "chbopomofo", 
+    "cheabkhasiancyrillic", "checkmark", "checyrillic", 
+    "chedescenderabkhasiancyrillic", "chedescendercyrillic", 
+    "chedieresiscyrillic", "cheharmenian", "chekhakassiancyrillic", 
+    "cheverticalstrokecyrillic", "chi", "chieuchacirclekorean", 
+    "chieuchaparenkorean", "chieuchcirclekorean", "chieuchkorean", 
+    "chieuchparenkorean", "chochangthai", "chochanthai", 
+    "chochingthai", "chochoethai", "chook", "cieucacirclekorean", 
+    "cieucaparenkorean", "cieuccirclekorean", "cieuckorean", 
+    "cieucparenkorean", "cieucuparenkorean", "circle", 
+    "circlemultiply", "circleot", "circleplus", "circlepostalmark", 
+    "circlewithlefthalfblack", "circlewithrighthalfblack", 
+    "circumflex", "circumflexbelowcmb", "circumflexcmb", "clear", 
+    "clickalveolar", "clickdental", "clicklateral", "clickretroflex", 
+    "club", "clubsuitblack", "clubsuitwhite", "cmcubedsquare", 
+    "cmonospace", "cmsquaredsquare", "coarmenian", "colon", 
+    "colonmonetary", "colonmonospace", "colonsign", "colonsmall", 
+    "colontriangularhalfmod", "colontriangularmod", "comma", 
+    "commaabovecmb", "commaaboverightcmb", "commaaccent", 
+    "commaarabic", "commaarmenian", "commainferior", "commamonospace", 
+    "commareversedabovecmb", "commareversedmod", "commasmall", 
+    "commasuperior", "commaturnedabovecmb", "commaturnedmod", 
+    "compass", "congruent", "contourintegral", "control", "controlACK", 
+    "controlBEL", "controlBS", "controlCAN", "controlCR", "controlDC1", 
+    "controlDC2", "controlDC3", "controlDC4", "controlDEL", 
+    "controlDLE", "controlEM", "controlENQ", "controlEOT", 
+    "controlESC", "controlETB", "controlETX", "controlFF", "controlFS", 
+    "controlGS", "controlHT", "controlLF", "controlNAK", "controlRS", 
+    "controlSI", "controlSO", "controlSOT", "controlSTX", "controlSUB", 
+    "controlSYN", "controlUS", "controlVT", "copyright", 
+    "copyrightsans", "copyrightserif", "cornerbracketleft", 
+    "cornerbracketlefthalfwidth", "cornerbracketleftvertical", 
+    "cornerbracketright", "cornerbracketrighthalfwidth", 
+    "cornerbracketrightvertical", "corporationsquare", "cosquare", 
+    "coverkgsquare", "cparen", "cruzeiro", "cstretched", "curlyand", 
+    "curlyor", "currency", "cyrBreve", "cyrFlex", "cyrbreve", 
+    "cyrflex", "d", "daarmenian", "dabengali", "dadarabic", "dadeva", 
+    "dadfinalarabic", "dadinitialarabic", "dadmedialarabic", "dagesh", 
+    "dageshhebrew", "dagger", "daggerdbl", "dagujarati", "dagurmukhi", 
+    "dahiragana", "dakatakana", "dalarabic", "dalet", "daletdagesh", 
+    "daletdageshhebrew", "dalethebrew", "dalfinalarabic", 
+    "dammaarabic", "dammalowarabic", "dammatanaltonearabic", 
+    "dammatanarabic", "danda", "dargahebrew", "dargalefthebrew", 
+    "dasiapneumatacyrilliccmb", "dblGrave", "dblanglebracketleft", 
+    "dblanglebracketleftvertical", "dblanglebracketright", 
+    "dblanglebracketrightvertical", "dblarchinvertedbelowcmb", 
+    "dblarrowleft", "dblarrowright", "dbldanda", "dblgrave", 
+    "dblgravecmb", "dblintegral", "dbllowline", "dbllowlinecmb", 
+    "dbloverlinecmb", "dblprimemod", "dblverticalbar", 
+    "dblverticallineabovecmb", "dbopomofo", "dbsquare", "dcaron", 
+    "dcedilla", "dcircle", "dcircumflexbelow", "dcroat", "ddabengali", 
+    "ddadeva", "ddagujarati", "ddagurmukhi", "ddalarabic", 
+    "ddalfinalarabic", "dddhadeva", "ddhabengali", "ddhadeva", 
+    "ddhagujarati", "ddhagurmukhi", "ddotaccent", "ddotbelow", 
+    "decimalseparatorarabic", "decimalseparatorpersian", "decyrillic", 
+    "degree", "dehihebrew", "dehiragana", "deicoptic", "dekatakana", 
+    "deleteleft", "deleteright", "delta", "deltaturned", 
+    "denominatorminusonenumeratorbengali", "dezh", "dhabengali", 
+    "dhadeva", "dhagujarati", "dhagurmukhi", "dhook", "dialytikatonos", 
+    "dialytikatonoscmb", "diamond", "diamondsuitwhite", "dieresis", 
+    "dieresisacute", "dieresisbelowcmb", "dieresiscmb", 
+    "dieresisgrave", "dieresistonos", "dihiragana", "dikatakana", 
+    "dittomark", "divide", "divides", "divisionslash", "djecyrillic", 
+    "dkshade", "dlinebelow", "dlsquare", "dmacron", "dmonospace", 
+    "dnblock", "dochadathai", "dodekthai", "dohiragana", "dokatakana", 
+    "dollar", "dollarinferior", "dollarmonospace", "dollaroldstyle", 
+    "dollarsmall", "dollarsuperior", "dong", "dorusquare", "dotaccent", 
+    "dotaccentcmb", "dotbelowcmb", "dotbelowcomb", "dotkatakana", 
+    "dotlessi", "dotlessj", "dotlessjstrokehook", "dotmath", 
+    "dottedcircle", "doubleyodpatah", "doubleyodpatahhebrew", 
+    "downtackbelowcmb", "downtackmod", "dparen", "dsuperior", "dtail", 
+    "dtopbar", "duhiragana", "dukatakana", "dz", "dzaltone", "dzcaron", 
+    "dzcurl", "dzeabkhasiancyrillic", "dzecyrillic", "dzhecyrillic", 
+    "e", "eacute", "earth", "ebengali", "ebopomofo", "ebreve", 
+    "ecandradeva", "ecandragujarati", "ecandravowelsigndeva", 
+    "ecandravowelsigngujarati", "ecaron", "ecedillabreve", 
+    "echarmenian", "echyiwnarmenian", "ecircle", "ecircumflex", 
+    "ecircumflexacute", "ecircumflexbelow", "ecircumflexdotbelow", 
+    "ecircumflexgrave", "ecircumflexhookabove", "ecircumflextilde", 
+    "ecyrillic", "edblgrave", "edeva", "edieresis", "edot", 
+    "edotaccent", "edotbelow", "eegurmukhi", "eematragurmukhi", 
+    "efcyrillic", "egrave", "egujarati", "eharmenian", "ehbopomofo", 
+    "ehiragana", "ehookabove", "eibopomofo", "eight", "eightarabic", 
+    "eightbengali", "eightcircle", "eightcircleinversesansserif", 
+    "eightdeva", "eighteencircle", "eighteenparen", "eighteenperiod", 
+    "eightgujarati", "eightgurmukhi", "eighthackarabic", 
+    "eighthangzhou", "eighthnotebeamed", "eightideographicparen", 
+    "eightinferior", "eightmonospace", "eightoldstyle", "eightparen", 
+    "eightperiod", "eightpersian", "eightroman", "eightsuperior", 
+    "eightthai", "einvertedbreve", "eiotifiedcyrillic", "ekatakana", 
+    "ekatakanahalfwidth", "ekonkargurmukhi", "ekorean", "elcyrillic", 
+    "element", "elevencircle", "elevenparen", "elevenperiod", 
+    "elevenroman", "ellipsis", "ellipsisvertical", "emacron", 
+    "emacronacute", "emacrongrave", "emcyrillic", "emdash", 
+    "emdashvertical", "emonospace", "emphasismarkarmenian", "emptyset", 
+    "enbopomofo", "encyrillic", "endash", "endashvertical", 
+    "endescendercyrillic", "eng", "engbopomofo", "enghecyrillic", 
+    "enhookcyrillic", "enspace", "eogonek", "eokorean", "eopen", 
+    "eopenclosed", "eopenreversed", "eopenreversedclosed", 
+    "eopenreversedhook", "eparen", "epsilon", "epsilontonos", "equal", 
+    "equalmonospace", "equalsmall", "equalsuperior", "equivalence", 
+    "erbopomofo", "ercyrillic", "ereversed", "ereversedcyrillic", 
+    "escyrillic", "esdescendercyrillic", "esh", "eshcurl", 
+    "eshortdeva", "eshortvowelsigndeva", "eshreversedloop", 
+    "eshsquatreversed", "esmallhiragana", "esmallkatakana", 
+    "esmallkatakanahalfwidth", "estimated", "esuperior", "eta", 
+    "etarmenian", "etatonos", "eth", "etilde", "etildebelow", 
+    "etnahtafoukhhebrew", "etnahtafoukhlefthebrew", "etnahtahebrew", 
+    "etnahtalefthebrew", "eturned", "eukorean", "euro", 
+    "evowelsignbengali", "evowelsigndeva", "evowelsigngujarati", 
+    "exclam", "exclamarmenian", "exclamdbl", "exclamdown", 
+    "exclamdownsmall", "exclammonospace", "exclamsmall", "existential", 
+    "ezh", "ezhcaron", "ezhcurl", "ezhreversed", "ezhtail", "f", 
+    "fadeva", "fagurmukhi", "fahrenheit", "fathaarabic", 
+    "fathalowarabic", "fathatanarabic", "fbopomofo", "fcircle", 
+    "fdotaccent", "feharabic", "feharmenian", "fehfinalarabic", 
+    "fehinitialarabic", "fehmedialarabic", "feicoptic", "female", "ff", 
+    "ffi", "ffl", "fi", "fifteencircle", "fifteenparen", 
+    "fifteenperiod", "figuredash", "filledbox", "filledrect", 
+    "finalkaf", "finalkafdagesh", "finalkafdageshhebrew", 
+    "finalkafhebrew", "finalmem", "finalmemhebrew", "finalnun", 
+    "finalnunhebrew", "finalpe", "finalpehebrew", "finaltsadi", 
+    "finaltsadihebrew", "firsttonechinese", "fisheye", "fitacyrillic", 
+    "five", "fivearabic", "fivebengali", "fivecircle", 
+    "fivecircleinversesansserif", "fivedeva", "fiveeighths", 
+    "fivegujarati", "fivegurmukhi", "fivehackarabic", "fivehangzhou", 
+    "fiveideographicparen", "fiveinferior", "fivemonospace", 
+    "fiveoldstyle", "fiveparen", "fiveperiod", "fivepersian", 
+    "fiveroman", "fivesuperior", "fivethai", "fl", "florin", 
+    "fmonospace", "fmsquare", "fofanthai", "fofathai", "fongmanthai", 
+    "forall", "four", "fourarabic", "fourbengali", "fourcircle", 
+    "fourcircleinversesansserif", "fourdeva", "fourgujarati", 
+    "fourgurmukhi", "fourhackarabic", "fourhangzhou", 
+    "fourideographicparen", "fourinferior", "fourmonospace", 
+    "fournumeratorbengali", "fouroldstyle", "fourparen", "fourperiod", 
+    "fourpersian", "fourroman", "foursuperior", "fourteencircle", 
+    "fourteenparen", "fourteenperiod", "fourthai", "fourthtonechinese", 
+    "fparen", "fraction", "franc", "g", "gabengali", "gacute", 
+    "gadeva", "gafarabic", "gaffinalarabic", "gafinitialarabic", 
+    "gafmedialarabic", "gagujarati", "gagurmukhi", "gahiragana", 
+    "gakatakana", "gamma", "gammalatinsmall", "gammasuperior", 
+    "gangiacoptic", "gbopomofo", "gbreve", "gcaron", "gcedilla", 
+    "gcircle", "gcircumflex", "gcommaaccent", "gdot", "gdotaccent", 
+    "gecyrillic", "gehiragana", "gekatakana", "geometricallyequal", 
+    "gereshaccenthebrew", "gereshhebrew", "gereshmuqdamhebrew", 
+    "germandbls", "gershayimaccenthebrew", "gershayimhebrew", 
+    "getamark", "ghabengali", "ghadarmenian", "ghadeva", "ghagujarati", 
+    "ghagurmukhi", "ghainarabic", "ghainfinalarabic", 
+    "ghaininitialarabic", "ghainmedialarabic", "ghemiddlehookcyrillic", 
+    "ghestrokecyrillic", "gheupturncyrillic", "ghhadeva", 
+    "ghhagurmukhi", "ghook", "ghzsquare", "gihiragana", "gikatakana", 
+    "gimarmenian", "gimel", "gimeldagesh", "gimeldageshhebrew", 
+    "gimelhebrew", "gjecyrillic", "glottalinvertedstroke", 
+    "glottalstop", "glottalstopinverted", "glottalstopmod", 
+    "glottalstopreversed", "glottalstopreversedmod", 
+    "glottalstopreversedsuperior", "glottalstopstroke", 
+    "glottalstopstrokereversed", "gmacron", "gmonospace", "gohiragana", 
+    "gokatakana", "gparen", "gpasquare", "gradient", "grave", 
+    "gravebelowcmb", "gravecmb", "gravecomb", "gravedeva", 
+    "gravelowmod", "gravemonospace", "gravetonecmb", "greater", 
+    "greaterequal", "greaterequalorless", "greatermonospace", 
+    "greaterorequivalent", "greaterorless", "greateroverequal", 
+    "greatersmall", "gscript", "gstroke", "guhiragana", 
+    "guillemotleft", "guillemotright", "guilsinglleft", 
+    "guilsinglright", "gukatakana", "guramusquare", "gysquare", "h", 
+    "haabkhasiancyrillic", "haaltonearabic", "habengali", 
+    "hadescendercyrillic", "hadeva", "hagujarati", "hagurmukhi", 
+    "haharabic", "hahfinalarabic", "hahinitialarabic", "hahiragana", 
+    "hahmedialarabic", "haitusquare", "hakatakana", 
+    "hakatakanahalfwidth", "halantgurmukhi", "hamzaarabic", 
+    "hamzalowarabic", "hangulfiller", "hardsigncyrillic", 
+    "harpoonleftbarbup", "harpoonrightbarbup", "hasquare", 
+    "hatafpatah", "hatafpatah16", "hatafpatah23", "hatafpatah2f", 
+    "hatafpatahhebrew", "hatafpatahnarrowhebrew", 
+    "hatafpatahquarterhebrew", "hatafpatahwidehebrew", "hatafqamats", 
+    "hatafqamats1b", "hatafqamats28", "hatafqamats34", 
+    "hatafqamatshebrew", "hatafqamatsnarrowhebrew", 
+    "hatafqamatsquarterhebrew", "hatafqamatswidehebrew", "hatafsegol", 
+    "hatafsegol17", "hatafsegol24", "hatafsegol30", "hatafsegolhebrew", 
+    "hatafsegolnarrowhebrew", "hatafsegolquarterhebrew", 
+    "hatafsegolwidehebrew", "hbar", "hbopomofo", "hbrevebelow", 
+    "hcedilla", "hcircle", "hcircumflex", "hdieresis", "hdotaccent", 
+    "hdotbelow", "he", "heart", "heartsuitblack", "heartsuitwhite", 
+    "hedagesh", "hedageshhebrew", "hehaltonearabic", "heharabic", 
+    "hehebrew", "hehfinalaltonearabic", "hehfinalalttwoarabic", 
+    "hehfinalarabic", "hehhamzaabovefinalarabic", 
+    "hehhamzaaboveisolatedarabic", "hehinitialaltonearabic", 
+    "hehinitialarabic", "hehiragana", "hehmedialaltonearabic", 
+    "hehmedialarabic", "heiseierasquare", "hekatakana", 
+    "hekatakanahalfwidth", "hekutaarusquare", "henghook", 
+    "herutusquare", "het", "hethebrew", "hhook", "hhooksuperior", 
+    "hieuhacirclekorean", "hieuhaparenkorean", "hieuhcirclekorean", 
+    "hieuhkorean", "hieuhparenkorean", "hihiragana", "hikatakana", 
+    "hikatakanahalfwidth", "hiriq", "hiriq14", "hiriq21", "hiriq2d", 
+    "hiriqhebrew", "hiriqnarrowhebrew", "hiriqquarterhebrew", 
+    "hiriqwidehebrew", "hlinebelow", "hmonospace", "hoarmenian", 
+    "hohipthai", "hohiragana", "hokatakana", "hokatakanahalfwidth", 
+    "holam", "holam19", "holam26", "holam32", "holamhebrew", 
+    "holamnarrowhebrew", "holamquarterhebrew", "holamwidehebrew", 
+    "honokhukthai", "hookabovecomb", "hookcmb", 
+    "hookpalatalizedbelowcmb", "hookretroflexbelowcmb", "hoonsquare", 
+    "horicoptic", "horizontalbar", "horncmb", "hotsprings", "house", 
+    "hparen", "hsuperior", "hturned", "huhiragana", "huiitosquare", 
+    "hukatakana", "hukatakanahalfwidth", "hungarumlaut", 
+    "hungarumlautcmb", "hv", "hyphen", "hypheninferior", 
+    "hyphenmonospace", "hyphensmall", "hyphensuperior", "hyphentwo", 
+    "i", "iacute", "iacyrillic", "ibengali", "ibopomofo", "ibreve", 
+    "icaron", "icircle", "icircumflex", "icyrillic", "idblgrave", 
+    "ideographearthcircle", "ideographfirecircle", 
+    "ideographicallianceparen", "ideographiccallparen", 
+    "ideographiccentrecircle", "ideographicclose", "ideographiccomma", 
+    "ideographiccommaleft", "ideographiccongratulationparen", 
+    "ideographiccorrectcircle", "ideographicearthparen", 
+    "ideographicenterpriseparen", "ideographicexcellentcircle", 
+    "ideographicfestivalparen", "ideographicfinancialcircle", 
+    "ideographicfinancialparen", "ideographicfireparen", 
+    "ideographichaveparen", "ideographichighcircle", 
+    "ideographiciterationmark", "ideographiclaborcircle", 
+    "ideographiclaborparen", "ideographicleftcircle", 
+    "ideographiclowcircle", "ideographicmedicinecircle", 
+    "ideographicmetalparen", "ideographicmoonparen", 
+    "ideographicnameparen", "ideographicperiod", 
+    "ideographicprintcircle", "ideographicreachparen", 
+    "ideographicrepresentparen", "ideographicresourceparen", 
+    "ideographicrightcircle", "ideographicsecretcircle", 
+    "ideographicselfparen", "ideographicsocietyparen", 
+    "ideographicspace", "ideographicspecialparen", 
+    "ideographicstockparen", "ideographicstudyparen", 
+    "ideographicsunparen", "ideographicsuperviseparen", 
+    "ideographicwaterparen", "ideographicwoodparen", "ideographiczero", 
+    "ideographmetalcircle", "ideographmooncircle", 
+    "ideographnamecircle", "ideographsuncircle", 
+    "ideographwatercircle", "ideographwoodcircle", "ideva", 
+    "idieresis", "idieresisacute", "idieresiscyrillic", "idotbelow", 
+    "iebrevecyrillic", "iecyrillic", "ieungacirclekorean", 
+    "ieungaparenkorean", "ieungcirclekorean", "ieungkorean", 
+    "ieungparenkorean", "igrave", "igujarati", "igurmukhi", 
+    "ihiragana", "ihookabove", "iibengali", "iicyrillic", "iideva", 
+    "iigujarati", "iigurmukhi", "iimatragurmukhi", "iinvertedbreve", 
+    "iishortcyrillic", "iivowelsignbengali", "iivowelsigndeva", 
+    "iivowelsigngujarati", "ij", "ikatakana", "ikatakanahalfwidth", 
+    "ikorean", "ilde", "iluyhebrew", "imacron", "imacroncyrillic", 
+    "imageorapproximatelyequal", "imatragurmukhi", "imonospace", 
+    "increment", "infinity", "iniarmenian", "integral", 
+    "integralbottom", "integralbt", "integralex", "integraltop", 
+    "integraltp", "intersection", "intisquare", "invbullet", 
+    "invcircle", "invsmileface", "iocyrillic", "iogonek", "iota", 
+    "iotadieresis", "iotadieresistonos", "iotalatin", "iotatonos", 
+    "iparen", "irigurmukhi", "ismallhiragana", "ismallkatakana", 
+    "ismallkatakanahalfwidth", "issharbengali", "istroke", "isuperior", 
+    "iterationhiragana", "iterationkatakana", "itilde", "itildebelow", 
+    "iubopomofo", "iucyrillic", "ivowelsignbengali", "ivowelsigndeva", 
+    "ivowelsigngujarati", "izhitsacyrillic", "izhitsadblgravecyrillic", 
+    "j", "jaarmenian", "jabengali", "jadeva", "jagujarati", 
+    "jagurmukhi", "jbopomofo", "jcaron", "jcircle", "jcircumflex", 
+    "jcrossedtail", "jdotlessstroke", "jecyrillic", "jeemarabic", 
+    "jeemfinalarabic", "jeeminitialarabic", "jeemmedialarabic", 
+    "jeharabic", "jehfinalarabic", "jhabengali", "jhadeva", 
+    "jhagujarati", "jhagurmukhi", "jheharmenian", "jis", "jmonospace", 
+    "jparen", "jsuperior", "k", "kabashkircyrillic", "kabengali", 
+    "kacute", "kacyrillic", "kadescendercyrillic", "kadeva", "kaf", 
+    "kafarabic", "kafdagesh", "kafdageshhebrew", "kaffinalarabic", 
+    "kafhebrew", "kafinitialarabic", "kafmedialarabic", 
+    "kafrafehebrew", "kagujarati", "kagurmukhi", "kahiragana", 
+    "kahookcyrillic", "kakatakana", "kakatakanahalfwidth", "kappa", 
+    "kappasymbolgreek", "kapyeounmieumkorean", "kapyeounphieuphkorean", 
+    "kapyeounpieupkorean", "kapyeounssangpieupkorean", "karoriisquare", 
+    "kashidaautoarabic", "kashidaautonosidebearingarabic", 
+    "kasmallkatakana", "kasquare", "kasraarabic", "kasratanarabic", 
+    "kastrokecyrillic", "katahiraprolongmarkhalfwidth", 
+    "kaverticalstrokecyrillic", "kbopomofo", "kcalsquare", "kcaron", 
+    "kcedilla", "kcircle", "kcommaaccent", "kdotbelow", "keharmenian", 
+    "kehiragana", "kekatakana", "kekatakanahalfwidth", "kenarmenian", 
+    "kesmallkatakana", "kgreenlandic", "khabengali", "khacyrillic", 
+    "khadeva", "khagujarati", "khagurmukhi", "khaharabic", 
+    "khahfinalarabic", "khahinitialarabic", "khahmedialarabic", 
+    "kheicoptic", "khhadeva", "khhagurmukhi", "khieukhacirclekorean", 
+    "khieukhaparenkorean", "khieukhcirclekorean", "khieukhkorean", 
+    "khieukhparenkorean", "khokhaithai", "khokhonthai", "khokhuatthai", 
+    "khokhwaithai", "khomutthai", "khook", "khorakhangthai", 
+    "khzsquare", "kihiragana", "kikatakana", "kikatakanahalfwidth", 
+    "kiroguramusquare", "kiromeetorusquare", "kirosquare", 
+    "kiyeokacirclekorean", "kiyeokaparenkorean", "kiyeokcirclekorean", 
+    "kiyeokkorean", "kiyeokparenkorean", "kiyeoksioskorean", 
+    "kjecyrillic", "klinebelow", "klsquare", "kmcubedsquare", 
+    "kmonospace", "kmsquaredsquare", "kohiragana", "kohmsquare", 
+    "kokaithai", "kokatakana", "kokatakanahalfwidth", "kooposquare", 
+    "koppacyrillic", "koreanstandardsymbol", "koroniscmb", "kparen", 
+    "kpasquare", "ksicyrillic", "ktsquare", "kturned", "kuhiragana", 
+    "kukatakana", "kukatakanahalfwidth", "kvsquare", "kwsquare", "l", 
+    "labengali", "lacute", "ladeva", "lagujarati", "lagurmukhi", 
+    "lakkhangyaothai", "lamaleffinalarabic", 
+    "lamalefhamzaabovefinalarabic", "lamalefhamzaaboveisolatedarabic", 
+    "lamalefhamzabelowfinalarabic", "lamalefhamzabelowisolatedarabic", 
+    "lamalefisolatedarabic", "lamalefmaddaabovefinalarabic", 
+    "lamalefmaddaaboveisolatedarabic", "lamarabic", "lambda", 
+    "lambdastroke", "lamed", "lameddagesh", "lameddageshhebrew", 
+    "lamedhebrew", "lamfinalarabic", "lamhahinitialarabic", 
+    "laminitialarabic", "lamjeeminitialarabic", "lamkhahinitialarabic", 
+    "lamlamhehisolatedarabic", "lammedialarabic", 
+    "lammeemhahinitialarabic", "lammeeminitialarabic", "largecircle", 
+    "lbar", "lbelt", "lbopomofo", "lcaron", "lcedilla", "lcircle", 
+    "lcircumflexbelow", "lcommaaccent", "ldot", "ldotaccent", 
+    "ldotbelow", "ldotbelowmacron", "leftangleabovecmb", 
+    "lefttackbelowcmb", "less", "lessequal", "lessequalorgreater", 
+    "lessmonospace", "lessorequivalent", "lessorgreater", 
+    "lessoverequal", "lesssmall", "lezh", "lfblock", "lhookretroflex", 
+    "lira", "liwnarmenian", "lj", "ljecyrillic", "ll", "lladeva", 
+    "llagujarati", "llinebelow", "llladeva", "llvocalicbengali", 
+    "llvocalicdeva", "llvocalicvowelsignbengali", 
+    "llvocalicvowelsigndeva", "lmiddletilde", "lmonospace", "lmsquare", 
+    "lochulathai", "logicaland", "logicalnot", "logicalnotreversed", 
+    "logicalor", "lolingthai", "longs", "lowlinecenterline", 
+    "lowlinecmb", "lowlinedashed", "lozenge", "lparen", "lslash", 
+    "lsquare", "lsuperior", "ltshade", "luthai", "lvocalicbengali", 
+    "lvocalicdeva", "lvocalicvowelsignbengali", 
+    "lvocalicvowelsigndeva", "lxsquare", "m", "mabengali", "macron", 
+    "macronbelowcmb", "macroncmb", "macronlowmod", "macronmonospace", 
+    "macute", "madeva", "magujarati", "magurmukhi", "mahapakhhebrew", 
+    "mahapakhlefthebrew", "mahiragana", "maichattawalowleftthai", 
+    "maichattawalowrightthai", "maichattawathai", 
+    "maichattawaupperleftthai", "maieklowleftthai", 
+    "maieklowrightthai", "maiekthai", "maiekupperleftthai", 
+    "maihanakatleftthai", "maihanakatthai", "maitaikhuleftthai", 
+    "maitaikhuthai", "maitholowleftthai", "maitholowrightthai", 
+    "maithothai", "maithoupperleftthai", "maitrilowleftthai", 
+    "maitrilowrightthai", "maitrithai", "maitriupperleftthai", 
+    "maiyamokthai", "makatakana", "makatakanahalfwidth", "male", 
+    "mansyonsquare", "maqafhebrew", "mars", "masoracirclehebrew", 
+    "masquare", "mbopomofo", "mbsquare", "mcircle", "mcubedsquare", 
+    "mdotaccent", "mdotbelow", "meemarabic", "meemfinalarabic", 
+    "meeminitialarabic", "meemmedialarabic", "meemmeeminitialarabic", 
+    "meemmeemisolatedarabic", "meetorusquare", "mehiragana", 
+    "meizierasquare", "mekatakana", "mekatakanahalfwidth", "mem", 
+    "memdagesh", "memdageshhebrew", "memhebrew", "menarmenian", 
+    "merkhahebrew", "merkhakefulahebrew", "merkhakefulalefthebrew", 
+    "merkhalefthebrew", "mhook", "mhzsquare", 
+    "middledotkatakanahalfwidth", "middot", "mieumacirclekorean", 
+    "mieumaparenkorean", "mieumcirclekorean", "mieumkorean", 
+    "mieumpansioskorean", "mieumparenkorean", "mieumpieupkorean", 
+    "mieumsioskorean", "mihiragana", "mikatakana", 
+    "mikatakanahalfwidth", "minus", "minusbelowcmb", "minuscircle", 
+    "minusmod", "minusplus", "minute", "miribaarusquare", "mirisquare", 
+    "mlonglegturned", "mlsquare", "mmcubedsquare", "mmonospace", 
+    "mmsquaredsquare", "mohiragana", "mohmsquare", "mokatakana", 
+    "mokatakanahalfwidth", "molsquare", "momathai", "moverssquare", 
+    "moverssquaredsquare", "mparen", "mpasquare", "mssquare", 
+    "msuperior", "mturned", "mu", "mu1", "muasquare", "muchgreater",
+    "muchless", "mufsquare", "mugreek", "mugsquare", "muhiragana", 
+    "mukatakana", "mukatakanahalfwidth", "mulsquare", "multiply", 
+    "mumsquare", "munahhebrew", "munahlefthebrew", "musicalnote", 
+    "musicalnotedbl", "musicflatsign", "musicsharpsign", "mussquare", 
+    "muvsquare", "muwsquare", "mvmegasquare", "mvsquare", 
+    "mwmegasquare", "mwsquare", "n", "nabengali", "nabla", "nacute", 
+    "nadeva", "nagujarati", "nagurmukhi", "nahiragana", "nakatakana", 
+    "nakatakanahalfwidth", "napostrophe", "nasquare", "nbopomofo", 
+    "nbspace", "ncaron", "ncedilla", "ncircle", "ncircumflexbelow", 
+    "ncommaaccent", "ndotaccent", "ndotbelow", "nehiragana", 
+    "nekatakana", "nekatakanahalfwidth", "newsheqelsign", "nfsquare", 
+    "ngabengali", "ngadeva", "ngagujarati", "ngagurmukhi", 
+    "ngonguthai", "nhiragana", "nhookleft", "nhookretroflex", 
+    "nieunacirclekorean", "nieunaparenkorean", "nieuncieuckorean", 
+    "nieuncirclekorean", "nieunhieuhkorean", "nieunkorean", 
+    "nieunpansioskorean", "nieunparenkorean", "nieunsioskorean", 
+    "nieuntikeutkorean", "nihiragana", "nikatakana", 
+    "nikatakanahalfwidth", "nikhahitleftthai", "nikhahitthai", "nine", 
+    "ninearabic", "ninebengali", "ninecircle", 
+    "ninecircleinversesansserif", "ninedeva", "ninegujarati", 
+    "ninegurmukhi", "ninehackarabic", "ninehangzhou", 
+    "nineideographicparen", "nineinferior", "ninemonospace", 
+    "nineoldstyle", "nineparen", "nineperiod", "ninepersian", 
+    "nineroman", "ninesuperior", "nineteencircle", "nineteenparen", 
+    "nineteenperiod", "ninethai", "nj", "njecyrillic", "nkatakana", 
+    "nkatakanahalfwidth", "nlegrightlong", "nlinebelow", "nmonospace", 
+    "nmsquare", "nnabengali", "nnadeva", "nnagujarati", "nnagurmukhi", 
+    "nnnadeva", "nohiragana", "nokatakana", "nokatakanahalfwidth", 
+    "nonbreakingspace", "nonenthai", "nonuthai", "noonarabic", 
+    "noonfinalarabic", "noonghunnaarabic", "noonghunnafinalarabic", 
+    "nooninitialarabic", "noonjeeminitialarabic", 
+    "noonjeemisolatedarabic", "noonmedialarabic", 
+    "noonmeeminitialarabic", "noonmeemisolatedarabic", 
+    "noonnoonfinalarabic", "notcontains", "notelement", "notelementof", 
+    "notequal", "notgreater", "notgreaternorequal", 
+    "notgreaternorless", "notidentical", "notless", "notlessnorequal", 
+    "notparallel", "notprecedes", "notsubset", "notsucceeds", 
+    "notsuperset", "nowarmenian", "nparen", "nssquare", "nsuperior", 
+    "ntilde", "nu", "nuhiragana", "nukatakana", "nukatakanahalfwidth", 
+    "nuktabengali", "nuktadeva", "nuktagujarati", "nuktagurmukhi", 
+    "numbersign", "numbersignmonospace", "numbersignsmall", 
+    "numeralsigngreek", "numeralsignlowergreek", "numero", "nun", 
+    "nundagesh", "nundageshhebrew", "nunhebrew", "nvsquare", 
+    "nwsquare", "nyabengali", "nyadeva", "nyagujarati", "nyagurmukhi", 
+    "o", "oacute", "oangthai", "obarred", "obarredcyrillic", 
+    "obarreddieresiscyrillic", "obengali", "obopomofo", "obreve", 
+    "ocandradeva", "ocandragujarati", "ocandravowelsigndeva", 
+    "ocandravowelsigngujarati", "ocaron", "ocircle", "ocircumflex", 
+    "ocircumflexacute", "ocircumflexdotbelow", "ocircumflexgrave", 
+    "ocircumflexhookabove", "ocircumflextilde", "ocyrillic", 
+    "odblacute", "odblgrave", "odeva", "odieresis", 
+    "odieresiscyrillic", "odotbelow", "oe", "oekorean", "ogonek", 
+    "ogonekcmb", "ograve", "ogujarati", "oharmenian", "ohiragana", 
+    "ohookabove", "ohorn", "ohornacute", "ohorndotbelow", "ohorngrave", 
+    "ohornhookabove", "ohorntilde", "ohungarumlaut", "oi", 
+    "oinvertedbreve", "okatakana", "okatakanahalfwidth", "okorean", 
+    "olehebrew", "omacron", "omacronacute", "omacrongrave", "omdeva", 
+    "omega", "omega1", "omegacyrillic", "omegalatinclosed", 
+    "omegaroundcyrillic", "omegatitlocyrillic", "omegatonos", 
+    "omgujarati", "omicron", "omicrontonos", "omonospace", "one", 
+    "onearabic", "onebengali", "onecircle", 
+    "onecircleinversesansserif", "onedeva", "onedotenleader", 
+    "oneeighth", "onefitted", "onegujarati", "onegurmukhi", 
+    "onehackarabic", "onehalf", "onehangzhou", "oneideographicparen", 
+    "oneinferior", "onemonospace", "onenumeratorbengali", 
+    "oneoldstyle", "oneparen", "oneperiod", "onepersian", "onequarter", 
+    "oneroman", "onesuperior", "onethai", "onethird", "oogonek", 
+    "oogonekmacron", "oogurmukhi", "oomatragurmukhi", "oopen", 
+    "oparen", "openbullet", "option", "ordfeminine", "ordmasculine", 
+    "orthogonal", "oshortdeva", "oshortvowelsigndeva", "oslash", 
+    "oslashacute", "osmallhiragana", "osmallkatakana", 
+    "osmallkatakanahalfwidth", "ostrokeacute", "osuperior", 
+    "otcyrillic", "otilde", "otildeacute", "otildedieresis", 
+    "oubopomofo", "overline", "overlinecenterline", "overlinecmb", 
+    "overlinedashed", "overlinedblwavy", "overlinewavy", "overscore", 
+    "ovowelsignbengali", "ovowelsigndeva", "ovowelsigngujarati", "p", 
+    "paampssquare", "paasentosquare", "pabengali", "pacute", "padeva", 
+    "pagedown", "pageup", "pagujarati", "pagurmukhi", "pahiragana", 
+    "paiyannoithai", "pakatakana", "palatalizationcyrilliccmb", 
+    "palochkacyrillic", "pansioskorean", "paragraph", "parallel", 
+    "parenleft", "parenleftaltonearabic", "parenleftbt", "parenleftex", 
+    "parenleftinferior", "parenleftmonospace", "parenleftsmall", 
+    "parenleftsuperior", "parenlefttp", "parenleftvertical", 
+    "parenright", "parenrightaltonearabic", "parenrightbt", 
+    "parenrightex", "parenrightinferior", "parenrightmonospace", 
+    "parenrightsmall", "parenrightsuperior", "parenrighttp", 
+    "parenrightvertical", "partialdiff", "paseqhebrew", "pashtahebrew", 
+    "pasquare", "patah", "patah11", "patah1d", "patah2a", 
+    "patahhebrew", "patahnarrowhebrew", "patahquarterhebrew", 
+    "patahwidehebrew", "pazerhebrew", "pbopomofo", "pcircle", 
+    "pdotaccent", "pe", "pecyrillic", "pedagesh", "pedageshhebrew", 
+    "peezisquare", "pefinaldageshhebrew", "peharabic", "peharmenian", 
+    "pehebrew", "pehfinalarabic", "pehinitialarabic", "pehiragana", 
+    "pehmedialarabic", "pekatakana", "pemiddlehookcyrillic", 
+    "perafehebrew", "percent", "percentarabic", "percentmonospace", 
+    "percentsmall", "period", "periodarmenian", "periodcentered", 
+    "periodhalfwidth", "periodinferior", "periodmonospace", 
+    "periodsmall", "periodsuperior", "perispomenigreekcmb", 
+    "perpendicular", "perthousand", "peseta", "pfsquare", "phabengali", 
+    "phadeva", "phagujarati", "phagurmukhi", "phi", "phi1", 
+    "phieuphacirclekorean", "phieuphaparenkorean", 
+    "phieuphcirclekorean", "phieuphkorean", "phieuphparenkorean", 
+    "philatin", "phinthuthai", "phisymbolgreek", "phook", 
+    "phophanthai", "phophungthai", "phosamphaothai", "pi", 
+    "pieupacirclekorean", "pieupaparenkorean", "pieupcieuckorean", 
+    "pieupcirclekorean", "pieupkiyeokkorean", "pieupkorean", 
+    "pieupparenkorean", "pieupsioskiyeokkorean", "pieupsioskorean", 
+    "pieupsiostikeutkorean", "pieupthieuthkorean", "pieuptikeutkorean", 
+    "pihiragana", "pikatakana", "pisymbolgreek", "piwrarmenian", 
+    "plus", "plusbelowcmb", "pluscircle", "plusminus", "plusmod", 
+    "plusmonospace", "plussmall", "plussuperior", "pmonospace", 
+    "pmsquare", "pohiragana", "pointingindexdownwhite", 
+    "pointingindexleftwhite", "pointingindexrightwhite", 
+    "pointingindexupwhite", "pokatakana", "poplathai", "postalmark", 
+    "postalmarkface", "pparen", "precedes", "prescription", "primemod", 
+    "primereversed", "product", "projective", "prolongedkana", 
+    "propellor", "propersubset", "propersuperset", "proportion", 
+    "proportional", "psi", "psicyrillic", "psilipneumatacyrilliccmb", 
+    "pssquare", "puhiragana", "pukatakana", "pvsquare", "pwsquare", 
+    "q", "qadeva", "qadmahebrew", "qafarabic", "qaffinalarabic", 
+    "qafinitialarabic", "qafmedialarabic", "qamats", "qamats10", 
+    "qamats1a", "qamats1c", "qamats27", "qamats29", "qamats33", 
+    "qamatsde", "qamatshebrew", "qamatsnarrowhebrew", 
+    "qamatsqatanhebrew", "qamatsqatannarrowhebrew", 
+    "qamatsqatanquarterhebrew", "qamatsqatanwidehebrew", 
+    "qamatsquarterhebrew", "qamatswidehebrew", "qarneyparahebrew", 
+    "qbopomofo", "qcircle", "qhook", "qmonospace", "qof", "qofdagesh", 
+    "qofdageshhebrew", "qofhebrew", "qparen", "quarternote", "qubuts", 
+    "qubuts18", "qubuts25", "qubuts31", "qubutshebrew", 
+    "qubutsnarrowhebrew", "qubutsquarterhebrew", "qubutswidehebrew", 
+    "question", "questionarabic", "questionarmenian", "questiondown", 
+    "questiondownsmall", "questiongreek", "questionmonospace", 
+    "questionsmall", "quotedbl", "quotedblbase", "quotedblleft", 
+    "quotedblmonospace", "quotedblprime", "quotedblprimereversed", 
+    "quotedblright", "quoteleft", "quoteleftreversed", "quotereversed", 
+    "quoteright", "quoterightn", "quotesinglbase", "quotesingle", 
+    "quotesinglemonospace", "r", "raarmenian", "rabengali", "racute", 
+    "radeva", "radical", "radicalex", "radoverssquare", 
+    "radoverssquaredsquare", "radsquare", "rafe", "rafehebrew", 
+    "ragujarati", "ragurmukhi", "rahiragana", "rakatakana", 
+    "rakatakanahalfwidth", "ralowerdiagonalbengali", 
+    "ramiddlediagonalbengali", "ramshorn", "ratio", "rbopomofo", 
+    "rcaron", "rcedilla", "rcircle", "rcommaaccent", "rdblgrave", 
+    "rdotaccent", "rdotbelow", "rdotbelowmacron", "referencemark", 
+    "reflexsubset", "reflexsuperset", "registered", "registersans", 
+    "registerserif", "reharabic", "reharmenian", "rehfinalarabic", 
+    "rehiragana", "rekatakana", "rekatakanahalfwidth", "resh", 
+    "reshdageshhebrew", "reshhebrew", "reversedtilde", "reviahebrew", 
+    "reviamugrashhebrew", "revlogicalnot", "rfishhook", 
+    "rfishhookreversed", "rhabengali", "rhadeva", "rho", "rhook", 
+    "rhookturned", "rhookturnedsuperior", "rhosymbolgreek", 
+    "rhotichookmod", "rieulacirclekorean", "rieulaparenkorean", 
+    "rieulcirclekorean", "rieulhieuhkorean", "rieulkiyeokkorean", 
+    "rieulkiyeoksioskorean", "rieulkorean", "rieulmieumkorean", 
+    "rieulpansioskorean", "rieulparenkorean", "rieulphieuphkorean", 
+    "rieulpieupkorean", "rieulpieupsioskorean", "rieulsioskorean", 
+    "rieulthieuthkorean", "rieultikeutkorean", 
+    "rieulyeorinhieuhkorean", "rightangle", "righttackbelowcmb", 
+    "righttriangle", "rihiragana", "rikatakana", "rikatakanahalfwidth", 
+    "ring", "ringbelowcmb", "ringcmb", "ringhalfleft", 
+    "ringhalfleftarmenian", "ringhalfleftbelowcmb", 
+    "ringhalfleftcentered", "ringhalfright", "ringhalfrightbelowcmb", 
+    "ringhalfrightcentered", "rinvertedbreve", "rittorusquare", 
+    "rlinebelow", "rlongleg", "rlonglegturned", "rmonospace", 
+    "rohiragana", "rokatakana", "rokatakanahalfwidth", "roruathai", 
+    "rparen", "rrabengali", "rradeva", "rragurmukhi", "rreharabic", 
+    "rrehfinalarabic", "rrvocalicbengali", "rrvocalicdeva", 
+    "rrvocalicgujarati", "rrvocalicvowelsignbengali", 
+    "rrvocalicvowelsigndeva", "rrvocalicvowelsigngujarati", 
+    "rsuperior", "rtblock", "rturned", "rturnedsuperior", "ruhiragana", 
+    "rukatakana", "rukatakanahalfwidth", "rupeemarkbengali", 
+    "rupeesignbengali", "rupiah", "ruthai", "rvocalicbengali", 
+    "rvocalicdeva", "rvocalicgujarati", "rvocalicvowelsignbengali", 
+    "rvocalicvowelsigndeva", "rvocalicvowelsigngujarati", "s", 
+    "sabengali", "sacute", "sacutedotaccent", "sadarabic", "sadeva", 
+    "sadfinalarabic", "sadinitialarabic", "sadmedialarabic", 
+    "sagujarati", "sagurmukhi", "sahiragana", "sakatakana", 
+    "sakatakanahalfwidth", "sallallahoualayhewasallamarabic", "samekh", 
+    "samekhdagesh", "samekhdageshhebrew", "samekhhebrew", "saraaathai", 
+    "saraaethai", "saraaimaimalaithai", "saraaimaimuanthai", 
+    "saraamthai", "saraathai", "saraethai", "saraiileftthai", 
+    "saraiithai", "saraileftthai", "saraithai", "saraothai", 
+    "saraueeleftthai", "saraueethai", "saraueleftthai", "sarauethai", 
+    "sarauthai", "sarauuthai", "sbopomofo", "scaron", 
+    "scarondotaccent", "scedilla", "schwa", "schwacyrillic", 
+    "schwadieresiscyrillic", "schwahook", "scircle", "scircumflex", 
+    "scommaaccent", "sdotaccent", "sdotbelow", "sdotbelowdotaccent", 
+    "seagullbelowcmb", "second", "secondtonechinese", "section", 
+    "seenarabic", "seenfinalarabic", "seeninitialarabic", 
+    "seenmedialarabic", "segol", "segol13", "segol1f", "segol2c", 
+    "segolhebrew", "segolnarrowhebrew", "segolquarterhebrew", 
+    "segoltahebrew", "segolwidehebrew", "seharmenian", "sehiragana", 
+    "sekatakana", "sekatakanahalfwidth", "semicolon", 
+    "semicolonarabic", "semicolonmonospace", "semicolonsmall", 
+    "semivoicedmarkkana", "semivoicedmarkkanahalfwidth", "sentisquare", 
+    "sentosquare", "seven", "sevenarabic", "sevenbengali", 
+    "sevencircle", "sevencircleinversesansserif", "sevendeva", 
+    "seveneighths", "sevengujarati", "sevengurmukhi", 
+    "sevenhackarabic", "sevenhangzhou", "sevenideographicparen", 
+    "seveninferior", "sevenmonospace", "sevenoldstyle", "sevenparen", 
+    "sevenperiod", "sevenpersian", "sevenroman", "sevensuperior", 
+    "seventeencircle", "seventeenparen", "seventeenperiod", 
+    "seventhai", "sfthyphen", "shaarmenian", "shabengali", 
+    "shacyrillic", "shaddaarabic", "shaddadammaarabic", 
+    "shaddadammatanarabic", "shaddafathaarabic", "shaddakasraarabic", 
+    "shaddakasratanarabic", "shade", "shadedark", "shadelight", 
+    "shademedium", "shadeva", "shagujarati", "shagurmukhi", 
+    "shalshelethebrew", "shbopomofo", "shchacyrillic", "sheenarabic", 
+    "sheenfinalarabic", "sheeninitialarabic", "sheenmedialarabic", 
+    "sheicoptic", "sheqel", "sheqelhebrew", "sheva", "sheva115", 
+    "sheva15", "sheva22", "sheva2e", "shevahebrew", 
+    "shevanarrowhebrew", "shevaquarterhebrew", "shevawidehebrew", 
+    "shhacyrillic", "shimacoptic", "shin", "shindagesh", 
+    "shindageshhebrew", "shindageshshindot", "shindageshshindothebrew", 
+    "shindageshsindot", "shindageshsindothebrew", "shindothebrew", 
+    "shinhebrew", "shinshindot", "shinshindothebrew", "shinsindot", 
+    "shinsindothebrew", "shook", "sigma", "sigma1", "sigmafinal", 
+    "sigmalunatesymbolgreek", "sihiragana", "sikatakana", 
+    "sikatakanahalfwidth", "siluqhebrew", "siluqlefthebrew", "similar", 
+    "sindothebrew", "siosacirclekorean", "siosaparenkorean", 
+    "sioscieuckorean", "sioscirclekorean", "sioskiyeokkorean", 
+    "sioskorean", "siosnieunkorean", "siosparenkorean", 
+    "siospieupkorean", "siostikeutkorean", "six", "sixarabic", 
+    "sixbengali", "sixcircle", "sixcircleinversesansserif", "sixdeva", 
+    "sixgujarati", "sixgurmukhi", "sixhackarabic", "sixhangzhou", 
+    "sixideographicparen", "sixinferior", "sixmonospace", 
+    "sixoldstyle", "sixparen", "sixperiod", "sixpersian", "sixroman", 
+    "sixsuperior", "sixteencircle", 
+    "sixteencurrencydenominatorbengali", "sixteenparen", 
+    "sixteenperiod", "sixthai", "slash", "slashmonospace", "slong", 
+    "slongdotaccent", "smileface", "smonospace", "sofpasuqhebrew", 
+    "softhyphen", "softsigncyrillic", "sohiragana", "sokatakana", 
+    "sokatakanahalfwidth", "soliduslongoverlaycmb", 
+    "solidusshortoverlaycmb", "sorusithai", "sosalathai", "sosothai", 
+    "sosuathai", "space", "spacehackarabic", "spade", "spadesuitblack", 
+    "spadesuitwhite", "sparen", "squarebelowcmb", "squarecc", 
+    "squarecm", "squarediagonalcrosshatchfill", "squarehorizontalfill", 
+    "squarekg", "squarekm", "squarekmcapital", "squareln", "squarelog", 
+    "squaremg", "squaremil", "squaremm", "squaremsquared", 
+    "squareorthogonalcrosshatchfill", 
+    "squareupperlefttolowerrightfill", 
+    "squareupperrighttolowerleftfill", "squareverticalfill", 
+    "squarewhitewithsmallblack", "srsquare", "ssabengali", "ssadeva", 
+    "ssagujarati", "ssangcieuckorean", "ssanghieuhkorean", 
+    "ssangieungkorean", "ssangkiyeokkorean", "ssangnieunkorean", 
+    "ssangpieupkorean", "ssangsioskorean", "ssangtikeutkorean", 
+    "ssuperior", "sterling", "sterlingmonospace", 
+    "strokelongoverlaycmb", "strokeshortoverlaycmb", "subset", 
+    "subsetnotequal", "subsetorequal", "succeeds", "suchthat", 
+    "suhiragana", "sukatakana", "sukatakanahalfwidth", "sukunarabic", 
+    "summation", "sun", "superset", "supersetnotequal", 
+    "supersetorequal", "svsquare", "syouwaerasquare", "t", "tabengali", 
+    "tackdown", "tackleft", "tadeva", "tagujarati", "tagurmukhi", 
+    "taharabic", "tahfinalarabic", "tahinitialarabic", "tahiragana", 
+    "tahmedialarabic", "taisyouerasquare", "takatakana", 
+    "takatakanahalfwidth", "tatweelarabic", "tau", "tav", "tavdages", 
+    "tavdagesh", "tavdageshhebrew", "tavhebrew", "tbar", "tbopomofo", 
+    "tcaron", "tccurl", "tcedilla", "tcheharabic", "tchehfinalarabic", 
+    "tchehinitialarabic", "tchehmedialarabic", "tcircle", 
+    "tcircumflexbelow", "tcommaaccent", "tdieresis", "tdotaccent", 
+    "tdotbelow", "tecyrillic", "tedescendercyrillic", "teharabic", 
+    "tehfinalarabic", "tehhahinitialarabic", "tehhahisolatedarabic", 
+    "tehinitialarabic", "tehiragana", "tehjeeminitialarabic", 
+    "tehjeemisolatedarabic", "tehmarbutaarabic", 
+    "tehmarbutafinalarabic", "tehmedialarabic", "tehmeeminitialarabic", 
+    "tehmeemisolatedarabic", "tehnoonfinalarabic", "tekatakana", 
+    "tekatakanahalfwidth", "telephone", "telephoneblack", 
+    "telishagedolahebrew", "telishaqetanahebrew", "tencircle", 
+    "tenideographicparen", "tenparen", "tenperiod", "tenroman", "tesh", 
+    "tet", "tetdagesh", "tetdageshhebrew", "tethebrew", 
+    "tetsecyrillic", "tevirhebrew", "tevirlefthebrew", "thabengali", 
+    "thadeva", "thagujarati", "thagurmukhi", "thalarabic", 
+    "thalfinalarabic", "thanthakhatlowleftthai", 
+    "thanthakhatlowrightthai", "thanthakhatthai", 
+    "thanthakhatupperleftthai", "theharabic", "thehfinalarabic", 
+    "thehinitialarabic", "thehmedialarabic", "thereexists", 
+    "therefore", "theta", "theta1", "thetasymbolgreek", 
+    "thieuthacirclekorean", "thieuthaparenkorean", 
+    "thieuthcirclekorean", "thieuthkorean", "thieuthparenkorean", 
+    "thirteencircle", "thirteenparen", "thirteenperiod", 
+    "thonangmonthothai", "thook", "thophuthaothai", "thorn", 
+    "thothahanthai", "thothanthai", "thothongthai", "thothungthai", 
+    "thousandcyrillic", "thousandsseparatorarabic", 
+    "thousandsseparatorpersian", "three", "threearabic", 
+    "threebengali", "threecircle", "threecircleinversesansserif", 
+    "threedeva", "threeeighths", "threegujarati", "threegurmukhi", 
+    "threehackarabic", "threehangzhou", "threeideographicparen", 
+    "threeinferior", "threemonospace", "threenumeratorbengali", 
+    "threeoldstyle", "threeparen", "threeperiod", "threepersian", 
+    "threequarters", "threequartersemdash", "threeroman", 
+    "threesuperior", "threethai", "thzsquare", "tihiragana", 
+    "tikatakana", "tikatakanahalfwidth", "tikeutacirclekorean", 
+    "tikeutaparenkorean", "tikeutcirclekorean", "tikeutkorean", 
+    "tikeutparenkorean", "tilde", "tildebelowcmb", "tildecmb", 
+    "tildecomb", "tildedoublecmb", "tildeoperator", "tildeoverlaycmb", 
+    "tildeverticalcmb", "timescircle", "tipehahebrew", 
+    "tipehalefthebrew", "tippigurmukhi", "titlocyrilliccmb", 
+    "tiwnarmenian", "tlinebelow", "tmonospace", "toarmenian", 
+    "tohiragana", "tokatakana", "tokatakanahalfwidth", 
+    "tonebarextrahighmod", "tonebarextralowmod", "tonebarhighmod", 
+    "tonebarlowmod", "tonebarmidmod", "tonefive", "tonesix", "tonetwo", 
+    "tonos", "tonsquare", "topatakthai", "tortoiseshellbracketleft", 
+    "tortoiseshellbracketleftsmall", 
+    "tortoiseshellbracketleftvertical", "tortoiseshellbracketright", 
+    "tortoiseshellbracketrightsmall", 
+    "tortoiseshellbracketrightvertical", "totaothai", "tpalatalhook", 
+    "tparen", "trademark", "trademarksans", "trademarkserif", 
+    "tretroflexhook", "triagdn", "triaglf", "triagrt", "triagup", "ts", 
+    "tsadi", "tsadidagesh", "tsadidageshhebrew", "tsadihebrew", 
+    "tsecyrillic", "tsere", "tsere12", "tsere1e", "tsere2b", 
+    "tserehebrew", "tserenarrowhebrew", "tserequarterhebrew", 
+    "tserewidehebrew", "tshecyrillic", "tsuperior", "ttabengali", 
+    "ttadeva", "ttagujarati", "ttagurmukhi", "tteharabic", 
+    "ttehfinalarabic", "ttehinitialarabic", "ttehmedialarabic", 
+    "tthabengali", "tthadeva", "tthagujarati", "tthagurmukhi", 
+    "tturned", "tuhiragana", "tukatakana", "tukatakanahalfwidth", 
+    "tusmallhiragana", "tusmallkatakana", "tusmallkatakanahalfwidth", 
+    "twelvecircle", "twelveparen", "twelveperiod", "twelveroman", 
+    "twentycircle", "twentyhangzhou", "twentyparen", "twentyperiod", 
+    "two", "twoarabic", "twobengali", "twocircle", 
+    "twocircleinversesansserif", "twodeva", "twodotenleader", 
+    "twodotleader", "twodotleadervertical", "twogujarati", 
+    "twogurmukhi", "twohackarabic", "twohangzhou", 
+    "twoideographicparen", "twoinferior", "twomonospace", 
+    "twonumeratorbengali", "twooldstyle", "twoparen", "twoperiod", 
+    "twopersian", "tworoman", "twostroke", "twosuperior", "twothai", 
+    "twothirds", "u", "uacute", "ubar", "ubengali", "ubopomofo", 
+    "ubreve", "ucaron", "ucircle", "ucircumflex", "ucircumflexbelow", 
+    "ucyrillic", "udattadeva", "udblacute", "udblgrave", "udeva", 
+    "udieresis", "udieresisacute", "udieresisbelow", "udieresiscaron", 
+    "udieresiscyrillic", "udieresisgrave", "udieresismacron", 
+    "udotbelow", "ugrave", "ugujarati", "ugurmukhi", "uhiragana", 
+    "uhookabove", "uhorn", "uhornacute", "uhorndotbelow", "uhorngrave", 
+    "uhornhookabove", "uhorntilde", "uhungarumlaut", 
+    "uhungarumlautcyrillic", "uinvertedbreve", "ukatakana", 
+    "ukatakanahalfwidth", "ukcyrillic", "ukorean", "umacron", 
+    "umacroncyrillic", "umacrondieresis", "umatragurmukhi", 
+    "umonospace", "underscore", "underscoredbl", "underscoremonospace", 
+    "underscorevertical", "underscorewavy", "union", "universal", 
+    "uogonek", "uparen", "upblock", "upperdothebrew", "upsilon", 
+    "upsilondieresis", "upsilondieresistonos", "upsilonlatin", 
+    "upsilontonos", "uptackbelowcmb", "uptackmod", "uragurmukhi", 
+    "uring", "ushortcyrillic", "usmallhiragana", "usmallkatakana", 
+    "usmallkatakanahalfwidth", "ustraightcyrillic", 
+    "ustraightstrokecyrillic", "utilde", "utildeacute", "utildebelow", 
+    "uubengali", "uudeva", "uugujarati", "uugurmukhi", 
+    "uumatragurmukhi", "uuvowelsignbengali", "uuvowelsigndeva", 
+    "uuvowelsigngujarati", "uvowelsignbengali", "uvowelsigndeva", 
+    "uvowelsigngujarati", "v", "vadeva", "vagujarati", "vagurmukhi", 
+    "vakatakana", "vav", "vavdagesh", "vavdagesh65", "vavdageshhebrew", 
+    "vavhebrew", "vavholam", "vavholamhebrew", "vavvavhebrew", 
+    "vavyodhebrew", "vcircle", "vdotbelow", "vecyrillic", "veharabic", 
+    "vehfinalarabic", "vehinitialarabic", "vehmedialarabic", 
+    "vekatakana", "venus", "verticalbar", "verticallineabovecmb", 
+    "verticallinebelowcmb", "verticallinelowmod", "verticallinemod", 
+    "vewarmenian", "vhook", "vikatakana", "viramabengali", 
+    "viramadeva", "viramagujarati", "visargabengali", "visargadeva", 
+    "visargagujarati", "vmonospace", "voarmenian", 
+    "voicediterationhiragana", "voicediterationkatakana", 
+    "voicedmarkkana", "voicedmarkkanahalfwidth", "vokatakana", 
+    "vparen", "vtilde", "vturned", "vuhiragana", "vukatakana", "w", 
+    "wacute", "waekorean", "wahiragana", "wakatakana", 
+    "wakatakanahalfwidth", "wakorean", "wasmallhiragana", 
+    "wasmallkatakana", "wattosquare", "wavedash", 
+    "wavyunderscorevertical", "wawarabic", "wawfinalarabic", 
+    "wawhamzaabovearabic", "wawhamzaabovefinalarabic", "wbsquare", 
+    "wcircle", "wcircumflex", "wdieresis", "wdotaccent", "wdotbelow", 
+    "wehiragana", "weierstrass", "wekatakana", "wekorean", "weokorean", 
+    "wgrave", "whitebullet", "whitecircle", "whitecircleinverse", 
+    "whitecornerbracketleft", "whitecornerbracketleftvertical", 
+    "whitecornerbracketright", "whitecornerbracketrightvertical", 
+    "whitediamond", "whitediamondcontainingblacksmalldiamond", 
+    "whitedownpointingsmalltriangle", "whitedownpointingtriangle", 
+    "whiteleftpointingsmalltriangle", "whiteleftpointingtriangle", 
+    "whitelenticularbracketleft", "whitelenticularbracketright", 
+    "whiterightpointingsmalltriangle", "whiterightpointingtriangle", 
+    "whitesmallsquare", "whitesmilingface", "whitesquare", "whitestar", 
+    "whitetelephone", "whitetortoiseshellbracketleft", 
+    "whitetortoiseshellbracketright", "whiteuppointingsmalltriangle", 
+    "whiteuppointingtriangle", "wihiragana", "wikatakana", "wikorean", 
+    "wmonospace", "wohiragana", "wokatakana", "wokatakanahalfwidth", 
+    "won", "wonmonospace", "wowaenthai", "wparen", "wring", 
+    "wsuperior", "wturned", "wynn", "x", "xabovecmb", "xbopomofo", 
+    "xcircle", "xdieresis", "xdotaccent", "xeharmenian", "xi", 
+    "xmonospace", "xparen", "xsuperior", "y", "yaadosquare", 
+    "yabengali", "yacute", "yadeva", "yaekorean", "yagujarati", 
+    "yagurmukhi", "yahiragana", "yakatakana", "yakatakanahalfwidth", 
+    "yakorean", "yamakkanthai", "yasmallhiragana", "yasmallkatakana", 
+    "yasmallkatakanahalfwidth", "yatcyrillic", "ycircle", 
+    "ycircumflex", "ydieresis", "ydotaccent", "ydotbelow", "yeharabic", 
+    "yehbarreearabic", "yehbarreefinalarabic", "yehfinalarabic", 
+    "yehhamzaabovearabic", "yehhamzaabovefinalarabic", 
+    "yehhamzaaboveinitialarabic", "yehhamzaabovemedialarabic", 
+    "yehinitialarabic", "yehmedialarabic", "yehmeeminitialarabic", 
+    "yehmeemisolatedarabic", "yehnoonfinalarabic", 
+    "yehthreedotsbelowarabic", "yekorean", "yen", "yenmonospace", 
+    "yeokorean", "yeorinhieuhkorean", "yerahbenyomohebrew", 
+    "yerahbenyomolefthebrew", "yericyrillic", "yerudieresiscyrillic", 
+    "yesieungkorean", "yesieungpansioskorean", "yesieungsioskorean", 
+    "yetivhebrew", "ygrave", "yhook", "yhookabove", "yiarmenian", 
+    "yicyrillic", "yikorean", "yinyang", "yiwnarmenian", "ymonospace", 
+    "yod", "yoddagesh", "yoddageshhebrew", "yodhebrew", "yodyodhebrew", 
+    "yodyodpatahhebrew", "yohiragana", "yoikorean", "yokatakana", 
+    "yokatakanahalfwidth", "yokorean", "yosmallhiragana", 
+    "yosmallkatakana", "yosmallkatakanahalfwidth", "yotgreek", 
+    "yoyaekorean", "yoyakorean", "yoyakthai", "yoyingthai", "yparen", 
+    "ypogegrammeni", "ypogegrammenigreekcmb", "yr", "yring", 
+    "ysuperior", "ytilde", "yturned", "yuhiragana", "yuikorean", 
+    "yukatakana", "yukatakanahalfwidth", "yukorean", "yusbigcyrillic", 
+    "yusbigiotifiedcyrillic", "yuslittlecyrillic", 
+    "yuslittleiotifiedcyrillic", "yusmallhiragana", "yusmallkatakana", 
+    "yusmallkatakanahalfwidth", "yuyekorean", "yuyeokorean", 
+    "yyabengali", "yyadeva", "z", "zaarmenian", "zacute", "zadeva", 
+    "zagurmukhi", "zaharabic", "zahfinalarabic", "zahinitialarabic", 
+    "zahiragana", "zahmedialarabic", "zainarabic", "zainfinalarabic", 
+    "zakatakana", "zaqefgadolhebrew", "zaqefqatanhebrew", 
+    "zarqahebrew", "zayin", "zayindagesh", "zayindageshhebrew", 
+    "zayinhebrew", "zbopomofo", "zcaron", "zcircle", "zcircumflex", 
+    "zcurl", "zdot", "zdotaccent", "zdotbelow", "zecyrillic", 
+    "zedescendercyrillic", "zedieresiscyrillic", "zehiragana", 
+    "zekatakana", "zero", "zeroarabic", "zerobengali", "zerodeva", 
+    "zerogujarati", "zerogurmukhi", "zerohackarabic", "zeroinferior", 
+    "zeromonospace", "zerooldstyle", "zeropersian", "zerosuperior", 
+    "zerothai", "zerowidthjoiner", "zerowidthnonjoiner", 
+    "zerowidthspace", "zeta", "zhbopomofo", "zhearmenian", 
+    "zhebrevecyrillic", "zhecyrillic", "zhedescendercyrillic", 
+    "zhedieresiscyrillic", "zihiragana", "zikatakana", "zinorhebrew", 
+    "zlinebelow", "zmonospace", "zohiragana", "zokatakana", "zparen", 
+    "zretroflexhook", "zstroke", "zuhiragana", "zukatakana", 
+};
+
+/*
+
+grep '^[^#;][^;]*;[^ ][^ ][^ ][^ ]$' glyphlist.txt | sort -t\; +0 -1 | \
+    cut -f2 -d\; | perl -ne 'chomp; print "0x$_, "' | \
+    fold -s -w68 | sed 's/^/    /'; echo
+
+ */
+static const unsigned short ps_codes_alphabetic[] = {
+    0x0041, 0x00C6, 0x01FC, 0x01E2, 0xF7E6, 0x00C1, 0xF7E1, 0x0102, 
+    0x1EAE, 0x04D0, 0x1EB6, 0x1EB0, 0x1EB2, 0x1EB4, 0x01CD, 0x24B6, 
+    0x00C2, 0x1EA4, 0x1EAC, 0x1EA6, 0x1EA8, 0xF7E2, 0x1EAA, 0xF6C9, 
+    0xF7B4, 0x0410, 0x0200, 0x00C4, 0x04D2, 0x01DE, 0xF7E4, 0x1EA0, 
+    0x01E0, 0x00C0, 0xF7E0, 0x1EA2, 0x04D4, 0x0202, 0x0391, 0x0386, 
+    0x0100, 0xFF21, 0x0104, 0x00C5, 0x01FA, 0x1E00, 0xF7E5, 0xF761, 
+    0x00C3, 0xF7E3, 0x0531, 0x0042, 0x24B7, 0x1E02, 0x1E04, 0x0411, 
+    0x0532, 0x0392, 0x0181, 0x1E06, 0xFF22, 0xF6F4, 0xF762, 0x0182, 
+    0x0043, 0x053E, 0x0106, 0xF6CA, 0xF6F5, 0x010C, 0x00C7, 0x1E08, 
+    0xF7E7, 0x24B8, 0x0108, 0x010A, 0x010A, 0xF7B8, 0x0549, 0x04BC, 
+    0x0427, 0x04BE, 0x04B6, 0x04F4, 0x0543, 0x04CB, 0x04B8, 0x03A7, 
+    0x0187, 0xF6F6, 0xFF23, 0x0551, 0xF763, 0x0044, 0x01F1, 0x01C4, 
+    0x0534, 0x0189, 0x010E, 0x1E10, 0x24B9, 0x1E12, 0x0110, 0x1E0A, 
+    0x1E0C, 0x0414, 0x03EE, 0x2206, 0x0394, 0x018A, 0xF6CB, 0xF6CC, 
+    0xF6CD, 0xF7A8, 0x03DC, 0x0402, 0x1E0E, 0xFF24, 0xF6F7, 0x0110, 
+    0xF764, 0x018B, 0x01F2, 0x01C5, 0x04E0, 0x0405, 0x040F, 0x0045, 
+    0x00C9, 0xF7E9, 0x0114, 0x011A, 0x1E1C, 0x0535, 0x24BA, 0x00CA, 
+    0x1EBE, 0x1E18, 0x1EC6, 0x1EC0, 0x1EC2, 0xF7EA, 0x1EC4, 0x0404, 
+    0x0204, 0x00CB, 0xF7EB, 0x0116, 0x0116, 0x1EB8, 0x0424, 0x00C8, 
+    0xF7E8, 0x0537, 0x1EBA, 0x2167, 0x0206, 0x0464, 0x041B, 0x216A, 
+    0x0112, 0x1E16, 0x1E14, 0x041C, 0xFF25, 0x041D, 0x04A2, 0x014A, 
+    0x04A4, 0x04C7, 0x0118, 0x0190, 0x0395, 0x0388, 0x0420, 0x018E, 
+    0x042D, 0x0421, 0x04AA, 0x01A9, 0xF765, 0x0397, 0x0538, 0x0389, 
+    0x00D0, 0xF7F0, 0x1EBC, 0x1E1A, 0x20AC, 0x01B7, 0x01EE, 0x01B8, 
+    0x0046, 0x24BB, 0x1E1E, 0x0556, 0x03E4, 0x0191, 0x0472, 0x2164, 
+    0xFF26, 0x2163, 0xF766, 0x0047, 0x3387, 0x01F4, 0x0393, 0x0194, 
+    0x03EA, 0x011E, 0x01E6, 0x0122, 0x24BC, 0x011C, 0x0122, 0x0120, 
+    0x0120, 0x0413, 0x0542, 0x0494, 0x0492, 0x0490, 0x0193, 0x0533, 
+    0x0403, 0x1E20, 0xFF27, 0xF6CE, 0xF760, 0xF767, 0x029B, 0x01E4, 
+    0x0048, 0x25CF, 0x25AA, 0x25AB, 0x25A1, 0x33CB, 0x04A8, 0x04B2, 
+    0x042A, 0x0126, 0x1E2A, 0x1E28, 0x24BD, 0x0124, 0x1E26, 0x1E22, 
+    0x1E24, 0xFF28, 0x0540, 0x03E8, 0xF768, 0xF6CF, 0xF6F8, 0x3390, 
+    0x0049, 0x042F, 0x0132, 0x042E, 0x00CD, 0xF7ED, 0x012C, 0x01CF, 
+    0x24BE, 0x00CE, 0xF7EE, 0x0406, 0x0208, 0x00CF, 0x1E2E, 0x04E4, 
+    0xF7EF, 0x0130, 0x0130, 0x1ECA, 0x04D6, 0x0415, 0x2111, 0x00CC, 
+    0xF7EC, 0x1EC8, 0x0418, 0x020A, 0x0419, 0x012A, 0x04E2, 0xFF29, 
+    0x053B, 0x0401, 0x012E, 0x0399, 0x0196, 0x03AA, 0x038A, 0xF769, 
+    0x0197, 0x0128, 0x1E2C, 0x0474, 0x0476, 0x004A, 0x0541, 0x24BF, 
+    0x0134, 0x0408, 0x054B, 0xFF2A, 0xF76A, 0x004B, 0x3385, 0x33CD, 
+    0x04A0, 0x1E30, 0x041A, 0x049A, 0x04C3, 0x039A, 0x049E, 0x049C, 
+    0x01E8, 0x0136, 0x24C0, 0x0136, 0x1E32, 0x0554, 0x053F, 0x0425, 
+    0x03E6, 0x0198, 0x040C, 0x1E34, 0xFF2B, 0x0480, 0x03DE, 0x046E, 
+    0xF76B, 0x004C, 0x01C7, 0xF6BF, 0x0139, 0x039B, 0x013D, 0x013B, 
+    0x24C1, 0x1E3C, 0x013B, 0x013F, 0x013F, 0x1E36, 0x1E38, 0x053C, 
+    0x01C8, 0x0409, 0x1E3A, 0xFF2C, 0x0141, 0xF6F9, 0xF76C, 0x004D, 
+    0x3386, 0xF6D0, 0xF7AF, 0x1E3E, 0x24C2, 0x1E40, 0x1E42, 0x0544, 
+    0xFF2D, 0xF76D, 0x019C, 0x039C, 0x004E, 0x01CA, 0x0143, 0x0147, 
+    0x0145, 0x24C3, 0x1E4A, 0x0145, 0x1E44, 0x1E46, 0x019D, 0x2168, 
+    0x01CB, 0x040A, 0x1E48, 0xFF2E, 0x0546, 0xF76E, 0x00D1, 0xF7F1, 
+    0x039D, 0x004F, 0x0152, 0xF6FA, 0x00D3, 0xF7F3, 0x04E8, 0x04EA, 
+    0x014E, 0x01D1, 0x019F, 0x24C4, 0x00D4, 0x1ED0, 0x1ED8, 0x1ED2, 
+    0x1ED4, 0xF7F4, 0x1ED6, 0x041E, 0x0150, 0x020C, 0x00D6, 0x04E6, 
+    0xF7F6, 0x1ECC, 0xF6FB, 0x00D2, 0xF7F2, 0x0555, 0x2126, 0x1ECE, 
+    0x01A0, 0x1EDA, 0x1EE2, 0x1EDC, 0x1EDE, 0x1EE0, 0x0150, 0x01A2, 
+    0x020E, 0x014C, 0x1E52, 0x1E50, 0x2126, 0x0460, 0x03A9, 0x047A, 
+    0x047C, 0x038F, 0x039F, 0x038C, 0xFF2F, 0x2160, 0x01EA, 0x01EC, 
+    0x0186, 0x00D8, 0x01FE, 0xF7F8, 0xF76F, 0x01FE, 0x047E, 0x00D5, 
+    0x1E4C, 0x1E4E, 0xF7F5, 0x0050, 0x1E54, 0x24C5, 0x1E56, 0x041F, 
+    0x054A, 0x04A6, 0x03A6, 0x01A4, 0x03A0, 0x0553, 0xFF30, 0x03A8, 
+    0x0470, 0xF770, 0x0051, 0x24C6, 0xFF31, 0xF771, 0x0052, 0x054C, 
+    0x0154, 0x0158, 0x0156, 0x24C7, 0x0156, 0x0210, 0x1E58, 0x1E5A, 
+    0x1E5C, 0x0550, 0x211C, 0x03A1, 0xF6FC, 0x0212, 0x1E5E, 0xFF32, 
+    0xF772, 0x0281, 0x02B6, 0x0053, 0x250C, 0x2514, 0x2510, 0x2518, 
+    0x253C, 0x252C, 0x2534, 0x251C, 0x2524, 0x2500, 0x2502, 0x2561, 
+    0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 
+    0x255B, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 
+    0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 
+    0x2552, 0x2553, 0x256B, 0x256A, 0x015A, 0x1E64, 0x03E0, 0x0160, 
+    0x1E66, 0xF6FD, 0x015E, 0x018F, 0x04D8, 0x04DA, 0x24C8, 0x015C, 
+    0x0218, 0x1E60, 0x1E62, 0x1E68, 0x054D, 0x2166, 0x0547, 0x0428, 
+    0x0429, 0x03E2, 0x04BA, 0x03EC, 0x03A3, 0x2165, 0xFF33, 0x042C, 
+    0xF773, 0x03DA, 0x0054, 0x03A4, 0x0166, 0x0164, 0x0162, 0x24C9, 
+    0x1E70, 0x0162, 0x1E6A, 0x1E6C, 0x0422, 0x04AC, 0x2169, 0x04B4, 
+    0x0398, 0x01AC, 0x00DE, 0xF7FE, 0x2162, 0xF6FE, 0x054F, 0x1E6E, 
+    0xFF34, 0x0539, 0x01BC, 0x0184, 0x01A7, 0x01AE, 0x0426, 0x040B, 
+    0xF774, 0x216B, 0x2161, 0x0055, 0x00DA, 0xF7FA, 0x016C, 0x01D3, 
+    0x24CA, 0x00DB, 0x1E76, 0xF7FB, 0x0423, 0x0170, 0x0214, 0x00DC, 
+    0x01D7, 0x1E72, 0x01D9, 0x04F0, 0x01DB, 0x01D5, 0xF7FC, 0x1EE4, 
+    0x00D9, 0xF7F9, 0x1EE6, 0x01AF, 0x1EE8, 0x1EF0, 0x1EEA, 0x1EEC, 
+    0x1EEE, 0x0170, 0x04F2, 0x0216, 0x0478, 0x016A, 0x04EE, 0x1E7A, 
+    0xFF35, 0x0172, 0x03A5, 0x03D2, 0x03D3, 0x01B1, 0x03AB, 0x03D4, 
+    0x03D2, 0x038E, 0x016E, 0x040E, 0xF775, 0x04AE, 0x04B0, 0x0168, 
+    0x1E78, 0x1E74, 0x0056, 0x24CB, 0x1E7E, 0x0412, 0x054E, 0x01B2, 
+    0xFF36, 0x0548, 0xF776, 0x1E7C, 0x0057, 0x1E82, 0x24CC, 0x0174, 
+    0x1E84, 0x1E86, 0x1E88, 0x1E80, 0xFF37, 0xF777, 0x0058, 0x24CD, 
+    0x1E8C, 0x1E8A, 0x053D, 0x039E, 0xFF38, 0xF778, 0x0059, 0x00DD, 
+    0xF7FD, 0x0462, 0x24CE, 0x0176, 0x0178, 0xF7FF, 0x1E8E, 0x1EF4, 
+    0x042B, 0x04F8, 0x1EF2, 0x01B3, 0x1EF6, 0x0545, 0x0407, 0x0552, 
+    0xFF39, 0xF779, 0x1EF8, 0x046A, 0x046C, 0x0466, 0x0468, 0x005A, 
+    0x0536, 0x0179, 0x017D, 0xF6FF, 0x24CF, 0x1E90, 0x017B, 0x017B, 
+    0x1E92, 0x0417, 0x0498, 0x04DE, 0x0396, 0x053A, 0x04C1, 0x0416, 
+    0x0496, 0x04DC, 0x1E94, 0xFF3A, 0xF77A, 0x01B5, 0x0061, 0x0986, 
+    0x00E1, 0x0906, 0x0A86, 0x0A06, 0x0A3E, 0x3303, 0x09BE, 0x093E, 
+    0x0ABE, 0x055F, 0x0970, 0x0985, 0x311A, 0x0103, 0x1EAF, 0x04D1, 
+    0x1EB7, 0x1EB1, 0x1EB3, 0x1EB5, 0x01CE, 0x24D0, 0x00E2, 0x1EA5, 
+    0x1EAD, 0x1EA7, 0x1EA9, 0x1EAB, 0x00B4, 0x0317, 0x0301, 0x0301, 
+    0x0954, 0x02CF, 0x0341, 0x0430, 0x0201, 0x0A71, 0x0905, 0x00E4, 
+    0x04D3, 0x01DF, 0x1EA1, 0x01E1, 0x00E6, 0x01FD, 0x3150, 0x01E3, 
+    0x2015, 0x20A4, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 
+    0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 
+    0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 
+    0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 
+    0x042D, 0x042E, 0x042F, 0x0490, 0x0402, 0x0403, 0x0404, 0x0405, 
+    0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040E, 
+    0xF6C4, 0xF6C5, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 
+    0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 
+    0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 
+    0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 
+    0x044D, 0x044E, 0x044F, 0x0491, 0x0452, 0x0453, 0x0454, 0x0455, 
+    0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045E, 
+    0x040F, 0x0462, 0x0472, 0x0474, 0xF6C6, 0x045F, 0x0463, 0x0473, 
+    0x0475, 0xF6C7, 0xF6C8, 0x04D9, 0x200E, 0x200F, 0x200D, 0x066A, 
+    0x060C, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 
+    0x0667, 0x0668, 0x0669, 0x061B, 0x061F, 0x0621, 0x0622, 0x0623, 
+    0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 
+    0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 
+    0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 
+    0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0648, 0x0649, 
+    0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 
+    0x0652, 0x0647, 0x06A4, 0x067E, 0x0686, 0x0698, 0x06AF, 0x0679, 
+    0x0688, 0x0691, 0x06BA, 0x06D2, 0x06D5, 0x20AA, 0x05BE, 0x05C3, 
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 
+    0x05E8, 0x05E9, 0x05EA, 0xFB2A, 0xFB2B, 0xFB4B, 0xFB1F, 0x05F0, 
+    0x05F1, 0x05F2, 0xFB35, 0x05B4, 0x05B5, 0x05B6, 0x05BB, 0x05B8, 
+    0x05B7, 0x05B0, 0x05B2, 0x05B1, 0x05B3, 0x05C2, 0x05C1, 0x05B9, 
+    0x05BC, 0x05BD, 0x05BF, 0x05C0, 0x02BC, 0x2105, 0x2113, 0x2116, 
+    0x202C, 0x202D, 0x202E, 0x200C, 0x066D, 0x02BD, 0x00E0, 0x0A85, 
+    0x0A05, 0x3042, 0x1EA3, 0x0990, 0x311E, 0x0910, 0x04D5, 0x0A90, 
+    0x0A10, 0x0A48, 0x0639, 0xFECA, 0xFECB, 0xFECC, 0x0203, 0x09C8, 
+    0x0948, 0x0AC8, 0x30A2, 0xFF71, 0x314F, 0x05D0, 0x0627, 0xFB30, 
+    0xFE8E, 0x0623, 0xFE84, 0x0625, 0xFE88, 0x05D0, 0xFB4F, 0x0622, 
+    0xFE82, 0x0649, 0xFEF0, 0xFEF3, 0xFEF4, 0xFB2E, 0xFB2F, 0x2135, 
+    0x224C, 0x03B1, 0x03AC, 0x0101, 0xFF41, 0x0026, 0xFF06, 0xF726, 
+    0x33C2, 0x3122, 0x3124, 0x0E5A, 0x2220, 0x3008, 0xFE3F, 0x3009, 
+    0xFE40, 0x2329, 0x232A, 0x212B, 0x0387, 0x0952, 0x0982, 0x0902, 
+    0x0A82, 0x0105, 0x3300, 0x249C, 0x055A, 0x02BC, 0xF8FF, 0x2250, 
+    0x2248, 0x2252, 0x2245, 0x318E, 0x318D, 0x2312, 0x1E9A, 0x00E5, 
+    0x01FB, 0x1E01, 0x2194, 0x21E3, 0x21E0, 0x21E2, 0x21E1, 0x21D4, 
+    0x21D3, 0x21D0, 0x21D2, 0x21D1, 0x2193, 0x2199, 0x2198, 0x21E9, 
+    0x02C5, 0x02C2, 0x02C3, 0x02C4, 0xF8E7, 0x2190, 0x21D0, 0x21CD, 
+    0x21C6, 0x21E6, 0x2192, 0x21CF, 0x279E, 0x21C4, 0x21E8, 0x21E4, 
+    0x21E5, 0x2191, 0x2195, 0x21A8, 0x21A8, 0x2196, 0x21C5, 0x2197, 
+    0x21E7, 0xF8E6, 0x005E, 0xFF3E, 0x007E, 0xFF5E, 0x0251, 0x0252, 
+    0x3041, 0x30A1, 0xFF67, 0x002A, 0x066D, 0x066D, 0x2217, 0xFF0A, 
+    0xFE61, 0x2042, 0xF6E9, 0x2243, 0x0040, 0x00E3, 0xFF20, 0xFE6B, 
+    0x0250, 0x0994, 0x3120, 0x0914, 0x0A94, 0x0A14, 0x09D7, 0x0A4C, 
+    0x09CC, 0x094C, 0x0ACC, 0x093D, 0x0561, 0x05E2, 0xFB20, 0x05E2, 
+    0x0062, 0x09AC, 0x005C, 0xFF3C, 0x092C, 0x0AAC, 0x0A2C, 0x3070, 
+    0x0E3F, 0x30D0, 0x007C, 0xFF5C, 0x3105, 0x24D1, 0x1E03, 0x1E05, 
+    0x266C, 0x2235, 0x0431, 0x0628, 0xFE90, 0xFE91, 0x3079, 0xFE92, 
+    0xFC9F, 0xFC08, 0xFC6D, 0x30D9, 0x0562, 0x05D1, 0x03B2, 0x03D0, 
+    0xFB31, 0xFB31, 0x05D1, 0xFB4C, 0x09AD, 0x092D, 0x0AAD, 0x0A2D, 
+    0x0253, 0x3073, 0x30D3, 0x0298, 0x0A02, 0x3331, 0x25CF, 0x25C6, 
+    0x25BC, 0x25C4, 0x25C0, 0x3010, 0xFE3B, 0x3011, 0xFE3C, 0x25E3, 
+    0x25E2, 0x25AC, 0x25BA, 0x25B6, 0x25AA, 0x263B, 0x25A0, 0x2605, 
+    0x25E4, 0x25E5, 0x25B4, 0x25B2, 0x2423, 0x1E07, 0x2588, 0xFF42, 
+    0x0E1A, 0x307C, 0x30DC, 0x249D, 0x33C3, 0xF8F4, 0x007B, 0xF8F3, 
+    0xF8F2, 0xFF5B, 0xFE5B, 0xF8F1, 0xFE37, 0x007D, 0xF8FE, 0xF8FD, 
+    0xFF5D, 0xFE5C, 0xF8FC, 0xFE38, 0x005B, 0xF8F0, 0xF8EF, 0xFF3B, 
+    0xF8EE, 0x005D, 0xF8FB, 0xF8FA, 0xFF3D, 0xF8F9, 0x02D8, 0x032E, 
+    0x0306, 0x032F, 0x0311, 0x0361, 0x032A, 0x033A, 0x00A6, 0x0180, 
+    0xF6EA, 0x0183, 0x3076, 0x30D6, 0x2022, 0x25D8, 0x2219, 0x25CE, 
+    0x0063, 0x056E, 0x099A, 0x0107, 0x091A, 0x0A9A, 0x0A1A, 0x3388, 
+    0x0981, 0x0310, 0x0901, 0x0A81, 0x21EA, 0x2105, 0x02C7, 0x032C, 
+    0x030C, 0x21B5, 0x3118, 0x010D, 0x00E7, 0x1E09, 0x24D2, 0x0109, 
+    0x0255, 0x010B, 0x010B, 0x33C5, 0x00B8, 0x0327, 0x00A2, 0x2103, 
+    0xF6DF, 0xFFE0, 0xF7A2, 0xF6E0, 0x0579, 0x099B, 0x091B, 0x0A9B, 
+    0x0A1B, 0x3114, 0x04BD, 0x2713, 0x0447, 0x04BF, 0x04B7, 0x04F5, 
+    0x0573, 0x04CC, 0x04B9, 0x03C7, 0x3277, 0x3217, 0x3269, 0x314A, 
+    0x3209, 0x0E0A, 0x0E08, 0x0E09, 0x0E0C, 0x0188, 0x3276, 0x3216, 
+    0x3268, 0x3148, 0x3208, 0x321C, 0x25CB, 0x2297, 0x2299, 0x2295, 
+    0x3036, 0x25D0, 0x25D1, 0x02C6, 0x032D, 0x0302, 0x2327, 0x01C2, 
+    0x01C0, 0x01C1, 0x01C3, 0x2663, 0x2663, 0x2667, 0x33A4, 0xFF43, 
+    0x33A0, 0x0581, 0x003A, 0x20A1, 0xFF1A, 0x20A1, 0xFE55, 0x02D1, 
+    0x02D0, 0x002C, 0x0313, 0x0315, 0xF6C3, 0x060C, 0x055D, 0xF6E1, 
+    0xFF0C, 0x0314, 0x02BD, 0xFE50, 0xF6E2, 0x0312, 0x02BB, 0x263C, 
+    0x2245, 0x222E, 0x2303, 0x0006, 0x0007, 0x0008, 0x0018, 0x000D, 
+    0x0011, 0x0012, 0x0013, 0x0014, 0x007F, 0x0010, 0x0019, 0x0005, 
+    0x0004, 0x001B, 0x0017, 0x0003, 0x000C, 0x001C, 0x001D, 0x0009, 
+    0x000A, 0x0015, 0x001E, 0x000F, 0x000E, 0x0002, 0x0001, 0x001A, 
+    0x0016, 0x001F, 0x000B, 0x00A9, 0xF8E9, 0xF6D9, 0x300C, 0xFF62, 
+    0xFE41, 0x300D, 0xFF63, 0xFE42, 0x337F, 0x33C7, 0x33C6, 0x249E, 
+    0x20A2, 0x0297, 0x22CF, 0x22CE, 0x00A4, 0xF6D1, 0xF6D2, 0xF6D4, 
+    0xF6D5, 0x0064, 0x0564, 0x09A6, 0x0636, 0x0926, 0xFEBE, 0xFEBF, 
+    0xFEC0, 0x05BC, 0x05BC, 0x2020, 0x2021, 0x0AA6, 0x0A26, 0x3060, 
+    0x30C0, 0x062F, 0x05D3, 0xFB33, 0xFB33, 0x05D3, 0xFEAA, 0x064F, 
+    0x064F, 0x064C, 0x064C, 0x0964, 0x05A7, 0x05A7, 0x0485, 0xF6D3, 
+    0x300A, 0xFE3D, 0x300B, 0xFE3E, 0x032B, 0x21D4, 0x21D2, 0x0965, 
+    0xF6D6, 0x030F, 0x222C, 0x2017, 0x0333, 0x033F, 0x02BA, 0x2016, 
+    0x030E, 0x3109, 0x33C8, 0x010F, 0x1E11, 0x24D3, 0x1E13, 0x0111, 
+    0x09A1, 0x0921, 0x0AA1, 0x0A21, 0x0688, 0xFB89, 0x095C, 0x09A2, 
+    0x0922, 0x0AA2, 0x0A22, 0x1E0B, 0x1E0D, 0x066B, 0x066B, 0x0434, 
+    0x00B0, 0x05AD, 0x3067, 0x03EF, 0x30C7, 0x232B, 0x2326, 0x03B4, 
+    0x018D, 0x09F8, 0x02A4, 0x09A7, 0x0927, 0x0AA7, 0x0A27, 0x0257, 
+    0x0385, 0x0344, 0x2666, 0x2662, 0x00A8, 0xF6D7, 0x0324, 0x0308, 
+    0xF6D8, 0x0385, 0x3062, 0x30C2, 0x3003, 0x00F7, 0x2223, 0x2215, 
+    0x0452, 0x2593, 0x1E0F, 0x3397, 0x0111, 0xFF44, 0x2584, 0x0E0E, 
+    0x0E14, 0x3069, 0x30C9, 0x0024, 0xF6E3, 0xFF04, 0xF724, 0xFE69, 
+    0xF6E4, 0x20AB, 0x3326, 0x02D9, 0x0307, 0x0323, 0x0323, 0x30FB, 
+    0x0131, 0xF6BE, 0x0284, 0x22C5, 0x25CC, 0xFB1F, 0xFB1F, 0x031E, 
+    0x02D5, 0x249F, 0xF6EB, 0x0256, 0x018C, 0x3065, 0x30C5, 0x01F3, 
+    0x02A3, 0x01C6, 0x02A5, 0x04E1, 0x0455, 0x045F, 0x0065, 0x00E9, 
+    0x2641, 0x098F, 0x311C, 0x0115, 0x090D, 0x0A8D, 0x0945, 0x0AC5, 
+    0x011B, 0x1E1D, 0x0565, 0x0587, 0x24D4, 0x00EA, 0x1EBF, 0x1E19, 
+    0x1EC7, 0x1EC1, 0x1EC3, 0x1EC5, 0x0454, 0x0205, 0x090F, 0x00EB, 
+    0x0117, 0x0117, 0x1EB9, 0x0A0F, 0x0A47, 0x0444, 0x00E8, 0x0A8F, 
+    0x0567, 0x311D, 0x3048, 0x1EBB, 0x311F, 0x0038, 0x0668, 0x09EE, 
+    0x2467, 0x2791, 0x096E, 0x2471, 0x2485, 0x2499, 0x0AEE, 0x0A6E, 
+    0x0668, 0x3028, 0x266B, 0x3227, 0x2088, 0xFF18, 0xF738, 0x247B, 
+    0x248F, 0x06F8, 0x2177, 0x2078, 0x0E58, 0x0207, 0x0465, 0x30A8, 
+    0xFF74, 0x0A74, 0x3154, 0x043B, 0x2208, 0x246A, 0x247E, 0x2492, 
+    0x217A, 0x2026, 0x22EE, 0x0113, 0x1E17, 0x1E15, 0x043C, 0x2014, 
+    0xFE31, 0xFF45, 0x055B, 0x2205, 0x3123, 0x043D, 0x2013, 0xFE32, 
+    0x04A3, 0x014B, 0x3125, 0x04A5, 0x04C8, 0x2002, 0x0119, 0x3153, 
+    0x025B, 0x029A, 0x025C, 0x025E, 0x025D, 0x24A0, 0x03B5, 0x03AD, 
+    0x003D, 0xFF1D, 0xFE66, 0x207C, 0x2261, 0x3126, 0x0440, 0x0258, 
+    0x044D, 0x0441, 0x04AB, 0x0283, 0x0286, 0x090E, 0x0946, 0x01AA, 
+    0x0285, 0x3047, 0x30A7, 0xFF6A, 0x212E, 0xF6EC, 0x03B7, 0x0568, 
+    0x03AE, 0x00F0, 0x1EBD, 0x1E1B, 0x0591, 0x0591, 0x0591, 0x0591, 
+    0x01DD, 0x3161, 0x20AC, 0x09C7, 0x0947, 0x0AC7, 0x0021, 0x055C, 
+    0x203C, 0x00A1, 0xF7A1, 0xFF01, 0xF721, 0x2203, 0x0292, 0x01EF, 
+    0x0293, 0x01B9, 0x01BA, 0x0066, 0x095E, 0x0A5E, 0x2109, 0x064E, 
+    0x064E, 0x064B, 0x3108, 0x24D5, 0x1E1F, 0x0641, 0x0586, 0xFED2, 
+    0xFED3, 0xFED4, 0x03E5, 0x2640, 0xFB00, 0xFB03, 0xFB04, 0xFB01, 
+    0x246E, 0x2482, 0x2496, 0x2012, 0x25A0, 0x25AC, 0x05DA, 0xFB3A, 
+    0xFB3A, 0x05DA, 0x05DD, 0x05DD, 0x05DF, 0x05DF, 0x05E3, 0x05E3, 
+    0x05E5, 0x05E5, 0x02C9, 0x25C9, 0x0473, 0x0035, 0x0665, 0x09EB, 
+    0x2464, 0x278E, 0x096B, 0x215D, 0x0AEB, 0x0A6B, 0x0665, 0x3025, 
+    0x3224, 0x2085, 0xFF15, 0xF735, 0x2478, 0x248C, 0x06F5, 0x2174, 
+    0x2075, 0x0E55, 0xFB02, 0x0192, 0xFF46, 0x3399, 0x0E1F, 0x0E1D, 
+    0x0E4F, 0x2200, 0x0034, 0x0664, 0x09EA, 0x2463, 0x278D, 0x096A, 
+    0x0AEA, 0x0A6A, 0x0664, 0x3024, 0x3223, 0x2084, 0xFF14, 0x09F7, 
+    0xF734, 0x2477, 0x248B, 0x06F4, 0x2173, 0x2074, 0x246D, 0x2481, 
+    0x2495, 0x0E54, 0x02CB, 0x24A1, 0x2044, 0x20A3, 0x0067, 0x0997, 
+    0x01F5, 0x0917, 0x06AF, 0xFB93, 0xFB94, 0xFB95, 0x0A97, 0x0A17, 
+    0x304C, 0x30AC, 0x03B3, 0x0263, 0x02E0, 0x03EB, 0x310D, 0x011F, 
+    0x01E7, 0x0123, 0x24D6, 0x011D, 0x0123, 0x0121, 0x0121, 0x0433, 
+    0x3052, 0x30B2, 0x2251, 0x059C, 0x05F3, 0x059D, 0x00DF, 0x059E, 
+    0x05F4, 0x3013, 0x0998, 0x0572, 0x0918, 0x0A98, 0x0A18, 0x063A, 
+    0xFECE, 0xFECF, 0xFED0, 0x0495, 0x0493, 0x0491, 0x095A, 0x0A5A, 
+    0x0260, 0x3393, 0x304E, 0x30AE, 0x0563, 0x05D2, 0xFB32, 0xFB32, 
+    0x05D2, 0x0453, 0x01BE, 0x0294, 0x0296, 0x02C0, 0x0295, 0x02C1, 
+    0x02E4, 0x02A1, 0x02A2, 0x1E21, 0xFF47, 0x3054, 0x30B4, 0x24A2, 
+    0x33AC, 0x2207, 0x0060, 0x0316, 0x0300, 0x0300, 0x0953, 0x02CE, 
+    0xFF40, 0x0340, 0x003E, 0x2265, 0x22DB, 0xFF1E, 0x2273, 0x2277, 
+    0x2267, 0xFE65, 0x0261, 0x01E5, 0x3050, 0x00AB, 0x00BB, 0x2039, 
+    0x203A, 0x30B0, 0x3318, 0x33C9, 0x0068, 0x04A9, 0x06C1, 0x09B9, 
+    0x04B3, 0x0939, 0x0AB9, 0x0A39, 0x062D, 0xFEA2, 0xFEA3, 0x306F, 
+    0xFEA4, 0x332A, 0x30CF, 0xFF8A, 0x0A4D, 0x0621, 0x0621, 0x3164, 
+    0x044A, 0x21BC, 0x21C0, 0x33CA, 0x05B2, 0x05B2, 0x05B2, 0x05B2, 
+    0x05B2, 0x05B2, 0x05B2, 0x05B2, 0x05B3, 0x05B3, 0x05B3, 0x05B3, 
+    0x05B3, 0x05B3, 0x05B3, 0x05B3, 0x05B1, 0x05B1, 0x05B1, 0x05B1, 
+    0x05B1, 0x05B1, 0x05B1, 0x05B1, 0x0127, 0x310F, 0x1E2B, 0x1E29, 
+    0x24D7, 0x0125, 0x1E27, 0x1E23, 0x1E25, 0x05D4, 0x2665, 0x2665, 
+    0x2661, 0xFB34, 0xFB34, 0x06C1, 0x0647, 0x05D4, 0xFBA7, 0xFEEA, 
+    0xFEEA, 0xFBA5, 0xFBA4, 0xFBA8, 0xFEEB, 0x3078, 0xFBA9, 0xFEEC, 
+    0x337B, 0x30D8, 0xFF8D, 0x3336, 0x0267, 0x3339, 0x05D7, 0x05D7, 
+    0x0266, 0x02B1, 0x327B, 0x321B, 0x326D, 0x314E, 0x320D, 0x3072, 
+    0x30D2, 0xFF8B, 0x05B4, 0x05B4, 0x05B4, 0x05B4, 0x05B4, 0x05B4, 
+    0x05B4, 0x05B4, 0x1E96, 0xFF48, 0x0570, 0x0E2B, 0x307B, 0x30DB, 
+    0xFF8E, 0x05B9, 0x05B9, 0x05B9, 0x05B9, 0x05B9, 0x05B9, 0x05B9, 
+    0x05B9, 0x0E2E, 0x0309, 0x0309, 0x0321, 0x0322, 0x3342, 0x03E9, 
+    0x2015, 0x031B, 0x2668, 0x2302, 0x24A3, 0x02B0, 0x0265, 0x3075, 
+    0x3333, 0x30D5, 0xFF8C, 0x02DD, 0x030B, 0x0195, 0x002D, 0xF6E5, 
+    0xFF0D, 0xFE63, 0xF6E6, 0x2010, 0x0069, 0x00ED, 0x044F, 0x0987, 
+    0x3127, 0x012D, 0x01D0, 0x24D8, 0x00EE, 0x0456, 0x0209, 0x328F, 
+    0x328B, 0x323F, 0x323A, 0x32A5, 0x3006, 0x3001, 0xFF64, 0x3237, 
+    0x32A3, 0x322F, 0x323D, 0x329D, 0x3240, 0x3296, 0x3236, 0x322B, 
+    0x3232, 0x32A4, 0x3005, 0x3298, 0x3238, 0x32A7, 0x32A6, 0x32A9, 
+    0x322E, 0x322A, 0x3234, 0x3002, 0x329E, 0x3243, 0x3239, 0x323E, 
+    0x32A8, 0x3299, 0x3242, 0x3233, 0x3000, 0x3235, 0x3231, 0x323B, 
+    0x3230, 0x323C, 0x322C, 0x322D, 0x3007, 0x328E, 0x328A, 0x3294, 
+    0x3290, 0x328C, 0x328D, 0x0907, 0x00EF, 0x1E2F, 0x04E5, 0x1ECB, 
+    0x04D7, 0x0435, 0x3275, 0x3215, 0x3267, 0x3147, 0x3207, 0x00EC, 
+    0x0A87, 0x0A07, 0x3044, 0x1EC9, 0x0988, 0x0438, 0x0908, 0x0A88, 
+    0x0A08, 0x0A40, 0x020B, 0x0439, 0x09C0, 0x0940, 0x0AC0, 0x0133, 
+    0x30A4, 0xFF72, 0x3163, 0x02DC, 0x05AC, 0x012B, 0x04E3, 0x2253, 
+    0x0A3F, 0xFF49, 0x2206, 0x221E, 0x056B, 0x222B, 0x2321, 0x2321, 
+    0xF8F5, 0x2320, 0x2320, 0x2229, 0x3305, 0x25D8, 0x25D9, 0x263B, 
+    0x0451, 0x012F, 0x03B9, 0x03CA, 0x0390, 0x0269, 0x03AF, 0x24A4, 
+    0x0A72, 0x3043, 0x30A3, 0xFF68, 0x09FA, 0x0268, 0xF6ED, 0x309D, 
+    0x30FD, 0x0129, 0x1E2D, 0x3129, 0x044E, 0x09BF, 0x093F, 0x0ABF, 
+    0x0475, 0x0477, 0x006A, 0x0571, 0x099C, 0x091C, 0x0A9C, 0x0A1C, 
+    0x3110, 0x01F0, 0x24D9, 0x0135, 0x029D, 0x025F, 0x0458, 0x062C, 
+    0xFE9E, 0xFE9F, 0xFEA0, 0x0698, 0xFB8B, 0x099D, 0x091D, 0x0A9D, 
+    0x0A1D, 0x057B, 0x3004, 0xFF4A, 0x24A5, 0x02B2, 0x006B, 0x04A1, 
+    0x0995, 0x1E31, 0x043A, 0x049B, 0x0915, 0x05DB, 0x0643, 0xFB3B, 
+    0xFB3B, 0xFEDA, 0x05DB, 0xFEDB, 0xFEDC, 0xFB4D, 0x0A95, 0x0A15, 
+    0x304B, 0x04C4, 0x30AB, 0xFF76, 0x03BA, 0x03F0, 0x3171, 0x3184, 
+    0x3178, 0x3179, 0x330D, 0x0640, 0x0640, 0x30F5, 0x3384, 0x0650, 
+    0x064D, 0x049F, 0xFF70, 0x049D, 0x310E, 0x3389, 0x01E9, 0x0137, 
+    0x24DA, 0x0137, 0x1E33, 0x0584, 0x3051, 0x30B1, 0xFF79, 0x056F, 
+    0x30F6, 0x0138, 0x0996, 0x0445, 0x0916, 0x0A96, 0x0A16, 0x062E, 
+    0xFEA6, 0xFEA7, 0xFEA8, 0x03E7, 0x0959, 0x0A59, 0x3278, 0x3218, 
+    0x326A, 0x314B, 0x320A, 0x0E02, 0x0E05, 0x0E03, 0x0E04, 0x0E5B, 
+    0x0199, 0x0E06, 0x3391, 0x304D, 0x30AD, 0xFF77, 0x3315, 0x3316, 
+    0x3314, 0x326E, 0x320E, 0x3260, 0x3131, 0x3200, 0x3133, 0x045C, 
+    0x1E35, 0x3398, 0x33A6, 0xFF4B, 0x33A2, 0x3053, 0x33C0, 0x0E01, 
+    0x30B3, 0xFF7A, 0x331E, 0x0481, 0x327F, 0x0343, 0x24A6, 0x33AA, 
+    0x046F, 0x33CF, 0x029E, 0x304F, 0x30AF, 0xFF78, 0x33B8, 0x33BE, 
+    0x006C, 0x09B2, 0x013A, 0x0932, 0x0AB2, 0x0A32, 0x0E45, 0xFEFC, 
+    0xFEF8, 0xFEF7, 0xFEFA, 0xFEF9, 0xFEFB, 0xFEF6, 0xFEF5, 0x0644, 
+    0x03BB, 0x019B, 0x05DC, 0xFB3C, 0xFB3C, 0x05DC, 0xFEDE, 0xFCCA, 
+    0xFEDF, 0xFCC9, 0xFCCB, 0xFDF2, 0xFEE0, 0xFD88, 0xFCCC, 0x25EF, 
+    0x019A, 0x026C, 0x310C, 0x013E, 0x013C, 0x24DB, 0x1E3D, 0x013C, 
+    0x0140, 0x0140, 0x1E37, 0x1E39, 0x031A, 0x0318, 0x003C, 0x2264, 
+    0x22DA, 0xFF1C, 0x2272, 0x2276, 0x2266, 0xFE64, 0x026E, 0x258C, 
+    0x026D, 0x20A4, 0x056C, 0x01C9, 0x0459, 0xF6C0, 0x0933, 0x0AB3, 
+    0x1E3B, 0x0934, 0x09E1, 0x0961, 0x09E3, 0x0963, 0x026B, 0xFF4C, 
+    0x33D0, 0x0E2C, 0x2227, 0x00AC, 0x2310, 0x2228, 0x0E25, 0x017F, 
+    0xFE4E, 0x0332, 0xFE4D, 0x25CA, 0x24A7, 0x0142, 0x2113, 0xF6EE, 
+    0x2591, 0x0E26, 0x098C, 0x090C, 0x09E2, 0x0962, 0x33D3, 0x006D, 
+    0x09AE, 0x00AF, 0x0331, 0x0304, 0x02CD, 0xFFE3, 0x1E3F, 0x092E, 
+    0x0AAE, 0x0A2E, 0x05A4, 0x05A4, 0x307E, 0xF895, 0xF894, 0x0E4B, 
+    0xF893, 0xF88C, 0xF88B, 0x0E48, 0xF88A, 0xF884, 0x0E31, 0xF889, 
+    0x0E47, 0xF88F, 0xF88E, 0x0E49, 0xF88D, 0xF892, 0xF891, 0x0E4A, 
+    0xF890, 0x0E46, 0x30DE, 0xFF8F, 0x2642, 0x3347, 0x05BE, 0x2642, 
+    0x05AF, 0x3383, 0x3107, 0x33D4, 0x24DC, 0x33A5, 0x1E41, 0x1E43, 
+    0x0645, 0xFEE2, 0xFEE3, 0xFEE4, 0xFCD1, 0xFC48, 0x334D, 0x3081, 
+    0x337E, 0x30E1, 0xFF92, 0x05DE, 0xFB3E, 0xFB3E, 0x05DE, 0x0574, 
+    0x05A5, 0x05A6, 0x05A6, 0x05A5, 0x0271, 0x3392, 0xFF65, 0x00B7, 
+    0x3272, 0x3212, 0x3264, 0x3141, 0x3170, 0x3204, 0x316E, 0x316F, 
+    0x307F, 0x30DF, 0xFF90, 0x2212, 0x0320, 0x2296, 0x02D7, 0x2213, 
+    0x2032, 0x334A, 0x3349, 0x0270, 0x3396, 0x33A3, 0xFF4D, 0x339F, 
+    0x3082, 0x33C1, 0x30E2, 0xFF93, 0x33D6, 0x0E21, 0x33A7, 0x33A8, 
+    0x24A8, 0x33AB, 0x33B3, 0xF6EF, 0x026F, 0x00B5, 0x00B5, 0x3382, 
+    0x226B, 0x226A, 0x338C, 0x03BC, 0x338D, 0x3080, 0x30E0, 0xFF91, 
+    0x3395, 0x00D7, 0x339B, 0x05A3, 0x05A3, 0x266A, 0x266B, 0x266D, 
+    0x266F, 0x33B2, 0x33B6, 0x33BC, 0x33B9, 0x33B7, 0x33BF, 0x33BD, 
+    0x006E, 0x09A8, 0x2207, 0x0144, 0x0928, 0x0AA8, 0x0A28, 0x306A, 
+    0x30CA, 0xFF85, 0x0149, 0x3381, 0x310B, 0x00A0, 0x0148, 0x0146, 
+    0x24DD, 0x1E4B, 0x0146, 0x1E45, 0x1E47, 0x306D, 0x30CD, 0xFF88, 
+    0x20AA, 0x338B, 0x0999, 0x0919, 0x0A99, 0x0A19, 0x0E07, 0x3093, 
+    0x0272, 0x0273, 0x326F, 0x320F, 0x3135, 0x3261, 0x3136, 0x3134, 
+    0x3168, 0x3201, 0x3167, 0x3166, 0x306B, 0x30CB, 0xFF86, 0xF899, 
+    0x0E4D, 0x0039, 0x0669, 0x09EF, 0x2468, 0x2792, 0x096F, 0x0AEF, 
+    0x0A6F, 0x0669, 0x3029, 0x3228, 0x2089, 0xFF19, 0xF739, 0x247C, 
+    0x2490, 0x06F9, 0x2178, 0x2079, 0x2472, 0x2486, 0x249A, 0x0E59, 
+    0x01CC, 0x045A, 0x30F3, 0xFF9D, 0x019E, 0x1E49, 0xFF4E, 0x339A, 
+    0x09A3, 0x0923, 0x0AA3, 0x0A23, 0x0929, 0x306E, 0x30CE, 0xFF89, 
+    0x00A0, 0x0E13, 0x0E19, 0x0646, 0xFEE6, 0x06BA, 0xFB9F, 0xFEE7, 
+    0xFCD2, 0xFC4B, 0xFEE8, 0xFCD5, 0xFC4E, 0xFC8D, 0x220C, 0x2209, 
+    0x2209, 0x2260, 0x226F, 0x2271, 0x2279, 0x2262, 0x226E, 0x2270, 
+    0x2226, 0x2280, 0x2284, 0x2281, 0x2285, 0x0576, 0x24A9, 0x33B1, 
+    0x207F, 0x00F1, 0x03BD, 0x306C, 0x30CC, 0xFF87, 0x09BC, 0x093C, 
+    0x0ABC, 0x0A3C, 0x0023, 0xFF03, 0xFE5F, 0x0374, 0x0375, 0x2116, 
+    0x05E0, 0xFB40, 0xFB40, 0x05E0, 0x33B5, 0x33BB, 0x099E, 0x091E, 
+    0x0A9E, 0x0A1E, 0x006F, 0x00F3, 0x0E2D, 0x0275, 0x04E9, 0x04EB, 
+    0x0993, 0x311B, 0x014F, 0x0911, 0x0A91, 0x0949, 0x0AC9, 0x01D2, 
+    0x24DE, 0x00F4, 0x1ED1, 0x1ED9, 0x1ED3, 0x1ED5, 0x1ED7, 0x043E, 
+    0x0151, 0x020D, 0x0913, 0x00F6, 0x04E7, 0x1ECD, 0x0153, 0x315A, 
+    0x02DB, 0x0328, 0x00F2, 0x0A93, 0x0585, 0x304A, 0x1ECF, 0x01A1, 
+    0x1EDB, 0x1EE3, 0x1EDD, 0x1EDF, 0x1EE1, 0x0151, 0x01A3, 0x020F, 
+    0x30AA, 0xFF75, 0x3157, 0x05AB, 0x014D, 0x1E53, 0x1E51, 0x0950, 
+    0x03C9, 0x03D6, 0x0461, 0x0277, 0x047B, 0x047D, 0x03CE, 0x0AD0, 
+    0x03BF, 0x03CC, 0xFF4F, 0x0031, 0x0661, 0x09E7, 0x2460, 0x278A, 
+    0x0967, 0x2024, 0x215B, 0xF6DC, 0x0AE7, 0x0A67, 0x0661, 0x00BD, 
+    0x3021, 0x3220, 0x2081, 0xFF11, 0x09F4, 0xF731, 0x2474, 0x2488, 
+    0x06F1, 0x00BC, 0x2170, 0x00B9, 0x0E51, 0x2153, 0x01EB, 0x01ED, 
+    0x0A13, 0x0A4B, 0x0254, 0x24AA, 0x25E6, 0x2325, 0x00AA, 0x00BA, 
+    0x221F, 0x0912, 0x094A, 0x00F8, 0x01FF, 0x3049, 0x30A9, 0xFF6B, 
+    0x01FF, 0xF6F0, 0x047F, 0x00F5, 0x1E4D, 0x1E4F, 0x3121, 0x203E, 
+    0xFE4A, 0x0305, 0xFE49, 0xFE4C, 0xFE4B, 0x00AF, 0x09CB, 0x094B, 
+    0x0ACB, 0x0070, 0x3380, 0x332B, 0x09AA, 0x1E55, 0x092A, 0x21DF, 
+    0x21DE, 0x0AAA, 0x0A2A, 0x3071, 0x0E2F, 0x30D1, 0x0484, 0x04C0, 
+    0x317F, 0x00B6, 0x2225, 0x0028, 0xFD3E, 0xF8ED, 0xF8EC, 0x208D, 
+    0xFF08, 0xFE59, 0x207D, 0xF8EB, 0xFE35, 0x0029, 0xFD3F, 0xF8F8, 
+    0xF8F7, 0x208E, 0xFF09, 0xFE5A, 0x207E, 0xF8F6, 0xFE36, 0x2202, 
+    0x05C0, 0x0599, 0x33A9, 0x05B7, 0x05B7, 0x05B7, 0x05B7, 0x05B7, 
+    0x05B7, 0x05B7, 0x05B7, 0x05A1, 0x3106, 0x24DF, 0x1E57, 0x05E4, 
+    0x043F, 0xFB44, 0xFB44, 0x333B, 0xFB43, 0x067E, 0x057A, 0x05E4, 
+    0xFB57, 0xFB58, 0x307A, 0xFB59, 0x30DA, 0x04A7, 0xFB4E, 0x0025, 
+    0x066A, 0xFF05, 0xFE6A, 0x002E, 0x0589, 0x00B7, 0xFF61, 0xF6E7, 
+    0xFF0E, 0xFE52, 0xF6E8, 0x0342, 0x22A5, 0x2030, 0x20A7, 0x338A, 
+    0x09AB, 0x092B, 0x0AAB, 0x0A2B, 0x03C6, 0x03D5, 0x327A, 0x321A, 
+    0x326C, 0x314D, 0x320C, 0x0278, 0x0E3A, 0x03D5, 0x01A5, 0x0E1E, 
+    0x0E1C, 0x0E20, 0x03C0, 0x3273, 0x3213, 0x3176, 0x3265, 0x3172, 
+    0x3142, 0x3205, 0x3174, 0x3144, 0x3175, 0x3177, 0x3173, 0x3074, 
+    0x30D4, 0x03D6, 0x0583, 0x002B, 0x031F, 0x2295, 0x00B1, 0x02D6, 
+    0xFF0B, 0xFE62, 0x207A, 0xFF50, 0x33D8, 0x307D, 0x261F, 0x261C, 
+    0x261E, 0x261D, 0x30DD, 0x0E1B, 0x3012, 0x3020, 0x24AB, 0x227A, 
+    0x211E, 0x02B9, 0x2035, 0x220F, 0x2305, 0x30FC, 0x2318, 0x2282, 
+    0x2283, 0x2237, 0x221D, 0x03C8, 0x0471, 0x0486, 0x33B0, 0x3077, 
+    0x30D7, 0x33B4, 0x33BA, 0x0071, 0x0958, 0x05A8, 0x0642, 0xFED6, 
+    0xFED7, 0xFED8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 
+    0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 0x05B8, 
+    0x05B8, 0x05B8, 0x059F, 0x3111, 0x24E0, 0x02A0, 0xFF51, 0x05E7, 
+    0xFB47, 0xFB47, 0x05E7, 0x24AC, 0x2669, 0x05BB, 0x05BB, 0x05BB, 
+    0x05BB, 0x05BB, 0x05BB, 0x05BB, 0x05BB, 0x003F, 0x061F, 0x055E, 
+    0x00BF, 0xF7BF, 0x037E, 0xFF1F, 0xF73F, 0x0022, 0x201E, 0x201C, 
+    0xFF02, 0x301E, 0x301D, 0x201D, 0x2018, 0x201B, 0x201B, 0x2019, 
+    0x0149, 0x201A, 0x0027, 0xFF07, 0x0072, 0x057C, 0x09B0, 0x0155, 
+    0x0930, 0x221A, 0xF8E5, 0x33AE, 0x33AF, 0x33AD, 0x05BF, 0x05BF, 
+    0x0AB0, 0x0A30, 0x3089, 0x30E9, 0xFF97, 0x09F1, 0x09F0, 0x0264, 
+    0x2236, 0x3116, 0x0159, 0x0157, 0x24E1, 0x0157, 0x0211, 0x1E59, 
+    0x1E5B, 0x1E5D, 0x203B, 0x2286, 0x2287, 0x00AE, 0xF8E8, 0xF6DA, 
+    0x0631, 0x0580, 0xFEAE, 0x308C, 0x30EC, 0xFF9A, 0x05E8, 0xFB48, 
+    0x05E8, 0x223D, 0x0597, 0x0597, 0x2310, 0x027E, 0x027F, 0x09DD, 
+    0x095D, 0x03C1, 0x027D, 0x027B, 0x02B5, 0x03F1, 0x02DE, 0x3271, 
+    0x3211, 0x3263, 0x3140, 0x313A, 0x3169, 0x3139, 0x313B, 0x316C, 
+    0x3203, 0x313F, 0x313C, 0x316B, 0x313D, 0x313E, 0x316A, 0x316D, 
+    0x221F, 0x0319, 0x22BF, 0x308A, 0x30EA, 0xFF98, 0x02DA, 0x0325, 
+    0x030A, 0x02BF, 0x0559, 0x031C, 0x02D3, 0x02BE, 0x0339, 0x02D2, 
+    0x0213, 0x3351, 0x1E5F, 0x027C, 0x027A, 0xFF52, 0x308D, 0x30ED, 
+    0xFF9B, 0x0E23, 0x24AD, 0x09DC, 0x0931, 0x0A5C, 0x0691, 0xFB8D, 
+    0x09E0, 0x0960, 0x0AE0, 0x09C4, 0x0944, 0x0AC4, 0xF6F1, 0x2590, 
+    0x0279, 0x02B4, 0x308B, 0x30EB, 0xFF99, 0x09F2, 0x09F3, 0xF6DD, 
+    0x0E24, 0x098B, 0x090B, 0x0A8B, 0x09C3, 0x0943, 0x0AC3, 0x0073, 
+    0x09B8, 0x015B, 0x1E65, 0x0635, 0x0938, 0xFEBA, 0xFEBB, 0xFEBC, 
+    0x0AB8, 0x0A38, 0x3055, 0x30B5, 0xFF7B, 0xFDFA, 0x05E1, 0xFB41, 
+    0xFB41, 0x05E1, 0x0E32, 0x0E41, 0x0E44, 0x0E43, 0x0E33, 0x0E30, 
+    0x0E40, 0xF886, 0x0E35, 0xF885, 0x0E34, 0x0E42, 0xF888, 0x0E37, 
+    0xF887, 0x0E36, 0x0E38, 0x0E39, 0x3119, 0x0161, 0x1E67, 0x015F, 
+    0x0259, 0x04D9, 0x04DB, 0x025A, 0x24E2, 0x015D, 0x0219, 0x1E61, 
+    0x1E63, 0x1E69, 0x033C, 0x2033, 0x02CA, 0x00A7, 0x0633, 0xFEB2, 
+    0xFEB3, 0xFEB4, 0x05B6, 0x05B6, 0x05B6, 0x05B6, 0x05B6, 0x05B6, 
+    0x05B6, 0x0592, 0x05B6, 0x057D, 0x305B, 0x30BB, 0xFF7E, 0x003B, 
+    0x061B, 0xFF1B, 0xFE54, 0x309C, 0xFF9F, 0x3322, 0x3323, 0x0037, 
+    0x0667, 0x09ED, 0x2466, 0x2790, 0x096D, 0x215E, 0x0AED, 0x0A6D, 
+    0x0667, 0x3027, 0x3226, 0x2087, 0xFF17, 0xF737, 0x247A, 0x248E, 
+    0x06F7, 0x2176, 0x2077, 0x2470, 0x2484, 0x2498, 0x0E57, 0x00AD, 
+    0x0577, 0x09B6, 0x0448, 0x0651, 0xFC61, 0xFC5E, 0xFC60, 0xFC62, 
+    0xFC5F, 0x2592, 0x2593, 0x2591, 0x2592, 0x0936, 0x0AB6, 0x0A36, 
+    0x0593, 0x3115, 0x0449, 0x0634, 0xFEB6, 0xFEB7, 0xFEB8, 0x03E3, 
+    0x20AA, 0x20AA, 0x05B0, 0x05B0, 0x05B0, 0x05B0, 0x05B0, 0x05B0, 
+    0x05B0, 0x05B0, 0x05B0, 0x04BB, 0x03ED, 0x05E9, 0xFB49, 0xFB49, 
+    0xFB2C, 0xFB2C, 0xFB2D, 0xFB2D, 0x05C1, 0x05E9, 0xFB2A, 0xFB2A, 
+    0xFB2B, 0xFB2B, 0x0282, 0x03C3, 0x03C2, 0x03C2, 0x03F2, 0x3057, 
+    0x30B7, 0xFF7C, 0x05BD, 0x05BD, 0x223C, 0x05C2, 0x3274, 0x3214, 
+    0x317E, 0x3266, 0x317A, 0x3145, 0x317B, 0x3206, 0x317D, 0x317C, 
+    0x0036, 0x0666, 0x09EC, 0x2465, 0x278F, 0x096C, 0x0AEC, 0x0A6C, 
+    0x0666, 0x3026, 0x3225, 0x2086, 0xFF16, 0xF736, 0x2479, 0x248D, 
+    0x06F6, 0x2175, 0x2076, 0x246F, 0x09F9, 0x2483, 0x2497, 0x0E56, 
+    0x002F, 0xFF0F, 0x017F, 0x1E9B, 0x263A, 0xFF53, 0x05C3, 0x00AD, 
+    0x044C, 0x305D, 0x30BD, 0xFF7F, 0x0338, 0x0337, 0x0E29, 0x0E28, 
+    0x0E0B, 0x0E2A, 0x0020, 0x0020, 0x2660, 0x2660, 0x2664, 0x24AE, 
+    0x033B, 0x33C4, 0x339D, 0x25A9, 0x25A4, 0x338F, 0x339E, 0x33CE, 
+    0x33D1, 0x33D2, 0x338E, 0x33D5, 0x339C, 0x33A1, 0x25A6, 0x25A7, 
+    0x25A8, 0x25A5, 0x25A3, 0x33DB, 0x09B7, 0x0937, 0x0AB7, 0x3149, 
+    0x3185, 0x3180, 0x3132, 0x3165, 0x3143, 0x3146, 0x3138, 0xF6F2, 
+    0x00A3, 0xFFE1, 0x0336, 0x0335, 0x2282, 0x228A, 0x2286, 0x227B, 
+    0x220B, 0x3059, 0x30B9, 0xFF7D, 0x0652, 0x2211, 0x263C, 0x2283, 
+    0x228B, 0x2287, 0x33DC, 0x337C, 0x0074, 0x09A4, 0x22A4, 0x22A3, 
+    0x0924, 0x0AA4, 0x0A24, 0x0637, 0xFEC2, 0xFEC3, 0x305F, 0xFEC4, 
+    0x337D, 0x30BF, 0xFF80, 0x0640, 0x03C4, 0x05EA, 0xFB4A, 0xFB4A, 
+    0xFB4A, 0x05EA, 0x0167, 0x310A, 0x0165, 0x02A8, 0x0163, 0x0686, 
+    0xFB7B, 0xFB7C, 0xFB7D, 0x24E3, 0x1E71, 0x0163, 0x1E97, 0x1E6B, 
+    0x1E6D, 0x0442, 0x04AD, 0x062A, 0xFE96, 0xFCA2, 0xFC0C, 0xFE97, 
+    0x3066, 0xFCA1, 0xFC0B, 0x0629, 0xFE94, 0xFE98, 0xFCA4, 0xFC0E, 
+    0xFC73, 0x30C6, 0xFF83, 0x2121, 0x260E, 0x05A0, 0x05A9, 0x2469, 
+    0x3229, 0x247D, 0x2491, 0x2179, 0x02A7, 0x05D8, 0xFB38, 0xFB38, 
+    0x05D8, 0x04B5, 0x059B, 0x059B, 0x09A5, 0x0925, 0x0AA5, 0x0A25, 
+    0x0630, 0xFEAC, 0xF898, 0xF897, 0x0E4C, 0xF896, 0x062B, 0xFE9A, 
+    0xFE9B, 0xFE9C, 0x2203, 0x2234, 0x03B8, 0x03D1, 0x03D1, 0x3279, 
+    0x3219, 0x326B, 0x314C, 0x320B, 0x246C, 0x2480, 0x2494, 0x0E11, 
+    0x01AD, 0x0E12, 0x00FE, 0x0E17, 0x0E10, 0x0E18, 0x0E16, 0x0482, 
+    0x066C, 0x066C, 0x0033, 0x0663, 0x09E9, 0x2462, 0x278C, 0x0969, 
+    0x215C, 0x0AE9, 0x0A69, 0x0663, 0x3023, 0x3222, 0x2083, 0xFF13, 
+    0x09F6, 0xF733, 0x2476, 0x248A, 0x06F3, 0x00BE, 0xF6DE, 0x2172, 
+    0x00B3, 0x0E53, 0x3394, 0x3061, 0x30C1, 0xFF81, 0x3270, 0x3210, 
+    0x3262, 0x3137, 0x3202, 0x02DC, 0x0330, 0x0303, 0x0303, 0x0360, 
+    0x223C, 0x0334, 0x033E, 0x2297, 0x0596, 0x0596, 0x0A70, 0x0483, 
+    0x057F, 0x1E6F, 0xFF54, 0x0569, 0x3068, 0x30C8, 0xFF84, 0x02E5, 
+    0x02E9, 0x02E6, 0x02E8, 0x02E7, 0x01BD, 0x0185, 0x01A8, 0x0384, 
+    0x3327, 0x0E0F, 0x3014, 0xFE5D, 0xFE39, 0x3015, 0xFE5E, 0xFE3A, 
+    0x0E15, 0x01AB, 0x24AF, 0x2122, 0xF8EA, 0xF6DB, 0x0288, 0x25BC, 
+    0x25C4, 0x25BA, 0x25B2, 0x02A6, 0x05E6, 0xFB46, 0xFB46, 0x05E6, 
+    0x0446, 0x05B5, 0x05B5, 0x05B5, 0x05B5, 0x05B5, 0x05B5, 0x05B5, 
+    0x05B5, 0x045B, 0xF6F3, 0x099F, 0x091F, 0x0A9F, 0x0A1F, 0x0679, 
+    0xFB67, 0xFB68, 0xFB69, 0x09A0, 0x0920, 0x0AA0, 0x0A20, 0x0287, 
+    0x3064, 0x30C4, 0xFF82, 0x3063, 0x30C3, 0xFF6F, 0x246B, 0x247F, 
+    0x2493, 0x217B, 0x2473, 0x5344, 0x2487, 0x249B, 0x0032, 0x0662, 
+    0x09E8, 0x2461, 0x278B, 0x0968, 0x2025, 0x2025, 0xFE30, 0x0AE8, 
+    0x0A68, 0x0662, 0x3022, 0x3221, 0x2082, 0xFF12, 0x09F5, 0xF732, 
+    0x2475, 0x2489, 0x06F2, 0x2171, 0x01BB, 0x00B2, 0x0E52, 0x2154, 
+    0x0075, 0x00FA, 0x0289, 0x0989, 0x3128, 0x016D, 0x01D4, 0x24E4, 
+    0x00FB, 0x1E77, 0x0443, 0x0951, 0x0171, 0x0215, 0x0909, 0x00FC, 
+    0x01D8, 0x1E73, 0x01DA, 0x04F1, 0x01DC, 0x01D6, 0x1EE5, 0x00F9, 
+    0x0A89, 0x0A09, 0x3046, 0x1EE7, 0x01B0, 0x1EE9, 0x1EF1, 0x1EEB, 
+    0x1EED, 0x1EEF, 0x0171, 0x04F3, 0x0217, 0x30A6, 0xFF73, 0x0479, 
+    0x315C, 0x016B, 0x04EF, 0x1E7B, 0x0A41, 0xFF55, 0x005F, 0x2017, 
+    0xFF3F, 0xFE33, 0xFE4F, 0x222A, 0x2200, 0x0173, 0x24B0, 0x2580, 
+    0x05C4, 0x03C5, 0x03CB, 0x03B0, 0x028A, 0x03CD, 0x031D, 0x02D4, 
+    0x0A73, 0x016F, 0x045E, 0x3045, 0x30A5, 0xFF69, 0x04AF, 0x04B1, 
+    0x0169, 0x1E79, 0x1E75, 0x098A, 0x090A, 0x0A8A, 0x0A0A, 0x0A42, 
+    0x09C2, 0x0942, 0x0AC2, 0x09C1, 0x0941, 0x0AC1, 0x0076, 0x0935, 
+    0x0AB5, 0x0A35, 0x30F7, 0x05D5, 0xFB35, 0xFB35, 0xFB35, 0x05D5, 
+    0xFB4B, 0xFB4B, 0x05F0, 0x05F1, 0x24E5, 0x1E7F, 0x0432, 0x06A4, 
+    0xFB6B, 0xFB6C, 0xFB6D, 0x30F9, 0x2640, 0x007C, 0x030D, 0x0329, 
+    0x02CC, 0x02C8, 0x057E, 0x028B, 0x30F8, 0x09CD, 0x094D, 0x0ACD, 
+    0x0983, 0x0903, 0x0A83, 0xFF56, 0x0578, 0x309E, 0x30FE, 0x309B, 
+    0xFF9E, 0x30FA, 0x24B1, 0x1E7D, 0x028C, 0x3094, 0x30F4, 0x0077, 
+    0x1E83, 0x3159, 0x308F, 0x30EF, 0xFF9C, 0x3158, 0x308E, 0x30EE, 
+    0x3357, 0x301C, 0xFE34, 0x0648, 0xFEEE, 0x0624, 0xFE86, 0x33DD, 
+    0x24E6, 0x0175, 0x1E85, 0x1E87, 0x1E89, 0x3091, 0x2118, 0x30F1, 
+    0x315E, 0x315D, 0x1E81, 0x25E6, 0x25CB, 0x25D9, 0x300E, 0xFE43, 
+    0x300F, 0xFE44, 0x25C7, 0x25C8, 0x25BF, 0x25BD, 0x25C3, 0x25C1, 
+    0x3016, 0x3017, 0x25B9, 0x25B7, 0x25AB, 0x263A, 0x25A1, 0x2606, 
+    0x260F, 0x3018, 0x3019, 0x25B5, 0x25B3, 0x3090, 0x30F0, 0x315F, 
+    0xFF57, 0x3092, 0x30F2, 0xFF66, 0x20A9, 0xFFE6, 0x0E27, 0x24B2, 
+    0x1E98, 0x02B7, 0x028D, 0x01BF, 0x0078, 0x033D, 0x3112, 0x24E7, 
+    0x1E8D, 0x1E8B, 0x056D, 0x03BE, 0xFF58, 0x24B3, 0x02E3, 0x0079, 
+    0x334E, 0x09AF, 0x00FD, 0x092F, 0x3152, 0x0AAF, 0x0A2F, 0x3084, 
+    0x30E4, 0xFF94, 0x3151, 0x0E4E, 0x3083, 0x30E3, 0xFF6C, 0x0463, 
+    0x24E8, 0x0177, 0x00FF, 0x1E8F, 0x1EF5, 0x064A, 0x06D2, 0xFBAF, 
+    0xFEF2, 0x0626, 0xFE8A, 0xFE8B, 0xFE8C, 0xFEF3, 0xFEF4, 0xFCDD, 
+    0xFC58, 0xFC94, 0x06D1, 0x3156, 0x00A5, 0xFFE5, 0x3155, 0x3186, 
+    0x05AA, 0x05AA, 0x044B, 0x04F9, 0x3181, 0x3183, 0x3182, 0x059A, 
+    0x1EF3, 0x01B4, 0x1EF7, 0x0575, 0x0457, 0x3162, 0x262F, 0x0582, 
+    0xFF59, 0x05D9, 0xFB39, 0xFB39, 0x05D9, 0x05F2, 0xFB1F, 0x3088, 
+    0x3189, 0x30E8, 0xFF96, 0x315B, 0x3087, 0x30E7, 0xFF6E, 0x03F3, 
+    0x3188, 0x3187, 0x0E22, 0x0E0D, 0x24B4, 0x037A, 0x0345, 0x01A6, 
+    0x1E99, 0x02B8, 0x1EF9, 0x028E, 0x3086, 0x318C, 0x30E6, 0xFF95, 
+    0x3160, 0x046B, 0x046D, 0x0467, 0x0469, 0x3085, 0x30E5, 0xFF6D, 
+    0x318B, 0x318A, 0x09DF, 0x095F, 0x007A, 0x0566, 0x017A, 0x095B, 
+    0x0A5B, 0x0638, 0xFEC6, 0xFEC7, 0x3056, 0xFEC8, 0x0632, 0xFEB0, 
+    0x30B6, 0x0595, 0x0594, 0x0598, 0x05D6, 0xFB36, 0xFB36, 0x05D6, 
+    0x3117, 0x017E, 0x24E9, 0x1E91, 0x0291, 0x017C, 0x017C, 0x1E93, 
+    0x0437, 0x0499, 0x04DF, 0x305C, 0x30BC, 0x0030, 0x0660, 0x09E6, 
+    0x0966, 0x0AE6, 0x0A66, 0x0660, 0x2080, 0xFF10, 0xF730, 0x06F0, 
+    0x2070, 0x0E50, 0xFEFF, 0x200C, 0x200B, 0x03B6, 0x3113, 0x056A, 
+    0x04C2, 0x0436, 0x0497, 0x04DD, 0x3058, 0x30B8, 0x05AE, 0x1E95, 
+    0xFF5A, 0x305E, 0x30BE, 0x24B5, 0x0290, 0x01B6, 0x305A, 0x30BA, 
+};
+
+wchar_t ps_glyph_to_unicode(char const *glyph)
+{
+    int i, j, k, c;
+
+    i = -1;
+    j = lenof(ps_glyphs_alphabetic);
+    while (j-i > 1) {
+       k = (i + j) / 2;
+       c = strcmp(glyph, ps_glyphs_alphabetic[k]);
+
+       if (c == 0)
+           return ps_codes_alphabetic[k];
+       else if (c < 0)
+           j = k;
+       else
+           i = k;
+    }
+
+    return 0xFFFF;                    /* illegal value means not found */
+}
+
+/* ----------------------------------------------------------------------
+ * Data about the standard fonts: available glyphs and font metrics.
+ */
+
+/*
+ * Character set list extracted from the back of the PostScript
+ * Language Reference Manual.
+ * 
+ * I'm going to include a bit of shell which creates a file
+ * containing the list of characters, and then several further bits
+ * of shell that process it in different ways.
+ */
+
+/*
+
+tr -s ' \n' '\n' << EOF > stdchars.txt
+A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+a b c d e f g h i j k l m n o p q r s t u v w x y z
+AE Aacute Acircumflex Adieresis Agrave Aring Atilde
+Ccedilla
+Eacute Ecircumflex Edieresis Egrave
+Eth
+Iacute Icircumflex Idieresis Igrave
+Lslash
+Ntilde
+OE Oacute Ocircumflex Odieresis Ograve Oslash Otilde
+Scaron
+Thorn
+Uacute Ucircumflex Udieresis Ugrave
+Yacute Ydieresis
+Zcaron
+ae aacute acircumflex adieresis agrave aring atilde
+ccedilla
+eacute ecircumflex edieresis egrave
+eth
+iacute icircumflex idieresis igrave
+lslash
+ntilde
+oe oacute ocircumflex odieresis ograve oslash otilde
+scaron
+thorn
+uacute ucircumflex udieresis ugrave
+yacute ydieresis
+zcaron
+acute ampersand asciicircum asciitilde asterisk at backslash bar
+braceleft braceright bracketleft bracketright breve brokenbar bullet
+caron cedilla cent circumflex colon comma copyright currency dagger
+daggerdbl degree dieresis divide dollar dotaccent dotlessi eight
+ellipsis emdash endash equal exclam exclamdown fi five fl florin
+four fraction germandbls grave greater guillemotleft guillemotright
+guilsinglleft guilsinglright hungarumlaut hyphen less logicalnot
+macron minus mu multiply nine numbersign ogonek one onehalf onequarter
+onesuperior ordfeminine ordmasculine paragraph parenleft parenright
+percent period periodcentered perthousand plus plusminus question
+questiondown quotedbl quotedblbase quotedblleft quotedblright quoteleft
+quoteright quotesinglbase quotesingle registered ring section semicolon
+seven six slash space sterling three threequarters threesuperior tilde
+trademark two twosuperior underscore yen zero
+EOF
+
+ */
+
+/*
+ * A simple string array giving the glyph list.
+ */
+
+/*
+
+perl -ne 'chomp; print "\"$_\", "; END { print "NULL" }' stdchars.txt | \
+    fold -s -w68 | sed 's/^/    /'
+
+*/
+
+const char *const ps_std_glyphs[] = {
+    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 
+    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", 
+    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 
+    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", 
+    "AE", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", 
+    "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", 
+    "Egrave", "Eth", "Iacute", "Icircumflex", "Idieresis", "Igrave", 
+    "Lslash", "Ntilde", "OE", "Oacute", "Ocircumflex", "Odieresis", 
+    "Ograve", "Oslash", "Otilde", "Scaron", "Thorn", "Uacute", 
+    "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", 
+    "Zcaron", "ae", "aacute", "acircumflex", "adieresis", "agrave", 
+    "aring", "atilde", "ccedilla", "eacute", "ecircumflex", 
+    "edieresis", "egrave", "eth", "iacute", "icircumflex", "idieresis", 
+    "igrave", "lslash", "ntilde", "oe", "oacute", "ocircumflex", 
+    "odieresis", "ograve", "oslash", "otilde", "scaron", "thorn", 
+    "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", 
+    "ydieresis", "zcaron", "acute", "ampersand", "asciicircum", 
+    "asciitilde", "asterisk", "at", "backslash", "bar", "braceleft", 
+    "braceright", "bracketleft", "bracketright", "breve", "brokenbar", 
+    "bullet", "caron", "cedilla", "cent", "circumflex", "colon", 
+    "comma", "copyright", "currency", "dagger", "daggerdbl", "degree", 
+    "dieresis", "divide", "dollar", "dotaccent", "dotlessi", "eight", 
+    "ellipsis", "emdash", "endash", "equal", "exclam", "exclamdown", 
+    "fi", "five", "fl", "florin", "four", "fraction", "germandbls", 
+    "grave", "greater", "guillemotleft", "guillemotright", 
+    "guilsinglleft", "guilsinglright", "hungarumlaut", "hyphen", 
+    "less", "logicalnot", "macron", "minus", "mu", "multiply", "nine", 
+    "numbersign", "ogonek", "one", "onehalf", "onequarter", 
+    "onesuperior", "ordfeminine", "ordmasculine", "paragraph", 
+    "parenleft", "parenright", "percent", "period", "periodcentered", 
+    "perthousand", "plus", "plusminus", "question", "questiondown", 
+    "quotedbl", "quotedblbase", "quotedblleft", "quotedblright", 
+    "quoteleft", "quoteright", "quotesinglbase", "quotesingle", 
+    "registered", "ring", "section", "semicolon", "seven", "six", 
+    "slash", "space", "sterling", "three", "threequarters", 
+    "threesuperior", "tilde", "trademark", "two", "twosuperior", 
+    "underscore", "yen", "zero", NULL
+};
+
+/*
+ * Character width information from the standard PS fonts. I can
+ * see no more sensible way to get hold of this information than by
+ * extracting it from GhostScript.
+ * 
+ * Initial experimentation with this technique yielded a load of
+ * floating-point values, which on closer examination all turned
+ * out (modulo rounding error due to decimal representation) to be
+ * exact multiples of 1/4096. Accordingly I now store all these
+ * arrays as integers with a scale of 4096.
+ */
+
+/*
+
+for i in Times-Roman Times-Italic Times-Bold Times-BoldItalic \
+         Helvetica Helvetica-Oblique Helvetica-Bold Helvetica-BoldOblique \
+         Courier Courier-Oblique Courier-Bold Courier-BoldOblique; do
+    echo "    {\"$i\", {"
+    (echo "/fn /$i findfont def";
+     echo '/cw { fn dup length dict begin';
+     echo '{1 index /FID ne {def} {pop pop} ifelse} forall';
+     echo '/Encoding Encoding dup length array copy def';
+     echo 'Encoding exch 65 exch put';
+     echo 'currentdict end /Spingly exch definefont setfont';
+     echo '(A) stringwidth pop 4096 mul round cvi /Spingly undefinefont} def';
+     perl -ne 'chomp; print "/$_ cw ==\n"' stdchars.txt) | \
+        gs -q -sDEVICE=nullpage - | sed 's/$/,/' | tr '\n' ' ' | \
+        fold -s -w68 | sed 's/^/        /'; echo
+    echo "    }},"
+done
+
+*/
+
+static const struct ps_std_font_data {
+    char const *name;
+    int widths[lenof(ps_std_glyphs)-1];
+} ps_std_fonts[] = {
+    {"Times-Roman", {
+        2957, 2732, 2732, 2957, 2502, 2277, 2957, 2957, 1363, 1593, 2957, 
+        2502, 3641, 2957, 2957, 2277, 2957, 2732, 2277, 2502, 2957, 2957, 
+        3866, 2957, 2957, 2502, 1818, 2048, 1818, 2048, 1818, 1363, 2048, 
+        2048, 1138, 1138, 2048, 1138, 3186, 2048, 2048, 2048, 2048, 1363, 
+        1593, 1138, 2048, 2048, 2957, 2048, 2048, 1818, 3641, 2957, 2957, 
+        2957, 2957, 2957, 2957, 2732, 2502, 2502, 2502, 2502, 2957, 1363, 
+        1363, 1363, 1363, 2502, 2957, 3641, 2957, 2957, 2957, 2957, 2957, 
+        2957, 2277, 2277, 2957, 2957, 2957, 2957, 2957, 2957, 2502, 2732, 
+        1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 
+        2048, 1138, 1138, 1138, 1138, 1138, 2048, 2957, 2048, 2048, 2048, 
+        2048, 2048, 2048, 1593, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 
+        1818, 1363, 3186, 1921, 2215, 2048, 3772, 1138, 819, 1966, 1966, 
+        1363, 1363, 1363, 819, 1433, 1363, 1363, 2048, 1363, 1138, 1024, 
+        3112, 2048, 2048, 2048, 1638, 1363, 2310, 2048, 1363, 1138, 2048, 
+        4096, 4096, 2048, 2310, 1363, 1363, 2277, 2048, 2277, 2048, 2048, 
+        684, 2048, 1363, 2310, 2048, 2048, 1363, 1363, 1363, 1363, 2310, 
+        2310, 1363, 2310, 2048, 2310, 2048, 2048, 1363, 2048, 3072, 3072, 
+        1228, 1130, 1269, 1855, 1363, 1363, 3411, 1024, 1024, 4096, 2310, 
+        2310, 1818, 1818, 1671, 1818, 1818, 1818, 1363, 1363, 1363, 737, 
+        3112, 1363, 2048, 1138, 2048, 2048, 1138, 1024, 2048, 2048, 3072, 
+        1228, 1363, 4014, 2048, 1228, 2048, 2048, 2048, 
+    }},
+    {"Times-Italic", {
+        2502, 2502, 2732, 2957, 2502, 2502, 2957, 2957, 1363, 1818, 2732, 
+        2277, 3411, 2732, 2957, 2502, 2957, 2502, 2048, 2277, 2957, 2502, 
+        3411, 2502, 2277, 2277, 2048, 2048, 1818, 2048, 1818, 1138, 2048, 
+        2048, 1138, 1138, 1818, 1138, 2957, 2048, 2048, 2048, 2048, 1593, 
+        1593, 1138, 2048, 1818, 2732, 1818, 1818, 1593, 3641, 2502, 2502, 
+        2502, 2502, 2502, 2502, 2732, 2502, 2502, 2502, 2502, 2957, 1363, 
+        1363, 1363, 1363, 2277, 2732, 3866, 2957, 2957, 2957, 2957, 2957, 
+        2957, 2048, 2502, 2957, 2957, 2957, 2957, 2277, 2277, 2277, 2732, 
+        2048, 2048, 2048, 2048, 2048, 2048, 1818, 1818, 1818, 1818, 1818, 
+        2048, 1138, 1138, 1138, 1138, 1138, 2048, 2732, 2048, 2048, 2048, 
+        2048, 2048, 2048, 1593, 2048, 2048, 2048, 2048, 2048, 1818, 1818, 
+        1593, 1363, 3186, 1728, 2215, 2048, 3768, 1138, 1126, 1638, 1638, 
+        1593, 1593, 1363, 1126, 1433, 1363, 1363, 2048, 1363, 1363, 1024, 
+        3112, 2048, 2048, 2048, 1638, 1363, 2764, 2048, 1363, 1138, 2048, 
+        3641, 3641, 2048, 2764, 1363, 1593, 2048, 2048, 2048, 2048, 2048, 
+        684, 2048, 1363, 2764, 2048, 2048, 1363, 1363, 1363, 1363, 2764, 
+        2764, 1363, 2764, 2048, 2764, 2048, 2048, 1363, 2048, 3072, 3072, 
+        1228, 1130, 1269, 2142, 1363, 1363, 3411, 1024, 1024, 4096, 2764, 
+        2764, 2048, 2048, 1720, 2277, 2277, 2277, 1363, 1363, 1363, 876, 
+        3112, 1363, 2048, 1363, 2048, 2048, 1138, 1024, 2048, 2048, 3072, 
+        1228, 1363, 4014, 2048, 1228, 2048, 2048, 2048, 
+    }},
+    {"Times-Bold", {
+        2957, 2732, 2957, 2957, 2732, 2502, 3186, 3186, 1593, 2048, 3186, 
+        2732, 3866, 2957, 3186, 2502, 3186, 2957, 2277, 2732, 2957, 2957, 
+        4096, 2957, 2957, 2732, 2048, 2277, 1818, 2277, 1818, 1363, 2048, 
+        2277, 1138, 1363, 2277, 1138, 3411, 2277, 2048, 2277, 2277, 1818, 
+        1593, 1363, 2277, 2048, 2957, 2048, 2048, 1818, 4096, 2957, 2957, 
+        2957, 2957, 2957, 2957, 2957, 2732, 2732, 2732, 2732, 2957, 1593, 
+        1593, 1593, 1593, 2732, 2957, 4096, 3186, 3186, 3186, 3186, 3186, 
+        3186, 2277, 2502, 2957, 2957, 2957, 2957, 2957, 2957, 2732, 2957, 
+        2048, 2048, 2048, 2048, 2048, 2048, 1818, 1818, 1818, 1818, 1818, 
+        2048, 1138, 1138, 1138, 1138, 1138, 2277, 2957, 2048, 2048, 2048, 
+        2048, 2048, 2048, 1593, 2277, 2277, 2277, 2277, 2277, 2048, 2048, 
+        1818, 1363, 3411, 2379, 2129, 2048, 3809, 1138, 901, 1613, 1613, 
+        1363, 1363, 1363, 901, 1433, 1363, 1363, 2048, 1363, 1363, 1024, 
+        3059, 2048, 2048, 2048, 1638, 1363, 2334, 2048, 1363, 1138, 2048, 
+        4096, 4096, 2048, 2334, 1363, 1363, 2277, 2048, 2277, 2048, 2048, 
+        684, 2277, 1363, 2334, 2048, 2048, 1363, 1363, 1363, 1363, 2334, 
+        2334, 1363, 2334, 2277, 2334, 2048, 2048, 1363, 2048, 3072, 3072, 
+        1228, 1228, 1351, 2211, 1363, 1363, 4096, 1024, 1024, 4096, 2334, 
+        2334, 2048, 2048, 2273, 2048, 2048, 2048, 1363, 1363, 1363, 1138, 
+        3059, 1363, 2048, 1363, 2048, 2048, 1138, 1024, 2048, 2048, 3072, 
+        1228, 1363, 4096, 2048, 1228, 2048, 2048, 2048, 
+    }},
+    {"Times-BoldItalic", {
+        2732, 2732, 2732, 2957, 2732, 2732, 2957, 3186, 1593, 2048, 2732, 
+        2502, 3641, 2957, 2957, 2502, 2957, 2732, 2277, 2502, 2957, 2732, 
+        3641, 2732, 2502, 2502, 2048, 2048, 1818, 2048, 1818, 1363, 2048, 
+        2277, 1138, 1138, 2048, 1138, 3186, 2277, 2048, 2048, 2048, 1593, 
+        1593, 1138, 2277, 1818, 2732, 2048, 1818, 1593, 3866, 2732, 2732, 
+        2732, 2732, 2732, 2732, 2732, 2732, 2732, 2732, 2732, 2957, 1593, 
+        1593, 1593, 1593, 2502, 2957, 3866, 2957, 2957, 2957, 2957, 2957, 
+        2957, 2277, 2502, 2957, 2957, 2957, 2957, 2502, 2502, 2502, 2957, 
+        2048, 2048, 2048, 2048, 2048, 2048, 1818, 1818, 1818, 1818, 1818, 
+        2048, 1138, 1138, 1138, 1138, 1138, 2277, 2957, 2048, 2048, 2048, 
+        2048, 2048, 2048, 1593, 2048, 2277, 2277, 2277, 2277, 1818, 1818, 
+        1593, 1363, 3186, 2334, 2334, 2048, 3407, 1138, 901, 1425, 1425, 
+        1363, 1363, 1363, 901, 1433, 1363, 1363, 2048, 1363, 1363, 1024, 
+        3059, 2048, 2048, 2048, 1638, 1363, 2334, 2048, 1363, 1138, 2048, 
+        4096, 4096, 2048, 2334, 1593, 1593, 2277, 2048, 2277, 2048, 2048, 
+        684, 2048, 1363, 2334, 2048, 2048, 1363, 1363, 1363, 1363, 2334, 
+        2482, 1363, 2482, 2359, 2334, 2048, 2048, 1363, 2048, 3072, 3072, 
+        1228, 1089, 1228, 2048, 1363, 1363, 3411, 1024, 1024, 4096, 2334, 
+        2334, 2048, 2048, 2273, 2048, 2048, 2048, 1363, 1363, 1363, 1138, 
+        3059, 1363, 2048, 1363, 2048, 2048, 1138, 1024, 2048, 2048, 3072, 
+        1228, 1363, 4096, 2048, 1228, 2048, 2048, 2048, 
+    }},
+    {"Helvetica", {
+        2732, 2732, 2957, 2957, 2732, 2502, 3186, 2957, 1138, 2048, 2732, 
+        2277, 3411, 2957, 3186, 2732, 3186, 2957, 2732, 2502, 2957, 2732, 
+        3866, 2732, 2732, 2502, 2277, 2277, 2048, 2277, 2277, 1138, 2277, 
+        2277, 909, 909, 2048, 909, 3411, 2277, 2277, 2277, 2277, 1363, 
+        2048, 1138, 2277, 2048, 2957, 2048, 2048, 2048, 4096, 2732, 2732, 
+        2732, 2732, 2732, 2732, 2957, 2732, 2732, 2732, 2732, 2957, 1138, 
+        1138, 1138, 1138, 2277, 2957, 4096, 3186, 3186, 3186, 3186, 3186, 
+        3186, 2732, 2727, 2957, 2957, 2957, 2957, 2727, 2732, 2502, 3641, 
+        2277, 2277, 2277, 2277, 2277, 2277, 2048, 2277, 2277, 2277, 2277, 
+        2277, 1138, 1138, 1138, 1138, 909, 2277, 3866, 2277, 2277, 2277, 
+        2277, 2502, 2277, 2048, 2273, 2277, 2277, 2277, 2277, 2048, 2048, 
+        2048, 1363, 2732, 1921, 2392, 1593, 4157, 1138, 1064, 1368, 1368, 
+        1138, 1138, 1363, 1064, 1433, 1363, 1363, 2277, 1363, 1138, 1138, 
+        3018, 2277, 2277, 2277, 2482, 1363, 2392, 2277, 1363, 1138, 2277, 
+        4096, 4096, 2277, 2392, 1138, 1363, 2048, 2277, 2048, 2277, 2277, 
+        684, 2502, 1363, 2392, 2277, 2277, 1363, 1363, 1363, 1363, 2392, 
+        2392, 1363, 2392, 2277, 2392, 2277, 2277, 1363, 2277, 3559, 3559, 
+        1437, 1515, 1495, 2199, 1363, 1363, 3641, 1138, 1138, 4096, 2392, 
+        2392, 2277, 2502, 1454, 1363, 1363, 1363, 909, 905, 909, 782, 3018, 
+        1363, 2277, 1138, 2277, 2277, 1138, 1138, 2277, 2277, 3559, 1437, 
+        1363, 4096, 2277, 1437, 2277, 2277, 2277, 
+    }},
+    {"Helvetica-Oblique", {
+        2732, 2732, 2957, 2957, 2732, 2502, 3186, 2957, 1138, 2048, 2732, 
+        2277, 3411, 2957, 3186, 2732, 3186, 2957, 2732, 2502, 2957, 2732, 
+        3866, 2732, 2732, 2502, 2277, 2277, 2048, 2277, 2277, 1138, 2277, 
+        2277, 909, 909, 2048, 909, 3411, 2277, 2277, 2277, 2277, 1363, 
+        2048, 1138, 2277, 2048, 2957, 2048, 2048, 2048, 4096, 2732, 2732, 
+        2732, 2732, 2732, 2732, 2957, 2732, 2732, 2732, 2732, 2957, 1138, 
+        1138, 1138, 1138, 2277, 2957, 4096, 3186, 3186, 3186, 3186, 3186, 
+        3186, 2732, 2732, 2957, 2957, 2957, 2957, 2732, 2732, 2502, 3641, 
+        2277, 2277, 2277, 2277, 2277, 2277, 2048, 2277, 2277, 2277, 2277, 
+        2277, 1138, 1138, 1138, 1138, 909, 2277, 3866, 2277, 2277, 2277, 
+        2277, 2502, 2277, 2048, 2277, 2277, 2277, 2277, 2277, 2048, 2048, 
+        2048, 1363, 2732, 1921, 2392, 1593, 4157, 1138, 1064, 1368, 1368, 
+        1138, 1138, 1363, 1064, 1433, 1363, 1363, 2277, 1363, 1138, 1138, 
+        3018, 2277, 2277, 2277, 2482, 1363, 2392, 2277, 1363, 1138, 2277, 
+        4096, 4096, 2277, 2392, 1138, 1363, 2048, 2277, 2048, 2277, 2277, 
+        684, 2502, 1363, 2392, 2277, 2277, 1363, 1363, 1363, 1363, 2392, 
+        2392, 1363, 2392, 2277, 2392, 2277, 2277, 1363, 2277, 3878, 3878, 
+        1597, 1515, 1495, 2199, 1363, 1363, 3641, 1138, 1138, 4096, 2392, 
+        2392, 2277, 2502, 1454, 1363, 1363, 1363, 909, 909, 909, 782, 3018, 
+        1363, 2277, 1138, 2277, 2277, 1138, 1138, 2277, 2277, 3878, 1597, 
+        1363, 4096, 2277, 1597, 2277, 2277, 2277, 
+    }},
+    {"Helvetica-Bold", {
+        2957, 2957, 2957, 2957, 2732, 2502, 3186, 2957, 1138, 2277, 2957, 
+        2502, 3411, 2957, 3186, 2732, 3186, 2957, 2732, 2502, 2957, 2732, 
+        3866, 2732, 2732, 2502, 2277, 2502, 2277, 2502, 2277, 1363, 2502, 
+        2502, 1138, 1138, 2277, 1138, 3641, 2502, 2502, 2502, 2502, 1593, 
+        2277, 1363, 2502, 2277, 3186, 2277, 2277, 2048, 4096, 2957, 2957, 
+        2957, 2957, 2957, 2957, 2957, 2732, 2732, 2732, 2732, 2957, 1138, 
+        1138, 1138, 1138, 2502, 2957, 4096, 3186, 3186, 3186, 3186, 3186, 
+        3186, 2732, 2732, 2957, 2957, 2957, 2957, 2732, 2732, 2502, 3641, 
+        2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 
+        2502, 1138, 1138, 1138, 1138, 1138, 2502, 3866, 2502, 2502, 2502, 
+        2502, 2502, 2502, 2277, 2502, 2502, 2502, 2502, 2502, 2277, 2277, 
+        2048, 1363, 2957, 2392, 2392, 1593, 3993, 1138, 1146, 1593, 1593, 
+        1363, 1363, 1363, 1146, 1433, 1363, 1363, 2277, 1363, 1363, 1138, 
+        3018, 2277, 2277, 2277, 2482, 1363, 2392, 2277, 1363, 1138, 2277, 
+        4096, 4096, 2277, 2392, 1363, 1363, 2502, 2277, 2502, 2277, 2277, 
+        684, 2502, 1363, 2392, 2277, 2277, 1363, 1363, 1363, 1363, 2392, 
+        2392, 1363, 2392, 2502, 2392, 2277, 2277, 1363, 2277, 3559, 3559, 
+        1437, 1515, 1495, 2277, 1363, 1363, 3641, 1138, 1138, 4096, 2392, 
+        2392, 2502, 2502, 1941, 2048, 2048, 2048, 1138, 1138, 1138, 974, 
+        3018, 1363, 2277, 1363, 2277, 2277, 1138, 1138, 2277, 2277, 3559, 
+        1437, 1363, 4096, 2277, 1437, 2277, 2277, 2277, 
+    }},
+    {"Helvetica-BoldOblique", {
+        2957, 2957, 2957, 2957, 2732, 2502, 3186, 2957, 1138, 2277, 2957, 
+        2502, 3411, 2957, 3186, 2732, 3186, 2957, 2732, 2502, 2957, 2732, 
+        3866, 2732, 2732, 2502, 2277, 2502, 2277, 2502, 2277, 1363, 2502, 
+        2502, 1138, 1138, 2277, 1138, 3641, 2502, 2502, 2502, 2502, 1593, 
+        2277, 1363, 2502, 2277, 3186, 2277, 2277, 2048, 4096, 2957, 2957, 
+        2957, 2957, 2957, 2957, 2957, 2732, 2732, 2732, 2732, 2957, 1138, 
+        1138, 1138, 1138, 2502, 2957, 4096, 3186, 3186, 3186, 3186, 3186, 
+        3186, 2732, 2732, 2957, 2957, 2957, 2957, 2732, 2732, 2502, 3641, 
+        2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 2277, 
+        2502, 1138, 1138, 1138, 1138, 1138, 2502, 3866, 2502, 2502, 2502, 
+        2502, 2502, 2502, 2277, 2502, 2502, 2502, 2502, 2502, 2277, 2277, 
+        2048, 1363, 2957, 2392, 2392, 1593, 3993, 1138, 1146, 1593, 1593, 
+        1363, 1363, 1363, 1146, 1433, 1363, 1363, 2277, 1363, 1363, 1138, 
+        3018, 2277, 2277, 2277, 2482, 1363, 2392, 2277, 1363, 1138, 2277, 
+        4096, 4096, 2277, 2392, 1363, 1363, 2502, 2277, 2502, 2277, 2277, 
+        684, 2502, 1363, 2392, 2277, 2277, 1363, 1363, 1363, 1363, 2392, 
+        2392, 1363, 2392, 2502, 2392, 2277, 2277, 1363, 2277, 4321, 4321, 
+        1818, 1515, 1495, 2277, 1363, 1363, 3641, 1138, 1138, 4096, 2392, 
+        2392, 2502, 2502, 1941, 2048, 2048, 2048, 1138, 1138, 1138, 974, 
+        3018, 1363, 2277, 1363, 2277, 2277, 1138, 1138, 2277, 2277, 4321, 
+        1818, 1363, 4096, 2277, 1818, 2277, 2277, 2277, 
+    }},
+    {"Courier", {
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+    }},
+    {"Courier-Oblique", {
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+    }},
+    {"Courier-Bold", {
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+    }},
+    {"Courier-BoldOblique", {
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+        2457, 2457, 2457, 2457, 2457, 2457, 2457, 2457, 
+    }},
+};
+
+const int *ps_std_font_widths(char const *fontname)
+{
+    int i;
+
+    for (i = 0; i < (int)lenof(ps_std_fonts[i].widths); i++)
+       if (!strcmp(ps_std_fonts[i].name, fontname))
+           return ps_std_fonts[i].widths;
+
+    return NULL;
+}