/**********************************************************************
** $Id$
**
** Implementation of QPSPrinter class
**
** Created : 941003
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses may use this file in accordance with the Qt Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

// Get the system specific includes and defines
#include "qplatformdefs.h"

#include "qpsprinter_p.h"

#ifndef QT_NO_PRINTER

#undef Q_PRINTER_USE_TYPE42

#include "qpainter.h"
#include "qapplication.h"
#include "qpaintdevicemetrics.h"
#include "qimage.h"
#include "qdatetime.h"
#include "qstring.h"
#include "qdict.h"
#include "qmemarray.h"
#include "qregexp.h"
#include "qfile.h"
#include "qbuffer.h"
#include "qintdict.h"
#include "qtextcodec.h"
#include "qsettings.h"
#include "qmap.h"
#include "qfontdatabase.h"

#include <ctype.h>
#if defined(_OS_WIN32_)
#include <io.h>
#else
#include <unistd.h>
#include <stdlib.h>
#endif

#ifdef Q_WS_X11
#include "qt_x11.h"
#ifdef None
#undef None
#endif
#ifdef GrayScale
#undef GrayScale
#endif
#endif

#ifdef Q_WS_X11
#include "qfontdata_p.h"
extern bool qt_has_xft;
#endif

static bool qt_gen_epsf = FALSE;

void qt_generate_epsf( bool b )
{
    qt_gen_epsf = b;
}

// Note: this is comment-stripped and word-wrapped later.
//
// #### improve build process and qembed the result, saving RAM and ROM
//

static const char * const ps_header[] = {
// basic definitions
"/d  /def load def",
"/D  {bind d} bind d",
"/d2 {dup dup} D",
"/B  {0 d2} D",
"/W  {255 d2} D",
"/ED {exch d} D",
"/D0 {0 ED} D",
"/LT {lineto} D",
"/MT {moveto} D",
"/S  {stroke} D",
"/F  {setfont} D",
"/SW {setlinewidth} D",
"/CP {closepath} D",
"/RL {rlineto} D",
"/NP {newpath} D",
"/CM {currentmatrix} D",
"/SM {setmatrix} D",
"/TR {translate} D",
"/SC {aload pop setrgbcolor} D",

"/BSt 0 d",                             // brush style
"/LWi 1 d",                             // line width
"/PSt 1 d",                             // pen style
"/Cx  0 d",                             // current x position
"/Cy  0 d",                             // current y position
"/WFi false d",                 // winding fill
"/OMo false d",                 // opaque mode (not transparent)
"",
"/BCol  [ 1 1 1 ] d",                   // brush color
"/PCol  [ 0 0 0 ] d",                   // pen color
"/BkCol [ 1 1 1 ] d",                   // background color

"/nS 0 d",                              // number of saved painter states

// LArr for the Pen styles is defined in emitHeader because of scaling

// Returns the line pattern (from pen style PSt).
//
// Argument:
//   bool pattern
//   true : draw pattern
//   false: fill pattern
"/GPS {",
"  PSt 1 ge PSt 5 le and",                      // valid pen pattern?
"    { { LArr PSt 1 sub 2 mul get }",           // draw pattern
"      { LArr PSt 2 mul 1 sub get } ifelse",    // opaque pattern
"    }",
"    { [] } ifelse",                            // out of range => solid line
"} D",

"/QS {",                                // stroke command
"    PSt 0 ne",                         // != NO_PEN
"    { LWi SW",                         // set line width
"      gsave",
"      PCol SC",                        // set pen color
"      true GPS 0 setdash S",           // draw line pattern
"      OMo PSt 1 ne and",               // opaque mode and not solid line?
"      { grestore BkCol SC",
"       false GPS dup 0 get setdash S", // fill in opaque pattern
"      }",
"      { grestore } ifelse",
"    } if",
"} D",

"/BDArr[",                              // Brush dense patterns:
"    0.06 0.12 0.37 0.50 0.63 0.88 0.94",
"] d",

// read 28 bits and leave them on tos
"/r28 {",
"  ", // skip past whitespace and read one character
"  { currentfile read pop ",
"    dup 32 gt { exit } if",
"    pop",
"  } loop",
"  ", // read three more
"  3 {",
"    currentfile read pop",
"  } repeat",
"  ", // make an accumulator
"  0",
"  ", // for each character, shift the accumulator and add in the character
"  4 {",
"    7 bitshift exch",
"    dup 128 gt { 84 sub } if 42 sub 127 and",
"    add",
"  } repeat",
"} D",

// read some bits and leave them on tos
"/rA 0 d ", // accumulator
"/rL 0 d ", // bits left

// takes number of bits, leaves number
"/rB {",
"  rL 0 eq {",
"    ", // if we have nothing, let's get something
"    /rA r28 d",
"    /rL 28 d",
"  } if",
"  dup rL gt {",
"    ", // if we don't have enough, take what we have and get more
"    rA exch rL sub rL exch",
"    /rA 0 d /rL 0 d",
"    rB exch bitshift add",
"  } {",
"    ", // else take some of what we have
"    dup rA 16#fffffff 3 -1 roll bitshift not and exch",
"    ", // ... and update rL and rA
"    dup rL exch sub /rL ED",
"    neg rA exch bitshift /rA ED",
"  } ifelse",
"} D",

// uncompresses from currentfile until the string on the stack is full;
// leaves the string there.  assumes that nothing could conceivably go
// wrong.
"/rC {",
"  /rL 0 d",
"  0",
"  {", // string pos
"    dup 2 index length ge { exit } if",
"    1 rB",
"    1 eq {", // compressed
"      3 rB", // string pos bits
"      dup 4 ge {",
"        dup rB", // string pos bits extra
"        1 index 5 ge {",
"          1 index 6 ge {",
"            1 index 7 ge {",
"              64 add",
"            } if",
"            32 add",
"          } if",
"          16 add",
"        } if",
"        4 add",
"       exch pop",
"      } if",
"      1 add 3 mul",
"      ", // string pos length
"      exch 10 rB 1 add 3 mul",
"      ", // string length pos dist
"      {",
"       dup 3 index lt {",
"         dup",
"       } {",
"         2 index",
"       } ifelse", // string length pos dist length-this-time
"       4 index 3 index 3 index sub 2 index getinterval",
"       5 index 4 index 3 -1 roll putinterval",
"       dup 4 -1 roll add 3 1 roll",
"       4 -1 roll exch sub ",
"       dup 0 eq { exit } if",
"       3 1 roll",
"      } loop", // string pos dist length
"      pop pop",
"    } {", // uncompressed
"      3 rB 1 add 3 mul",
"      {",
"       2 copy 8 rB put 1 add",
"      } repeat",
"    } ifelse",
"  } loop",
"  pop",
"} D",

// uncompresses grayscale from currentfile until the string on the
// stack is full; leaves the string there.  assumes that nothing could
// conceivably go wrong.
"/rG {",
"  /rL 0 d",
"  0",
"  {", // string pos
"    dup 2 index length ge { exit } if",
"    1 rB",
"    1 eq {", // compressed
"      3 rB", // string pos bits
"      dup 4 ge {",
"        dup rB", // string pos bits extra
"        1 index 5 ge {",
"          1 index 6 ge {",
"            1 index 7 ge {",
"              64 add",
"            } if",
"            32 add",
"          } if",
"          16 add",
"        } if",
"        4 add",
"       exch pop",
"      } if",
"      1 add",
"      ", // string pos length
"      exch 10 rB 1 add",
"      ", // string length pos dist
"      {",
"       dup 3 index lt {",
"         dup",
"       } {",
"         2 index",
"       } ifelse", // string length pos dist length-this-time
"       4 index 3 index 3 index sub 2 index getinterval",
"       5 index 4 index 3 -1 roll putinterval",
"       dup 4 -1 roll add 3 1 roll",
"       4 -1 roll exch sub ",
"       dup 0 eq { exit } if",
"       3 1 roll",
"      } loop", // string pos dist length
"      pop pop",
"    } {", // uncompressed
"      3 rB 1 add",
"      {",
"       2 copy 8 rB put 1 add",
"      } repeat",
"    } ifelse",
"  } loop",
"  pop",
"} D",

"/sl D0", // ## What is this supposed to do?
"/QCIgray D0 /QCIcolor D0 /QCIindex D0",
"/QCI {", // as colorimage but without the last two arguments
"  /colorimage where {",
"    pop",
"    false 3 colorimage",
"  }{",  // the hard way, based on PD code by John Walker <kelvin@autodesk.com>
"    exec /QCIcolor ED",
"    /QCIgray QCIcolor length 3 idiv string d",
"    0 1 QCIcolor length 3 idiv 1 sub",
"    { /QCIindex ED",
"      /x QCIindex 3 mul d",
"      QCIgray QCIindex",
"      QCIcolor x       get 0.30 mul",
"      QCIcolor x 1 add get 0.59 mul",
"      QCIcolor x 2 add get 0.11 mul",
"      add add cvi",
"      put",
"    } for",
"    QCIgray image",
"  } ifelse",
"} D",

"/defM matrix d",

"/BF {",                                // brush fill
"   gsave",
"   BSt 1 eq",                          // solid brush?
"   {",
"     BCol SC"
"     WFi { fill } { eofill } ifelse",
"   } if",
"   BSt 2 ge BSt 8 le and",             // dense pattern?
"   {",
"     BDArr BSt 2 sub get setgray fill"
"   } if",
"   BSt 9 ge BSt 14 le and",            // brush pattern?
"   {",
"     WFi { clip } { eoclip } ifelse",
"     defM SM",
"     pathbbox",                        // left upper right lower
"     3 index 3 index translate",
"     4 2 roll",                        // right lower left upper
"     3 2 roll",                        // right left upper lower
"     exch",                            // left right lower upper
"     sub /h ED",
"     sub /w ED",
"     OMo {",
"         NP",
"         0 0 MT",
"         0 h RL",
"         w 0 RL",
"         0 h neg RL",
"         CP",
"         BkCol SC",
"         fill",
"     } if",
"     BCol SC",
"     0.3 SW",
"     NP",
"     BSt 9 eq BSt 11 eq or",           // horiz or cross pattern
"     { 0 4 h",
"       { dup 0 exch MT w exch LT } for",
"     } if",
"     BSt 10 eq BSt 11 eq or",          // vert or cross pattern
"     { 0 4 w",
"       { dup 0 MT h LT } for",
"     } if",
"     BSt 12 eq BSt 14 eq or",          // F-diag or diag cross
"     { w h gt",
"       { 0 6 w h add",
"         { dup 0 MT h sub h LT } for"
"       } { 0 6 w h add",
"         { dup 0 exch MT w sub w exch LT } for"
"       } ifelse",
"     } if",
"     BSt 13 eq BSt 14 eq or",          // B-diag or diag cross
"     { w h gt",
"       { 0 6 w h add",
"         { dup h MT h sub 0 LT } for",
"       } { 0 6 w h add",
"         { dup w exch MT w sub 0 exch LT } for"
"       } ifelse",
"     } if",
"     S",
"   } if",
"   BSt 24 eq",                         // CustomPattern
"     ",
"   {",
"   } if",
"   grestore",
"} D",

// for arc
"/mat matrix d",
"/ang1 D0 /ang2 D0",
"/w D0 /h D0",
"/x D0 /y D0",

"/ARC {",                               // Generic ARC function [ X Y W H ang1 ang2 ]
"    /ang2 ED /ang1 ED /h ED /w ED /y ED /x ED",
"    mat CM pop",
"    x w 2 div add y h 2 div add TR",
"    1 h w div neg scale",
"    ang2 0 ge",
"    {0 0 w 2 div ang1 ang1 ang2 add arc }",
"    {0 0 w 2 div ang1 ang1 ang2 add arcn} ifelse",
"    mat SM",
"} D",

"/C D0",

"/P {",                                 // PdcDrawPoint [x y]
"    NP",
"    MT",
"    0.5 0.5 rmoveto",
"    0  -1 RL",
"    -1 0 RL",
"    0  1 RL",
"    CP",
"    PCol SC",
"    fill",
"} D",

"/M {",                                 // PdcMoveTo [x y]
"    /Cy ED /Cx ED",
"} D",

"/L {",                                 // PdcLineTo [x y]
"    NP",
"    Cx Cy MT",
"    /Cy ED /Cx ED",
"    Cx Cy LT",
"    QS",
"} D",

"/DL {",                                // PdcDrawLine [x1 y1 x0 y0]
"    NP",
"    MT",
"    LT",
"    QS",
"} D",

"/HL {",                                // PdcDrawLine [x1 y x0]
"    1 index DL",
"} D",

"/VL {",                                // PdcDrawLine [x y1 y0]
"    2 index exch DL",
"} D",

"/R {",                                 // PdcDrawRect [x y w h]
"    /h ED /w ED /y ED /x ED",
"    NP",
"    x y MT",
"    0 h RL",
"    w 0 RL",
"    0 h neg RL",
"    CP",
"    BF",
"    QS",
"} D",

"/ACR {",                                       // add clip rect
"    /h ED /w ED /y ED /x ED",
"    x y MT",
"    0 h RL",
"    w 0 RL",
"    0 h neg RL",
"    CP",
"} D",

"/CLSTART {",                           // clipping start
"    /clipTmp matrix CM d",             // save current matrix
"    defM SM",                          // Page default matrix
"    NP",
"} D",

"/CLEND {",                             // clipping end
"    clip",
"    NP",
"    clipTmp SM",                       // restore the current matrix
"} D",

"/CLO {",                               // clipping off
"    grestore",                         // restore top of page state
"    gsave",                            // save it back again
"    defM SM",                          // set coordsys (defensive progr.)
"} D",

"/xr D0 /yr D0",
"/rx D0 /ry D0 /rx2 D0 /ry2 D0",

"/RR {",                                // PdcDrawRoundRect [x y w h xr yr]
"    /yr ED /xr ED /h ED /w ED /y ED /x ED",
"    xr 0 le yr 0 le or",
"    {x y w h R}",                  // Do rect if one of rounding values is less than 0.
"    {xr 100 ge yr 100 ge or",
"       {x y w h E}",                    // Do ellipse if both rounding values are larger than 100
"       {",
"        /rx xr w mul 200 div d",
"        /ry yr h mul 200 div d",
"        /rx2 rx 2 mul d",
"        /ry2 ry 2 mul d",
"        NP",
"        x rx add y MT",
"        x               y               rx2 ry2 180 -90",
"        x               y h add ry2 sub rx2 ry2 270 -90",
"        x w add rx2 sub y h add ry2 sub rx2 ry2 0   -90",
"        x w add rx2 sub y               rx2 ry2 90  -90",
"        ARC ARC ARC ARC",
"        CP",
"        BF",
"        QS",
"       } ifelse",
"    } ifelse",
"} D",

"/E {",                         // PdcDrawEllipse [x y w h]
"    /h ED /w ED /y ED /x ED",
"    mat CM pop",
"    x w 2 div add y h 2 div add translate",
"    1 h w div scale",
"    NP",
"    0 0 w 2 div 0 360 arc",
"    mat SM",
"    BF",
"    QS",
"} D",

"/A {",                         // PdcDrawArc [x y w h ang1 ang2]
"    16 div exch 16 div exch",
"    NP",
"    ARC",
"    QS",
"} D",

"/PIE {",                               // PdcDrawPie [x y w h ang1 ang2]
"    /ang2 ED /ang1 ED /h ED /w ED /y ED /x ED",
"    NP",
"    x w 2 div add y h 2 div add MT",
"    x y w h ang1 16 div ang2 16 div ARC",
"    CP",
"    BF",
"    QS",
"} D",

"/CH {",                                // PdcDrawChord [x y w h ang1 ang2]
"    16 div exch 16 div exch",
"    NP",
"    ARC",
"    CP",
"    BF",
"    QS",
"} D",

"/BZ {",                                // PdcDrawCubicBezier [4 points]
"    curveto",
"    QS",
"} D",

"/CRGB {",                              // Compute RGB [R G B] => R/255 G/255 B/255
"    255 div 3 1 roll",
"    255 div 3 1 roll",
"    255 div 3 1 roll",
"} D",

"/SV {",                                // Save painter state
"    BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol",
"    /nS nS 1 add d",
"    gsave",
"} D",

"/RS {",                                // Restore painter state
"    nS 0 gt",
"    { grestore",
"      /BkCol ED /PCol ED /BCol ED /OMo ED /WFi ED",
"      /Cy ED /Cx ED /PSt ED /LWi ED /BSt ED",
"      /nS nS 1 sub d",
"    } if",
"} D",

"/BC {",                                // PdcSetBkColor [R G B]
"    CRGB",
"    BkCol astore pop",
"} D",

"/BR {",                                // PdcSetBrush [style R G B]
"    CRGB",
"    BCol astore pop",
"    /BSt ED",
"} D",

"/WB {",                                // set white solid brush
"    1 W BR",
"} D",

"/NB {",                                // set nobrush
"    0 B BR",
"} D",

"/PE {",                                // PdcSetPen [style width R G B Cap Join]
"    setlinejoin setlinecap"
"    CRGB",
"    PCol astore pop",
"    /LWi ED",
"    /PSt ED",
"    LWi 0 eq { 0.25 /LWi ED } if", // ### 3.0 remove this line
"} D",

"/P1 {",                                // PdcSetPen [R G B]
"    1 0 5 2 roll 0 0 PE",
"} D",

"/ST {",                                // SET TRANSFORM [matrix]
"    defM setmatrix",
"    concat",
"} D",

// this function tries to find a suitable postscript font. We try quite hard not to get courier for a
// proportional font. The following takes an array of fonts. The algorithm will take the first one that
// gives a match (defined as not resulting in a courier font).
// each entry in the table is an array of the form [ /Fontname x-stretch slant ]
// x-strtch can be used to stretch/squeeze the font in x direction. This gives better results when
// eg substituting helvetica for arial
// slant is an optional slant. 0 is non slanted, 0.2 is a typical value for a syntetic oblique.
// encoding can be either an encoding vector of false if the default font encoding is requested.
"/qtfindfont {",                        // fntlist
"  true exch true exch",                          // push a dummy on the stack,
"  {",                          // so the loop over the array will leave a font in any case when exiting.
"    exch pop exch pop",                 // (dummy | oldfont) (dummy | fontdict) fontarray
"    dup 0 get dup findfont",           // get the fontname from the array and load it
"    dup /FontName get",                // see if the font exists
"    3 -1 roll eq {",                   // see if fontname and the one provided are equal
"      exit",
"    } if",
"  } forall",
"  exch",                               // font fontarray
"} D",

"/qtdefinefont {",                      // newname encoding font fontarray      defines a postscript font
"  dup",
"  1 get /fxscale exch def",            // define scale, sland and encoding
"  2 get /fslant exch def",
"  exch /fencoding exch def",
"  [ fxscale 0 fslant 1 0 0 ] makefont",        // transform font accordingly
"  fencoding false eq {",               // check if we have an encoding and use it if available
"  } {",
"    dup maxlength dict begin",         // copy font
"    {",
"      1 index /FID ne",                        // don't copy FID, as it's not allowed in PS Level 1
"      {def}{pop pop}ifelse",
"    } forall",
"    /Encoding fencoding def",          // replace encoding
"    currentdict end",
"  } ifelse",
"  definefont pop",
"} D",

// the next three commands are for defining fonts. The first one
// tries to find the most suitable printer font out of a fontlist.
"/MF {",                                // newname encoding fontlist
"  qtfindfont qtdefinefont",
"} D",

// used for asian fonts. We don't need an encoding, but we can simulate italic.
"/MSF {",                               // newname slant fontname
"  findfont exch",                      // newname font slant
"  /slant exch d",                      // newname font
"  [ 1 0 slant 1 0 0 ] makefont",
"  definefont pop",
"} D",

// an embedded font. This is used for the base fonts of the composite font used later on.
"/MFEmb {",                             // newname encoding fontname
"  findfont dup length dict begin",
"  {",
"    1 index /FID ne",
"    {d}{pop pop}ifelse",
"  } forall",
"  /Encoding ED currentdict end",
"  definefont pop",
"} D",

"/DF {",                                // newname pointsize fontmame
"  findfont",
"  /FONTSIZE 3 -1 roll d [ FONTSIZE 0 0 FONTSIZE -1 mul 0 0 ] makefont",
"  d",
"} D",

"/ty 0 d",
"/Y {",
"    /ty ED",
"} D",

"/Tl {", // draw underline/strikeout line: () w x y lw ->Tl-> () w x
"    gsave setlinewidth",
"    PCol SC",
"    NP 1 index exch MT",
"    1 index 0 rlineto stroke",
"    grestore",
"} D",

"/T {",                                 // PdcDrawText2 [string fm.width x]
"    PCol SC", // really need to kill these SCs
"    ty MT",
"    1 index", // string cwidth string
"    dup length 2 div exch", // string cwidth length string     !have to divide by 2 since we use unicode!
"    stringwidth pop", // string cwidth length pwidth
"    3 -1 roll", // string length pwidth cwidth
"    exch sub exch div", // string extraperchar
"    exch 0 exch", // extraperchar 0 string
"    ashow",
"} D",

"/QI {",
"    /C save d",
"    pageinit",
"    /Cx  0 d",                         // reset current x position
"    /Cy  0 d",                         // reset current y position
"    /OMo false d",
"} D",

"/QP {",                                // show page
"    C restore",
"    showpage",
"} D",

// merges one key value pair into the page device dict
"/SPD {", // key value
"    /setpagedevice where {"
"        1 dict dup begin 3 1 roll def end"
"        setpagedevice"
"    } { pop pop } ifelse"
"} D",

0 };



// the next table is derived from a list provided by Adobe on its web
// server: http://partners.adobe.com/asn/developer/typeforum/glyphlist.txt

// the start of the header comment:
//
// Name:          Adobe Glyph List
// Table version: 1.2
// Date:          22 Oct 1998
//
// Description:
//
//   The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
//   names, and should be used only as described in the document "Unicode and
//   Glyph Names," at
//   http://www.adobe.com/asn/developer/typeforum/unicodegn.html .
//
// the list contains glyphs in the private use area of unicode. These should get removed when regenerating the glyphlist.
// also 0 shout be mapped to .notdef
static const struct {
    Q_UINT16 u;
    const char * g;
} unicodetoglyph[] = {
    // grep '^[0-9A-F][0-9A-F][0-9A-F][0-9A-F];' < /tmp/glyphlist.txt | sed -e 's/;/, "/' -e 's-;-" },  // -' -e 's/^/    { 0x/' | sort
    { 0x0000, ".notdef" },
    { 0x0020, "space" },  // SPACE
    { 0x0021, "exclam" },  // EXCLAMATION MARK
    { 0x0022, "quotedbl" },  // QUOTATION MARK
    { 0x0023, "numbersign" },  // NUMBER SIGN
    { 0x0024, "dollar" },  // DOLLAR SIGN
    { 0x0025, "percent" },  // PERCENT SIGN
    { 0x0026, "ampersand" },  // AMPERSAND
    { 0x0027, "quotesingle" },  // APOSTROPHE
    { 0x0028, "parenleft" },  // LEFT PARENTHESIS
    { 0x0029, "parenright" },  // RIGHT PARENTHESIS
    { 0x002A, "asterisk" },  // ASTERISK
    { 0x002B, "plus" },  // PLUS SIGN
    { 0x002C, "comma" },  // COMMA
    { 0x002D, "hyphen" },  // HYPHEN-MINUS
    { 0x002E, "period" },  // FULL STOP
    { 0x002F, "slash" },  // SOLIDUS
    { 0x0030, "zero" },  // DIGIT ZERO
    { 0x0031, "one" },  // DIGIT ONE
    { 0x0032, "two" },  // DIGIT TWO
    { 0x0033, "three" },  // DIGIT THREE
    { 0x0034, "four" },  // DIGIT FOUR
    { 0x0035, "five" },  // DIGIT FIVE
    { 0x0036, "six" },  // DIGIT SIX
    { 0x0037, "seven" },  // DIGIT SEVEN
    { 0x0038, "eight" },  // DIGIT EIGHT
    { 0x0039, "nine" },  // DIGIT NINE
    { 0x003A, "colon" },  // COLON
    { 0x003B, "semicolon" },  // SEMICOLON
    { 0x003C, "less" },  // LESS-THAN SIGN
    { 0x003D, "equal" },  // EQUALS SIGN
    { 0x003E, "greater" },  // GREATER-THAN SIGN
    { 0x003F, "question" },  // QUESTION MARK
    { 0x0040, "at" },  // COMMERCIAL AT
    { 0x0041, "A" },  // LATIN CAPITAL LETTER A
    { 0x0042, "B" },  // LATIN CAPITAL LETTER B
    { 0x0043, "C" },  // LATIN CAPITAL LETTER C
    { 0x0044, "D" },  // LATIN CAPITAL LETTER D
    { 0x0045, "E" },  // LATIN CAPITAL LETTER E
    { 0x0046, "F" },  // LATIN CAPITAL LETTER F
    { 0x0047, "G" },  // LATIN CAPITAL LETTER G
    { 0x0048, "H" },  // LATIN CAPITAL LETTER H
    { 0x0049, "I" },  // LATIN CAPITAL LETTER I
    { 0x004A, "J" },  // LATIN CAPITAL LETTER J
    { 0x004B, "K" },  // LATIN CAPITAL LETTER K
    { 0x004C, "L" },  // LATIN CAPITAL LETTER L
    { 0x004D, "M" },  // LATIN CAPITAL LETTER M
    { 0x004E, "N" },  // LATIN CAPITAL LETTER N
    { 0x004F, "O" },  // LATIN CAPITAL LETTER O
    { 0x0050, "P" },  // LATIN CAPITAL LETTER P
    { 0x0051, "Q" },  // LATIN CAPITAL LETTER Q
    { 0x0052, "R" },  // LATIN CAPITAL LETTER R
    { 0x0053, "S" },  // LATIN CAPITAL LETTER S
    { 0x0054, "T" },  // LATIN CAPITAL LETTER T
    { 0x0055, "U" },  // LATIN CAPITAL LETTER U
    { 0x0056, "V" },  // LATIN CAPITAL LETTER V
    { 0x0057, "W" },  // LATIN CAPITAL LETTER W
    { 0x0058, "X" },  // LATIN CAPITAL LETTER X
    { 0x0059, "Y" },  // LATIN CAPITAL LETTER Y
    { 0x005A, "Z" },  // LATIN CAPITAL LETTER Z
    { 0x005B, "bracketleft" },  // LEFT SQUARE BRACKET
    { 0x005C, "backslash" },  // REVERSE SOLIDUS
    { 0x005D, "bracketright" },  // RIGHT SQUARE BRACKET
    { 0x005E, "asciicircum" },  // CIRCUMFLEX ACCENT
    { 0x005F, "underscore" },  // LOW LINE
    { 0x0060, "grave" },  // GRAVE ACCENT
    { 0x0061, "a" },  // LATIN SMALL LETTER A
    { 0x0062, "b" },  // LATIN SMALL LETTER B
    { 0x0063, "c" },  // LATIN SMALL LETTER C
    { 0x0064, "d" },  // LATIN SMALL LETTER D
    { 0x0065, "e" },  // LATIN SMALL LETTER E
    { 0x0066, "f" },  // LATIN SMALL LETTER F
    { 0x0067, "g" },  // LATIN SMALL LETTER G
    { 0x0068, "h" },  // LATIN SMALL LETTER H
    { 0x0069, "i" },  // LATIN SMALL LETTER I
    { 0x006A, "j" },  // LATIN SMALL LETTER J
    { 0x006B, "k" },  // LATIN SMALL LETTER K
    { 0x006C, "l" },  // LATIN SMALL LETTER L
    { 0x006D, "m" },  // LATIN SMALL LETTER M
    { 0x006E, "n" },  // LATIN SMALL LETTER N
    { 0x006F, "o" },  // LATIN SMALL LETTER O
    { 0x0070, "p" },  // LATIN SMALL LETTER P
    { 0x0071, "q" },  // LATIN SMALL LETTER Q
    { 0x0072, "r" },  // LATIN SMALL LETTER R
    { 0x0073, "s" },  // LATIN SMALL LETTER S
    { 0x0074, "t" },  // LATIN SMALL LETTER T
    { 0x0075, "u" },  // LATIN SMALL LETTER U
    { 0x0076, "v" },  // LATIN SMALL LETTER V
    { 0x0077, "w" },  // LATIN SMALL LETTER W
    { 0x0078, "x" },  // LATIN SMALL LETTER X
    { 0x0079, "y" },  // LATIN SMALL LETTER Y
    { 0x007A, "z" },  // LATIN SMALL LETTER Z
    { 0x007B, "braceleft" },  // LEFT CURLY BRACKET
    { 0x007C, "bar" },  // VERTICAL LINE
    { 0x007D, "braceright" },  // RIGHT CURLY BRACKET
    { 0x007E, "asciitilde" },  // TILDE
    { 0x00A0, "space" },  // NO-BREAK SPACE;Duplicate
    { 0x00A1, "exclamdown" },  // INVERTED EXCLAMATION MARK
    { 0x00A2, "cent" },  // CENT SIGN
    { 0x00A3, "sterling" },  // POUND SIGN
    { 0x00A4, "currency" },  // CURRENCY SIGN
    { 0x00A5, "yen" },  // YEN SIGN
    { 0x00A6, "brokenbar" },  // BROKEN BAR
    { 0x00A7, "section" },  // SECTION SIGN
    { 0x00A8, "dieresis" },  // DIAERESIS
    { 0x00A9, "copyright" },  // COPYRIGHT SIGN
    { 0x00AA, "ordfeminine" },  // FEMININE ORDINAL INDICATOR
    { 0x00AB, "guillemotleft" },  // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
    { 0x00AC, "logicalnot" },  // NOT SIGN
    { 0x00AD, "hyphen" },  // SOFT HYPHEN;Duplicate
    { 0x00AE, "registered" },  // REGISTERED SIGN
    { 0x00AF, "macron" },  // MACRON
    { 0x00B0, "degree" },  // DEGREE SIGN
    { 0x00B1, "plusminus" },  // PLUS-MINUS SIGN
    { 0x00B2, "twosuperior" },  // SUPERSCRIPT TWO
    { 0x00B3, "threesuperior" },  // SUPERSCRIPT THREE
    { 0x00B4, "acute" },  // ACUTE ACCENT
    { 0x00B5, "mu" },  // MICRO SIGN
    { 0x00B6, "paragraph" },  // PILCROW SIGN
    { 0x00B7, "periodcentered" },  // MIDDLE DOT
    { 0x00B8, "cedilla" },  // CEDILLA
    { 0x00B9, "onesuperior" },  // SUPERSCRIPT ONE
    { 0x00BA, "ordmasculine" },  // MASCULINE ORDINAL INDICATOR
    { 0x00BB, "guillemotright" }, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
    { 0x00BC, "onequarter" },  // VULGAR FRACTION ONE QUARTER
    { 0x00BD, "onehalf" },  // VULGAR FRACTION ONE HALF
    { 0x00BE, "threequarters" },  // VULGAR FRACTION THREE QUARTERS
    { 0x00BF, "questiondown" },  // INVERTED QUESTION MARK
    { 0x00C0, "Agrave" },  // LATIN CAPITAL LETTER A WITH GRAVE
    { 0x00C1, "Aacute" },  // LATIN CAPITAL LETTER A WITH ACUTE
    { 0x00C2, "Acircumflex" },  // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
    { 0x00C3, "Atilde" },  // LATIN CAPITAL LETTER A WITH TILDE
    { 0x00C4, "Adieresis" },  // LATIN CAPITAL LETTER A WITH DIAERESIS
    { 0x00C5, "Aring" },  // LATIN CAPITAL LETTER A WITH RING ABOVE
    { 0x00C6, "AE" },  // LATIN CAPITAL LETTER AE
    { 0x00C7, "Ccedilla" },  // LATIN CAPITAL LETTER C WITH CEDILLA
    { 0x00C8, "Egrave" },  // LATIN CAPITAL LETTER E WITH GRAVE
    { 0x00C9, "Eacute" },  // LATIN CAPITAL LETTER E WITH ACUTE
    { 0x00CA, "Ecircumflex" },  // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
    { 0x00CB, "Edieresis" },  // LATIN CAPITAL LETTER E WITH DIAERESIS
    { 0x00CC, "Igrave" },  // LATIN CAPITAL LETTER I WITH GRAVE
    { 0x00CD, "Iacute" },  // LATIN CAPITAL LETTER I WITH ACUTE
    { 0x00CE, "Icircumflex" },  // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
    { 0x00CF, "Idieresis" },  // LATIN CAPITAL LETTER I WITH DIAERESIS
    { 0x00D0, "Eth" },  // LATIN CAPITAL LETTER ETH
    { 0x00D1, "Ntilde" },  // LATIN CAPITAL LETTER N WITH TILDE
    { 0x00D2, "Ograve" },  // LATIN CAPITAL LETTER O WITH GRAVE
    { 0x00D3, "Oacute" },  // LATIN CAPITAL LETTER O WITH ACUTE
    { 0x00D4, "Ocircumflex" },  // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
    { 0x00D5, "Otilde" },  // LATIN CAPITAL LETTER O WITH TILDE
    { 0x00D6, "Odieresis" },  // LATIN CAPITAL LETTER O WITH DIAERESIS
    { 0x00D7, "multiply" },  // MULTIPLICATION SIGN
    { 0x00D8, "Oslash" },  // LATIN CAPITAL LETTER O WITH STROKE
    { 0x00D9, "Ugrave" },  // LATIN CAPITAL LETTER U WITH GRAVE
    { 0x00DA, "Uacute" },  // LATIN CAPITAL LETTER U WITH ACUTE
    { 0x00DB, "Ucircumflex" },  // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
    { 0x00DC, "Udieresis" },  // LATIN CAPITAL LETTER U WITH DIAERESIS
    { 0x00DD, "Yacute" },  // LATIN CAPITAL LETTER Y WITH ACUTE
    { 0x00DE, "Thorn" },  // LATIN CAPITAL LETTER THORN
    { 0x00DF, "germandbls" },  // LATIN SMALL LETTER SHARP S
    { 0x00E0, "agrave" },  // LATIN SMALL LETTER A WITH GRAVE
    { 0x00E1, "aacute" },  // LATIN SMALL LETTER A WITH ACUTE
    { 0x00E2, "acircumflex" },  // LATIN SMALL LETTER A WITH CIRCUMFLEX
    { 0x00E3, "atilde" },  // LATIN SMALL LETTER A WITH TILDE
    { 0x00E4, "adieresis" },  // LATIN SMALL LETTER A WITH DIAERESIS
    { 0x00E5, "aring" },  // LATIN SMALL LETTER A WITH RING ABOVE
    { 0x00E6, "ae" },  // LATIN SMALL LETTER AE
    { 0x00E7, "ccedilla" },  // LATIN SMALL LETTER C WITH CEDILLA
    { 0x00E8, "egrave" },  // LATIN SMALL LETTER E WITH GRAVE
    { 0x00E9, "eacute" },  // LATIN SMALL LETTER E WITH ACUTE
    { 0x00EA, "ecircumflex" },  // LATIN SMALL LETTER E WITH CIRCUMFLEX
    { 0x00EB, "edieresis" },  // LATIN SMALL LETTER E WITH DIAERESIS
    { 0x00EC, "igrave" },  // LATIN SMALL LETTER I WITH GRAVE
    { 0x00ED, "iacute" },  // LATIN SMALL LETTER I WITH ACUTE
    { 0x00EE, "icircumflex" },  // LATIN SMALL LETTER I WITH CIRCUMFLEX
    { 0x00EF, "idieresis" },  // LATIN SMALL LETTER I WITH DIAERESIS
    { 0x00F0, "eth" },  // LATIN SMALL LETTER ETH
    { 0x00F1, "ntilde" },  // LATIN SMALL LETTER N WITH TILDE
    { 0x00F2, "ograve" },  // LATIN SMALL LETTER O WITH GRAVE
    { 0x00F3, "oacute" },  // LATIN SMALL LETTER O WITH ACUTE
    { 0x00F4, "ocircumflex" },  // LATIN SMALL LETTER O WITH CIRCUMFLEX
    { 0x00F5, "otilde" },  // LATIN SMALL LETTER O WITH TILDE
    { 0x00F6, "odieresis" },  // LATIN SMALL LETTER O WITH DIAERESIS
    { 0x00F7, "divide" },  // DIVISION SIGN
    { 0x00F8, "oslash" },  // LATIN SMALL LETTER O WITH STROKE
    { 0x00F9, "ugrave" },  // LATIN SMALL LETTER U WITH GRAVE
    { 0x00FA, "uacute" },  // LATIN SMALL LETTER U WITH ACUTE
    { 0x00FB, "ucircumflex" },  // LATIN SMALL LETTER U WITH CIRCUMFLEX
    { 0x00FC, "udieresis" },  // LATIN SMALL LETTER U WITH DIAERESIS
    { 0x00FD, "yacute" },  // LATIN SMALL LETTER Y WITH ACUTE
    { 0x00FE, "thorn" },  // LATIN SMALL LETTER THORN
    { 0x00FF, "ydieresis" },  // LATIN SMALL LETTER Y WITH DIAERESIS
    { 0x0100, "Amacron" },  // LATIN CAPITAL LETTER A WITH MACRON
    { 0x0101, "amacron" },  // LATIN SMALL LETTER A WITH MACRON
    { 0x0102, "Abreve" },  // LATIN CAPITAL LETTER A WITH BREVE
    { 0x0103, "abreve" },  // LATIN SMALL LETTER A WITH BREVE
    { 0x0104, "Aogonek" },  // LATIN CAPITAL LETTER A WITH OGONEK
    { 0x0105, "aogonek" },  // LATIN SMALL LETTER A WITH OGONEK
    { 0x0106, "Cacute" },  // LATIN CAPITAL LETTER C WITH ACUTE
    { 0x0107, "cacute" },  // LATIN SMALL LETTER C WITH ACUTE
    { 0x0108, "Ccircumflex" },  // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
    { 0x0109, "ccircumflex" },  // LATIN SMALL LETTER C WITH CIRCUMFLEX
    { 0x010A, "Cdotaccent" },  // LATIN CAPITAL LETTER C WITH DOT ABOVE
    { 0x010B, "cdotaccent" },  // LATIN SMALL LETTER C WITH DOT ABOVE
    { 0x010C, "Ccaron" },  // LATIN CAPITAL LETTER C WITH CARON
    { 0x010D, "ccaron" },  // LATIN SMALL LETTER C WITH CARON
    { 0x010E, "Dcaron" },  // LATIN CAPITAL LETTER D WITH CARON
    { 0x010F, "dcaron" },  // LATIN SMALL LETTER D WITH CARON
    { 0x0110, "Dcroat" },  // LATIN CAPITAL LETTER D WITH STROKE
    { 0x0111, "dcroat" },  // LATIN SMALL LETTER D WITH STROKE
    { 0x0112, "Emacron" },  // LATIN CAPITAL LETTER E WITH MACRON
    { 0x0113, "emacron" },  // LATIN SMALL LETTER E WITH MACRON
    { 0x0114, "Ebreve" },  // LATIN CAPITAL LETTER E WITH BREVE
    { 0x0115, "ebreve" },  // LATIN SMALL LETTER E WITH BREVE
    { 0x0116, "Edotaccent" },  // LATIN CAPITAL LETTER E WITH DOT ABOVE
    { 0x0117, "edotaccent" },  // LATIN SMALL LETTER E WITH DOT ABOVE
    { 0x0118, "Eogonek" },  // LATIN CAPITAL LETTER E WITH OGONEK
    { 0x0119, "eogonek" },  // LATIN SMALL LETTER E WITH OGONEK
    { 0x011A, "Ecaron" },  // LATIN CAPITAL LETTER E WITH CARON
    { 0x011B, "ecaron" },  // LATIN SMALL LETTER E WITH CARON
    { 0x011C, "Gcircumflex" },  // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
    { 0x011D, "gcircumflex" },  // LATIN SMALL LETTER G WITH CIRCUMFLEX
    { 0x011E, "Gbreve" },  // LATIN CAPITAL LETTER G WITH BREVE
    { 0x011F, "gbreve" },  // LATIN SMALL LETTER G WITH BREVE
    { 0x0120, "Gdotaccent" },  // LATIN CAPITAL LETTER G WITH DOT ABOVE
    { 0x0121, "gdotaccent" },  // LATIN SMALL LETTER G WITH DOT ABOVE
    { 0x0122, "Gcommaaccent" },  // LATIN CAPITAL LETTER G WITH CEDILLA
    { 0x0123, "gcommaaccent" },  // LATIN SMALL LETTER G WITH CEDILLA
    { 0x0124, "Hcircumflex" },  // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
    { 0x0125, "hcircumflex" },  // LATIN SMALL LETTER H WITH CIRCUMFLEX
    { 0x0126, "Hbar" },  // LATIN CAPITAL LETTER H WITH STROKE
    { 0x0127, "hbar" },  // LATIN SMALL LETTER H WITH STROKE
    { 0x0128, "Itilde" },  // LATIN CAPITAL LETTER I WITH TILDE
    { 0x0129, "itilde" },  // LATIN SMALL LETTER I WITH TILDE
    { 0x012A, "Imacron" },  // LATIN CAPITAL LETTER I WITH MACRON
    { 0x012B, "imacron" },  // LATIN SMALL LETTER I WITH MACRON
    { 0x012C, "Ibreve" },  // LATIN CAPITAL LETTER I WITH BREVE
    { 0x012D, "ibreve" },  // LATIN SMALL LETTER I WITH BREVE
    { 0x012E, "Iogonek" },  // LATIN CAPITAL LETTER I WITH OGONEK
    { 0x012F, "iogonek" },  // LATIN SMALL LETTER I WITH OGONEK
    { 0x0130, "Idotaccent" },  // LATIN CAPITAL LETTER I WITH DOT ABOVE
    { 0x0131, "dotlessi" },  // LATIN SMALL LETTER DOTLESS I
    { 0x0132, "IJ" },  // LATIN CAPITAL LIGATURE IJ
    { 0x0133, "ij" },  // LATIN SMALL LIGATURE IJ
    { 0x0134, "Jcircumflex" },  // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
    { 0x0135, "jcircumflex" },  // LATIN SMALL LETTER J WITH CIRCUMFLEX
    { 0x0136, "Kcommaaccent" },  // LATIN CAPITAL LETTER K WITH CEDILLA
    { 0x0137, "kcommaaccent" },  // LATIN SMALL LETTER K WITH CEDILLA
    { 0x0138, "kgreenlandic" },  // LATIN SMALL LETTER KRA
    { 0x0139, "Lacute" },  // LATIN CAPITAL LETTER L WITH ACUTE
    { 0x013A, "lacute" },  // LATIN SMALL LETTER L WITH ACUTE
    { 0x013B, "Lcommaaccent" },  // LATIN CAPITAL LETTER L WITH CEDILLA
    { 0x013C, "lcommaaccent" },  // LATIN SMALL LETTER L WITH CEDILLA
    { 0x013D, "Lcaron" },  // LATIN CAPITAL LETTER L WITH CARON
    { 0x013E, "lcaron" },  // LATIN SMALL LETTER L WITH CARON
    { 0x013F, "Ldot" },  // LATIN CAPITAL LETTER L WITH MIDDLE DOT
    { 0x0140, "ldot" },  // LATIN SMALL LETTER L WITH MIDDLE DOT
    { 0x0141, "Lslash" },  // LATIN CAPITAL LETTER L WITH STROKE
    { 0x0142, "lslash" },  // LATIN SMALL LETTER L WITH STROKE
    { 0x0143, "Nacute" },  // LATIN CAPITAL LETTER N WITH ACUTE
    { 0x0144, "nacute" },  // LATIN SMALL LETTER N WITH ACUTE
    { 0x0145, "Ncommaaccent" },  // LATIN CAPITAL LETTER N WITH CEDILLA
    { 0x0146, "ncommaaccent" },  // LATIN SMALL LETTER N WITH CEDILLA
    { 0x0147, "Ncaron" },  // LATIN CAPITAL LETTER N WITH CARON
    { 0x0148, "ncaron" },  // LATIN SMALL LETTER N WITH CARON
    { 0x0149, "napostrophe" },  // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
    { 0x014A, "Eng" },  // LATIN CAPITAL LETTER ENG
    { 0x014B, "eng" },  // LATIN SMALL LETTER ENG
    { 0x014C, "Omacron" },  // LATIN CAPITAL LETTER O WITH MACRON
    { 0x014D, "omacron" },  // LATIN SMALL LETTER O WITH MACRON
    { 0x014E, "Obreve" },  // LATIN CAPITAL LETTER O WITH BREVE
    { 0x014F, "obreve" },  // LATIN SMALL LETTER O WITH BREVE
    { 0x0150, "Ohungarumlaut" },  // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
    { 0x0151, "ohungarumlaut" },  // LATIN SMALL LETTER O WITH DOUBLE ACUTE
    { 0x0152, "OE" },  // LATIN CAPITAL LIGATURE OE
    { 0x0153, "oe" },  // LATIN SMALL LIGATURE OE
    { 0x0154, "Racute" },  // LATIN CAPITAL LETTER R WITH ACUTE
    { 0x0155, "racute" },  // LATIN SMALL LETTER R WITH ACUTE
    { 0x0156, "Rcommaaccent" },  // LATIN CAPITAL LETTER R WITH CEDILLA
    { 0x0157, "rcommaaccent" },  // LATIN SMALL LETTER R WITH CEDILLA
    { 0x0158, "Rcaron" },  // LATIN CAPITAL LETTER R WITH CARON
    { 0x0159, "rcaron" },  // LATIN SMALL LETTER R WITH CARON
    { 0x015A, "Sacute" },  // LATIN CAPITAL LETTER S WITH ACUTE
    { 0x015B, "sacute" },  // LATIN SMALL LETTER S WITH ACUTE
    { 0x015C, "Scircumflex" },  // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
    { 0x015D, "scircumflex" },  // LATIN SMALL LETTER S WITH CIRCUMFLEX
    { 0x015E, "Scedilla" },  // LATIN CAPITAL LETTER S WITH CEDILLA
    { 0x015F, "scedilla" },  // LATIN SMALL LETTER S WITH CEDILLA
    { 0x0160, "Scaron" },  // LATIN CAPITAL LETTER S WITH CARON
    { 0x0161, "scaron" },  // LATIN SMALL LETTER S WITH CARON
    { 0x0162, "Tcommaaccent" },  // LATIN CAPITAL LETTER T WITH CEDILLA
    { 0x0163, "tcommaaccent" },  // LATIN SMALL LETTER T WITH CEDILLA
    { 0x0164, "Tcaron" },  // LATIN CAPITAL LETTER T WITH CARON
    { 0x0165, "tcaron" },  // LATIN SMALL LETTER T WITH CARON
    { 0x0166, "Tbar" },  // LATIN CAPITAL LETTER T WITH STROKE
    { 0x0167, "tbar" },  // LATIN SMALL LETTER T WITH STROKE
    { 0x0168, "Utilde" },  // LATIN CAPITAL LETTER U WITH TILDE
    { 0x0169, "utilde" },  // LATIN SMALL LETTER U WITH TILDE
    { 0x016A, "Umacron" },  // LATIN CAPITAL LETTER U WITH MACRON
    { 0x016B, "umacron" },  // LATIN SMALL LETTER U WITH MACRON
    { 0x016C, "Ubreve" },  // LATIN CAPITAL LETTER U WITH BREVE
    { 0x016D, "ubreve" },  // LATIN SMALL LETTER U WITH BREVE
    { 0x016E, "Uring" },  // LATIN CAPITAL LETTER U WITH RING ABOVE
    { 0x016F, "uring" },  // LATIN SMALL LETTER U WITH RING ABOVE
    { 0x0170, "Uhungarumlaut" },  // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
    { 0x0171, "uhungarumlaut" },  // LATIN SMALL LETTER U WITH DOUBLE ACUTE
    { 0x0172, "Uogonek" },  // LATIN CAPITAL LETTER U WITH OGONEK
    { 0x0173, "uogonek" },  // LATIN SMALL LETTER U WITH OGONEK
    { 0x0174, "Wcircumflex" },  // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
    { 0x0175, "wcircumflex" },  // LATIN SMALL LETTER W WITH CIRCUMFLEX
    { 0x0176, "Ycircumflex" },  // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
    { 0x0177, "ycircumflex" },  // LATIN SMALL LETTER Y WITH CIRCUMFLEX
    { 0x0178, "Ydieresis" },  // LATIN CAPITAL LETTER Y WITH DIAERESIS
    { 0x0179, "Zacute" },  // LATIN CAPITAL LETTER Z WITH ACUTE
    { 0x017A, "zacute" },  // LATIN SMALL LETTER Z WITH ACUTE
    { 0x017B, "Zdotaccent" },  // LATIN CAPITAL LETTER Z WITH DOT ABOVE
    { 0x017C, "zdotaccent" },  // LATIN SMALL LETTER Z WITH DOT ABOVE
    { 0x017D, "Zcaron" },  // LATIN CAPITAL LETTER Z WITH CARON
    { 0x017E, "zcaron" },  // LATIN SMALL LETTER Z WITH CARON
    { 0x017F, "longs" },  // LATIN SMALL LETTER LONG S
    { 0x0192, "florin" },  // LATIN SMALL LETTER F WITH HOOK
    { 0x01A0, "Ohorn" },  // LATIN CAPITAL LETTER O WITH HORN
    { 0x01A1, "ohorn" },  // LATIN SMALL LETTER O WITH HORN
    { 0x01AF, "Uhorn" },  // LATIN CAPITAL LETTER U WITH HORN
    { 0x01B0, "uhorn" },  // LATIN SMALL LETTER U WITH HORN
    { 0x01E6, "Gcaron" },  // LATIN CAPITAL LETTER G WITH CARON
    { 0x01E7, "gcaron" },  // LATIN SMALL LETTER G WITH CARON
    { 0x01FA, "Aringacute" },  // LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
    { 0x01FB, "aringacute" },  // LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
    { 0x01FC, "AEacute" },  // LATIN CAPITAL LETTER AE WITH ACUTE
    { 0x01FD, "aeacute" },  // LATIN SMALL LETTER AE WITH ACUTE
    { 0x01FE, "Oslashacute" },  // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
    { 0x01FF, "oslashacute" },  // LATIN SMALL LETTER O WITH STROKE AND ACUTE
    { 0x0218, "Scommaaccent" },  // LATIN CAPITAL LETTER S WITH COMMA BELOW
    { 0x0219, "scommaaccent" },  // LATIN SMALL LETTER S WITH COMMA BELOW
    { 0x021A, "Tcommaaccent" },  // LATIN CAPITAL LETTER T WITH COMMA BELOW;Duplicate
    { 0x021B, "tcommaaccent" },  // LATIN SMALL LETTER T WITH COMMA BELOW;Duplicate
    { 0x02BC, "afii57929" },  // MODIFIER LETTER APOSTROPHE
    { 0x02BD, "afii64937" },  // MODIFIER LETTER REVERSED COMMA
    { 0x02C6, "circumflex" },  // MODIFIER LETTER CIRCUMFLEX ACCENT
    { 0x02C7, "caron" },  // CARON
    { 0x02C9, "macron" },  // MODIFIER LETTER MACRON;Duplicate
    { 0x02D8, "breve" },  // BREVE
    { 0x02D9, "dotaccent" },  // DOT ABOVE
    { 0x02DA, "ring" },  // RING ABOVE
    { 0x02DB, "ogonek" },  // OGONEK
    { 0x02DC, "tilde" },  // SMALL TILDE
    { 0x02DD, "hungarumlaut" },  // DOUBLE ACUTE ACCENT
    { 0x0300, "gravecomb" },  // COMBINING GRAVE ACCENT
    { 0x0301, "acutecomb" },  // COMBINING ACUTE ACCENT
    { 0x0303, "tildecomb" },  // COMBINING TILDE
    { 0x0309, "hookabovecomb" },  // COMBINING HOOK ABOVE
    { 0x0323, "dotbelowcomb" },  // COMBINING DOT BELOW
    { 0x0384, "tonos" },  // GREEK TONOS
    { 0x0385, "dieresistonos" },  // GREEK DIALYTIKA TONOS
    { 0x0386, "Alphatonos" },  // GREEK CAPITAL LETTER ALPHA WITH TONOS
    { 0x0387, "anoteleia" },  // GREEK ANO TELEIA
    { 0x0388, "Epsilontonos" },  // GREEK CAPITAL LETTER EPSILON WITH TONOS
    { 0x0389, "Etatonos" },  // GREEK CAPITAL LETTER ETA WITH TONOS
    { 0x038A, "Iotatonos" },  // GREEK CAPITAL LETTER IOTA WITH TONOS
    { 0x038C, "Omicrontonos" },  // GREEK CAPITAL LETTER OMICRON WITH TONOS
    { 0x038E, "Upsilontonos" },  // GREEK CAPITAL LETTER UPSILON WITH TONOS
    { 0x038F, "Omegatonos" },  // GREEK CAPITAL LETTER OMEGA WITH TONOS
    { 0x0390, "iotadieresistonos" },  // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
    { 0x0391, "Alpha" },  // GREEK CAPITAL LETTER ALPHA
    { 0x0392, "Beta" },  // GREEK CAPITAL LETTER BETA
    { 0x0393, "Gamma" },  // GREEK CAPITAL LETTER GAMMA
    { 0x0394, "Delta" },  // GREEK CAPITAL LETTER DELTA;Duplicate
    { 0x0395, "Epsilon" },  // GREEK CAPITAL LETTER EPSILON
    { 0x0396, "Zeta" },  // GREEK CAPITAL LETTER ZETA
    { 0x0397, "Eta" },  // GREEK CAPITAL LETTER ETA
    { 0x0398, "Theta" },  // GREEK CAPITAL LETTER THETA
    { 0x0399, "Iota" },  // GREEK CAPITAL LETTER IOTA
    { 0x039A, "Kappa" },  // GREEK CAPITAL LETTER KAPPA
    { 0x039B, "Lambda" },  // GREEK CAPITAL LETTER LAMDA
    { 0x039C, "Mu" },  // GREEK CAPITAL LETTER MU
    { 0x039D, "Nu" },  // GREEK CAPITAL LETTER NU
    { 0x039E, "Xi" },  // GREEK CAPITAL LETTER XI
    { 0x039F, "Omicron" },  // GREEK CAPITAL LETTER OMICRON
    { 0x03A0, "Pi" },  // GREEK CAPITAL LETTER PI
    { 0x03A1, "Rho" },  // GREEK CAPITAL LETTER RHO
    { 0x03A3, "Sigma" },  // GREEK CAPITAL LETTER SIGMA
    { 0x03A4, "Tau" },  // GREEK CAPITAL LETTER TAU
    { 0x03A5, "Upsilon" },  // GREEK CAPITAL LETTER UPSILON
    { 0x03A6, "Phi" },  // GREEK CAPITAL LETTER PHI
    { 0x03A7, "Chi" },  // GREEK CAPITAL LETTER CHI
    { 0x03A8, "Psi" },  // GREEK CAPITAL LETTER PSI
    { 0x03A9, "Omega" },  // GREEK CAPITAL LETTER OMEGA;Duplicate
    { 0x03AA, "Iotadieresis" },  // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
    { 0x03AB, "Upsilondieresis" },  // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
    { 0x03AC, "alphatonos" },  // GREEK SMALL LETTER ALPHA WITH TONOS
    { 0x03AD, "epsilontonos" },  // GREEK SMALL LETTER EPSILON WITH TONOS
    { 0x03AE, "etatonos" },  // GREEK SMALL LETTER ETA WITH TONOS
    { 0x03AF, "iotatonos" },  // GREEK SMALL LETTER IOTA WITH TONOS
    { 0x03B0, "upsilondieresistonos" },  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
    { 0x03B1, "alpha" },  // GREEK SMALL LETTER ALPHA
    { 0x03B2, "beta" },  // GREEK SMALL LETTER BETA
    { 0x03B3, "gamma" },  // GREEK SMALL LETTER GAMMA
    { 0x03B4, "delta" },  // GREEK SMALL LETTER DELTA
    { 0x03B5, "epsilon" },  // GREEK SMALL LETTER EPSILON
    { 0x03B6, "zeta" },  // GREEK SMALL LETTER ZETA
    { 0x03B7, "eta" },  // GREEK SMALL LETTER ETA
    { 0x03B8, "theta" },  // GREEK SMALL LETTER THETA
    { 0x03B9, "iota" },  // GREEK SMALL LETTER IOTA
    { 0x03BA, "kappa" },  // GREEK SMALL LETTER KAPPA
    { 0x03BB, "lambda" },  // GREEK SMALL LETTER LAMDA
    { 0x03BC, "mu" },  // GREEK SMALL LETTER MU;Duplicate
    { 0x03BD, "nu" },  // GREEK SMALL LETTER NU
    { 0x03BE, "xi" },  // GREEK SMALL LETTER XI
    { 0x03BF, "omicron" },  // GREEK SMALL LETTER OMICRON
    { 0x03C0, "pi" },  // GREEK SMALL LETTER PI
    { 0x03C1, "rho" },  // GREEK SMALL LETTER RHO
    { 0x03C2, "sigma1" },  // GREEK SMALL LETTER FINAL SIGMA
    { 0x03C3, "sigma" },  // GREEK SMALL LETTER SIGMA
    { 0x03C4, "tau" },  // GREEK SMALL LETTER TAU
    { 0x03C5, "upsilon" },  // GREEK SMALL LETTER UPSILON
    { 0x03C6, "phi" },  // GREEK SMALL LETTER PHI
    { 0x03C7, "chi" },  // GREEK SMALL LETTER CHI
    { 0x03C8, "psi" },  // GREEK SMALL LETTER PSI
    { 0x03C9, "omega" },  // GREEK SMALL LETTER OMEGA
    { 0x03CA, "iotadieresis" },  // GREEK SMALL LETTER IOTA WITH DIALYTIKA
    { 0x03CB, "upsilondieresis" },  // GREEK SMALL LETTER UPSILON WITH DIALYTIKA
    { 0x03CC, "omicrontonos" },  // GREEK SMALL LETTER OMICRON WITH TONOS
    { 0x03CD, "upsilontonos" },  // GREEK SMALL LETTER UPSILON WITH TONOS
    { 0x03CE, "omegatonos" },  // GREEK SMALL LETTER OMEGA WITH TONOS
    { 0x03D1, "theta1" },  // GREEK THETA SYMBOL
    { 0x03D2, "Upsilon1" },  // GREEK UPSILON WITH HOOK SYMBOL
    { 0x03D5, "phi1" },  // GREEK PHI SYMBOL
    { 0x03D6, "omega1" },  // GREEK PI SYMBOL
    { 0x0401, "afii10023" },  // CYRILLIC CAPITAL LETTER IO
    { 0x0402, "afii10051" },  // CYRILLIC CAPITAL LETTER DJE
    { 0x0403, "afii10052" },  // CYRILLIC CAPITAL LETTER GJE
    { 0x0404, "afii10053" },  // CYRILLIC CAPITAL LETTER UKRAINIAN IE
    { 0x0405, "afii10054" },  // CYRILLIC CAPITAL LETTER DZE
    { 0x0406, "afii10055" },  // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
    { 0x0407, "afii10056" },  // CYRILLIC CAPITAL LETTER YI
    { 0x0408, "afii10057" },  // CYRILLIC CAPITAL LETTER JE
    { 0x0409, "afii10058" },  // CYRILLIC CAPITAL LETTER LJE
    { 0x040A, "afii10059" },  // CYRILLIC CAPITAL LETTER NJE
    { 0x040B, "afii10060" },  // CYRILLIC CAPITAL LETTER TSHE
    { 0x040C, "afii10061" },  // CYRILLIC CAPITAL LETTER KJE
    { 0x040E, "afii10062" },  // CYRILLIC CAPITAL LETTER SHORT U
    { 0x040F, "afii10145" },  // CYRILLIC CAPITAL LETTER DZHE
    { 0x0410, "afii10017" },  // CYRILLIC CAPITAL LETTER A
    { 0x0411, "afii10018" },  // CYRILLIC CAPITAL LETTER BE
    { 0x0412, "afii10019" },  // CYRILLIC CAPITAL LETTER VE
    { 0x0413, "afii10020" },  // CYRILLIC CAPITAL LETTER GHE
    { 0x0414, "afii10021" },  // CYRILLIC CAPITAL LETTER DE
    { 0x0415, "afii10022" },  // CYRILLIC CAPITAL LETTER IE
    { 0x0416, "afii10024" },  // CYRILLIC CAPITAL LETTER ZHE
    { 0x0417, "afii10025" },  // CYRILLIC CAPITAL LETTER ZE
    { 0x0418, "afii10026" },  // CYRILLIC CAPITAL LETTER I
    { 0x0419, "afii10027" },  // CYRILLIC CAPITAL LETTER SHORT I
    { 0x041A, "afii10028" },  // CYRILLIC CAPITAL LETTER KA
    { 0x041B, "afii10029" },  // CYRILLIC CAPITAL LETTER EL
    { 0x041C, "afii10030" },  // CYRILLIC CAPITAL LETTER EM
    { 0x041D, "afii10031" },  // CYRILLIC CAPITAL LETTER EN
    { 0x041E, "afii10032" },  // CYRILLIC CAPITAL LETTER O
    { 0x041F, "afii10033" },  // CYRILLIC CAPITAL LETTER PE
    { 0x0420, "afii10034" },  // CYRILLIC CAPITAL LETTER ER
    { 0x0421, "afii10035" },  // CYRILLIC CAPITAL LETTER ES
    { 0x0422, "afii10036" },  // CYRILLIC CAPITAL LETTER TE
    { 0x0423, "afii10037" },  // CYRILLIC CAPITAL LETTER U
    { 0x0424, "afii10038" },  // CYRILLIC CAPITAL LETTER EF
    { 0x0425, "afii10039" },  // CYRILLIC CAPITAL LETTER HA
    { 0x0426, "afii10040" },  // CYRILLIC CAPITAL LETTER TSE
    { 0x0427, "afii10041" },  // CYRILLIC CAPITAL LETTER CHE
    { 0x0428, "afii10042" },  // CYRILLIC CAPITAL LETTER SHA
    { 0x0429, "afii10043" },  // CYRILLIC CAPITAL LETTER SHCHA
    { 0x042A, "afii10044" },  // CYRILLIC CAPITAL LETTER HARD SIGN
    { 0x042B, "afii10045" },  // CYRILLIC CAPITAL LETTER YERU
    { 0x042C, "afii10046" },  // CYRILLIC CAPITAL LETTER SOFT SIGN
    { 0x042D, "afii10047" },  // CYRILLIC CAPITAL LETTER E
    { 0x042E, "afii10048" },  // CYRILLIC CAPITAL LETTER YU
    { 0x042F, "afii10049" },  // CYRILLIC CAPITAL LETTER YA
    { 0x0430, "afii10065" },  // CYRILLIC SMALL LETTER A
    { 0x0431, "afii10066" },  // CYRILLIC SMALL LETTER BE
    { 0x0432, "afii10067" },  // CYRILLIC SMALL LETTER VE
    { 0x0433, "afii10068" },  // CYRILLIC SMALL LETTER GHE
    { 0x0434, "afii10069" },  // CYRILLIC SMALL LETTER DE
    { 0x0435, "afii10070" },  // CYRILLIC SMALL LETTER IE
    { 0x0436, "afii10072" },  // CYRILLIC SMALL LETTER ZHE
    { 0x0437, "afii10073" },  // CYRILLIC SMALL LETTER ZE
    { 0x0438, "afii10074" },  // CYRILLIC SMALL LETTER I
    { 0x0439, "afii10075" },  // CYRILLIC SMALL LETTER SHORT I
    { 0x043A, "afii10076" },  // CYRILLIC SMALL LETTER KA
    { 0x043B, "afii10077" },  // CYRILLIC SMALL LETTER EL
    { 0x043C, "afii10078" },  // CYRILLIC SMALL LETTER EM
    { 0x043D, "afii10079" },  // CYRILLIC SMALL LETTER EN
    { 0x043E, "afii10080" },  // CYRILLIC SMALL LETTER O
    { 0x043F, "afii10081" },  // CYRILLIC SMALL LETTER PE
    { 0x0440, "afii10082" },  // CYRILLIC SMALL LETTER ER
    { 0x0441, "afii10083" },  // CYRILLIC SMALL LETTER ES
    { 0x0442, "afii10084" },  // CYRILLIC SMALL LETTER TE
    { 0x0443, "afii10085" },  // CYRILLIC SMALL LETTER U
    { 0x0444, "afii10086" },  // CYRILLIC SMALL LETTER EF
    { 0x0445, "afii10087" },  // CYRILLIC SMALL LETTER HA
    { 0x0446, "afii10088" },  // CYRILLIC SMALL LETTER TSE
    { 0x0447, "afii10089" },  // CYRILLIC SMALL LETTER CHE
    { 0x0448, "afii10090" },  // CYRILLIC SMALL LETTER SHA
    { 0x0449, "afii10091" },  // CYRILLIC SMALL LETTER SHCHA
    { 0x044A, "afii10092" },  // CYRILLIC SMALL LETTER HARD SIGN
    { 0x044B, "afii10093" },  // CYRILLIC SMALL LETTER YERU
    { 0x044C, "afii10094" },  // CYRILLIC SMALL LETTER SOFT SIGN
    { 0x044D, "afii10095" },  // CYRILLIC SMALL LETTER E
    { 0x044E, "afii10096" },  // CYRILLIC SMALL LETTER YU
    { 0x044F, "afii10097" },  // CYRILLIC SMALL LETTER YA
    { 0x0451, "afii10071" },  // CYRILLIC SMALL LETTER IO
    { 0x0452, "afii10099" },  // CYRILLIC SMALL LETTER DJE
    { 0x0453, "afii10100" },  // CYRILLIC SMALL LETTER GJE
    { 0x0454, "afii10101" },  // CYRILLIC SMALL LETTER UKRAINIAN IE
    { 0x0455, "afii10102" },  // CYRILLIC SMALL LETTER DZE
    { 0x0456, "afii10103" },  // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
    { 0x0457, "afii10104" },  // CYRILLIC SMALL LETTER YI
    { 0x0458, "afii10105" },  // CYRILLIC SMALL LETTER JE
    { 0x0459, "afii10106" },  // CYRILLIC SMALL LETTER LJE
    { 0x045A, "afii10107" },  // CYRILLIC SMALL LETTER NJE
    { 0x045B, "afii10108" },  // CYRILLIC SMALL LETTER TSHE
    { 0x045C, "afii10109" },  // CYRILLIC SMALL LETTER KJE
    { 0x045E, "afii10110" },  // CYRILLIC SMALL LETTER SHORT U
    { 0x045F, "afii10193" },  // CYRILLIC SMALL LETTER DZHE
    { 0x0462, "afii10146" },  // CYRILLIC CAPITAL LETTER YAT
    { 0x0463, "afii10194" },  // CYRILLIC SMALL LETTER YAT
    { 0x0472, "afii10147" },  // CYRILLIC CAPITAL LETTER FITA
    { 0x0473, "afii10195" },  // CYRILLIC SMALL LETTER FITA
    { 0x0474, "afii10148" },  // CYRILLIC CAPITAL LETTER IZHITSA
    { 0x0475, "afii10196" },  // CYRILLIC SMALL LETTER IZHITSA
    { 0x0490, "afii10050" },  // CYRILLIC CAPITAL LETTER GHE WITH UPTURN
    { 0x0491, "afii10098" },  // CYRILLIC SMALL LETTER GHE WITH UPTURN
    { 0x04D9, "afii10846" },  // CYRILLIC SMALL LETTER SCHWA
    { 0x05B0, "afii57799" },  // HEBREW POINT SHEVA
    { 0x05B1, "afii57801" },  // HEBREW POINT HATAF SEGOL
    { 0x05B2, "afii57800" },  // HEBREW POINT HATAF PATAH
    { 0x05B3, "afii57802" },  // HEBREW POINT HATAF QAMATS
    { 0x05B4, "afii57793" },  // HEBREW POINT HIRIQ
    { 0x05B5, "afii57794" },  // HEBREW POINT TSERE
    { 0x05B6, "afii57795" },  // HEBREW POINT SEGOL
    { 0x05B7, "afii57798" },  // HEBREW POINT PATAH
    { 0x05B8, "afii57797" },  // HEBREW POINT QAMATS
    { 0x05B9, "afii57806" },  // HEBREW POINT HOLAM
    { 0x05BB, "afii57796" },  // HEBREW POINT QUBUTS
    { 0x05BC, "afii57807" },  // HEBREW POINT DAGESH OR MAPIQ
    { 0x05BD, "afii57839" },  // HEBREW POINT METEG
    { 0x05BE, "afii57645" },  // HEBREW PUNCTUATION MAQAF
    { 0x05BF, "afii57841" },  // HEBREW POINT RAFE
    { 0x05C0, "afii57842" },  // HEBREW PUNCTUATION PASEQ
    { 0x05C1, "afii57804" },  // HEBREW POINT SHIN DOT
    { 0x05C2, "afii57803" },  // HEBREW POINT SIN DOT
    { 0x05C3, "afii57658" },  // HEBREW PUNCTUATION SOF PASUQ
    { 0x05D0, "afii57664" },  // HEBREW LETTER ALEF
    { 0x05D1, "afii57665" },  // HEBREW LETTER BET
    { 0x05D2, "afii57666" },  // HEBREW LETTER GIMEL
    { 0x05D3, "afii57667" },  // HEBREW LETTER DALET
    { 0x05D4, "afii57668" },  // HEBREW LETTER HE
    { 0x05D5, "afii57669" },  // HEBREW LETTER VAV
    { 0x05D6, "afii57670" },  // HEBREW LETTER ZAYIN
    { 0x05D7, "afii57671" },  // HEBREW LETTER HET
    { 0x05D8, "afii57672" },  // HEBREW LETTER TET
    { 0x05D9, "afii57673" },  // HEBREW LETTER YOD
    { 0x05DA, "afii57674" },  // HEBREW LETTER FINAL KAF
    { 0x05DB, "afii57675" },  // HEBREW LETTER KAF
    { 0x05DC, "afii57676" },  // HEBREW LETTER LAMED
    { 0x05DD, "afii57677" },  // HEBREW LETTER FINAL MEM
    { 0x05DE, "afii57678" },  // HEBREW LETTER MEM
    { 0x05DF, "afii57679" },  // HEBREW LETTER FINAL NUN
    { 0x05E0, "afii57680" },  // HEBREW LETTER NUN
    { 0x05E1, "afii57681" },  // HEBREW LETTER SAMEKH
    { 0x05E2, "afii57682" },  // HEBREW LETTER AYIN
    { 0x05E3, "afii57683" },  // HEBREW LETTER FINAL PE
    { 0x05E4, "afii57684" },  // HEBREW LETTER PE
    { 0x05E5, "afii57685" },  // HEBREW LETTER FINAL TSADI
    { 0x05E6, "afii57686" },  // HEBREW LETTER TSADI
    { 0x05E7, "afii57687" },  // HEBREW LETTER QOF
    { 0x05E8, "afii57688" },  // HEBREW LETTER RESH
    { 0x05E9, "afii57689" },  // HEBREW LETTER SHIN
    { 0x05EA, "afii57690" },  // HEBREW LETTER TAV
    { 0x05F0, "afii57716" },  // HEBREW LIGATURE YIDDISH DOUBLE VAV
    { 0x05F1, "afii57717" },  // HEBREW LIGATURE YIDDISH VAV YOD
    { 0x05F2, "afii57718" },  // HEBREW LIGATURE YIDDISH DOUBLE YOD
    { 0x060C, "afii57388" },  // ARABIC COMMA
    { 0x061B, "afii57403" },  // ARABIC SEMICOLON
    { 0x061F, "afii57407" },  // ARABIC QUESTION MARK
    { 0x0621, "afii57409" },  // ARABIC LETTER HAMZA
    { 0x0622, "afii57410" },  // ARABIC LETTER ALEF WITH MADDA ABOVE
    { 0x0623, "afii57411" },  // ARABIC LETTER ALEF WITH HAMZA ABOVE
    { 0x0624, "afii57412" },  // ARABIC LETTER WAW WITH HAMZA ABOVE
    { 0x0625, "afii57413" },  // ARABIC LETTER ALEF WITH HAMZA BELOW
    { 0x0626, "afii57414" },  // ARABIC LETTER YEH WITH HAMZA ABOVE
    { 0x0627, "afii57415" },  // ARABIC LETTER ALEF
    { 0x0628, "afii57416" },  // ARABIC LETTER BEH
    { 0x0629, "afii57417" },  // ARABIC LETTER TEH MARBUTA
    { 0x062A, "afii57418" },  // ARABIC LETTER TEH
    { 0x062B, "afii57419" },  // ARABIC LETTER THEH
    { 0x062C, "afii57420" },  // ARABIC LETTER JEEM
    { 0x062D, "afii57421" },  // ARABIC LETTER HAH
    { 0x062E, "afii57422" },  // ARABIC LETTER KHAH
    { 0x062F, "afii57423" },  // ARABIC LETTER DAL
    { 0x0630, "afii57424" },  // ARABIC LETTER THAL
    { 0x0631, "afii57425" },  // ARABIC LETTER REH
    { 0x0632, "afii57426" },  // ARABIC LETTER ZAIN
    { 0x0633, "afii57427" },  // ARABIC LETTER SEEN
    { 0x0634, "afii57428" },  // ARABIC LETTER SHEEN
    { 0x0635, "afii57429" },  // ARABIC LETTER SAD
    { 0x0636, "afii57430" },  // ARABIC LETTER DAD
    { 0x0637, "afii57431" },  // ARABIC LETTER TAH
    { 0x0638, "afii57432" },  // ARABIC LETTER ZAH
    { 0x0639, "afii57433" },  // ARABIC LETTER AIN
    { 0x063A, "afii57434" },  // ARABIC LETTER GHAIN
    { 0x0640, "afii57440" },  // ARABIC TATWEEL
    { 0x0641, "afii57441" },  // ARABIC LETTER FEH
    { 0x0642, "afii57442" },  // ARABIC LETTER QAF
    { 0x0643, "afii57443" },  // ARABIC LETTER KAF
    { 0x0644, "afii57444" },  // ARABIC LETTER LAM
    { 0x0645, "afii57445" },  // ARABIC LETTER MEEM
    { 0x0646, "afii57446" },  // ARABIC LETTER NOON
    { 0x0647, "afii57470" },  // ARABIC LETTER HEH
    { 0x0648, "afii57448" },  // ARABIC LETTER WAW
    { 0x0649, "afii57449" },  // ARABIC LETTER ALEF MAKSURA
    { 0x064A, "afii57450" },  // ARABIC LETTER YEH
    { 0x064B, "afii57451" },  // ARABIC FATHATAN
    { 0x064C, "afii57452" },  // ARABIC DAMMATAN
    { 0x064D, "afii57453" },  // ARABIC KASRATAN
    { 0x064E, "afii57454" },  // ARABIC FATHA
    { 0x064F, "afii57455" },  // ARABIC DAMMA
    { 0x0650, "afii57456" },  // ARABIC KASRA
    { 0x0651, "afii57457" },  // ARABIC SHADDA
    { 0x0652, "afii57458" },  // ARABIC SUKUN
    { 0x0660, "afii57392" },  // ARABIC-INDIC DIGIT ZERO
    { 0x0661, "afii57393" },  // ARABIC-INDIC DIGIT ONE
    { 0x0662, "afii57394" },  // ARABIC-INDIC DIGIT TWO
    { 0x0663, "afii57395" },  // ARABIC-INDIC DIGIT THREE
    { 0x0664, "afii57396" },  // ARABIC-INDIC DIGIT FOUR
    { 0x0665, "afii57397" },  // ARABIC-INDIC DIGIT FIVE
    { 0x0666, "afii57398" },  // ARABIC-INDIC DIGIT SIX
    { 0x0667, "afii57399" },  // ARABIC-INDIC DIGIT SEVEN
    { 0x0668, "afii57400" },  // ARABIC-INDIC DIGIT EIGHT
    { 0x0669, "afii57401" },  // ARABIC-INDIC DIGIT NINE
    { 0x066A, "afii57381" },  // ARABIC PERCENT SIGN
    { 0x066D, "afii63167" },  // ARABIC FIVE POINTED STAR
    { 0x0679, "afii57511" },  // ARABIC LETTER TTEH
    { 0x067E, "afii57506" },  // ARABIC LETTER PEH
    { 0x0686, "afii57507" },  // ARABIC LETTER TCHEH
    { 0x0688, "afii57512" },  // ARABIC LETTER DDAL
    { 0x0691, "afii57513" },  // ARABIC LETTER RREH
    { 0x0698, "afii57508" },  // ARABIC LETTER JEH
    { 0x06A4, "afii57505" },  // ARABIC LETTER VEH
    { 0x06AF, "afii57509" },  // ARABIC LETTER GAF
    { 0x06BA, "afii57514" },  // ARABIC LETTER NOON GHUNNA
    { 0x06D2, "afii57519" },  // ARABIC LETTER YEH BARREE
    { 0x06D5, "afii57534" },  // ARABIC LETTER AE
    { 0x1E80, "Wgrave" },  // LATIN CAPITAL LETTER W WITH GRAVE
    { 0x1E81, "wgrave" },  // LATIN SMALL LETTER W WITH GRAVE
    { 0x1E82, "Wacute" },  // LATIN CAPITAL LETTER W WITH ACUTE
    { 0x1E83, "wacute" },  // LATIN SMALL LETTER W WITH ACUTE
    { 0x1E84, "Wdieresis" },  // LATIN CAPITAL LETTER W WITH DIAERESIS
    { 0x1E85, "wdieresis" },  // LATIN SMALL LETTER W WITH DIAERESIS
    { 0x1EF2, "Ygrave" },  // LATIN CAPITAL LETTER Y WITH GRAVE
    { 0x1EF3, "ygrave" },  // LATIN SMALL LETTER Y WITH GRAVE
    { 0x200C, "afii61664" },  // ZERO WIDTH NON-JOINER
    { 0x200D, "afii301" },  // ZERO WIDTH JOINER
    { 0x200E, "afii299" },  // LEFT-TO-RIGHT MARK
    { 0x200F, "afii300" },  // RIGHT-TO-LEFT MARK
    { 0x2012, "figuredash" },  // FIGURE DASH
    { 0x2013, "endash" },  // EN DASH
    { 0x2014, "emdash" },  // EM DASH
    { 0x2015, "afii00208" },  // HORIZONTAL BAR
    { 0x2017, "underscoredbl" },  // DOUBLE LOW LINE
    { 0x2018, "quoteleft" },  // LEFT SINGLE QUOTATION MARK
    { 0x2019, "quoteright" },  // RIGHT SINGLE QUOTATION MARK
    { 0x201A, "quotesinglbase" },  // SINGLE LOW-9 QUOTATION MARK
    { 0x201B, "quotereversed" },  // SINGLE HIGH-REVERSED-9 QUOTATION MARK
    { 0x201C, "quotedblleft" },  // LEFT DOUBLE QUOTATION MARK
    { 0x201D, "quotedblright" },  // RIGHT DOUBLE QUOTATION MARK
    { 0x201E, "quotedblbase" },  // DOUBLE LOW-9 QUOTATION MARK
    { 0x2020, "dagger" },  // DAGGER
    { 0x2021, "daggerdbl" },  // DOUBLE DAGGER
    { 0x2022, "bullet" },  // BULLET
    { 0x2024, "onedotenleader" },  // ONE DOT LEADER
    { 0x2025, "twodotenleader" },  // TWO DOT LEADER
    { 0x2026, "ellipsis" },  // HORIZONTAL ELLIPSIS
    { 0x202C, "afii61573" },  // POP DIRECTIONAL FORMATTING
    { 0x202D, "afii61574" },  // LEFT-TO-RIGHT OVERRIDE
    { 0x202E, "afii61575" },  // RIGHT-TO-LEFT OVERRIDE
    { 0x2030, "perthousand" },  // PER MILLE SIGN
    { 0x2032, "minute" },  // PRIME
    { 0x2033, "second" },  // DOUBLE PRIME
    { 0x2039, "guilsinglleft" },  // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
    { 0x203A, "guilsinglright" },  // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
    { 0x203C, "exclamdbl" },  // DOUBLE EXCLAMATION MARK
    { 0x2044, "fraction" },  // FRACTION SLASH
    { 0x2070, "zerosuperior" },  // SUPERSCRIPT ZERO
    { 0x2074, "foursuperior" },  // SUPERSCRIPT FOUR
    { 0x2075, "fivesuperior" },  // SUPERSCRIPT FIVE
    { 0x2076, "sixsuperior" },  // SUPERSCRIPT SIX
    { 0x2077, "sevensuperior" },  // SUPERSCRIPT SEVEN
    { 0x2078, "eightsuperior" },  // SUPERSCRIPT EIGHT
    { 0x2079, "ninesuperior" },  // SUPERSCRIPT NINE
    { 0x207D, "parenleftsuperior" },  // SUPERSCRIPT LEFT PARENTHESIS
    { 0x207E, "parenrightsuperior" },  // SUPERSCRIPT RIGHT PARENTHESIS
    { 0x207F, "nsuperior" },  // SUPERSCRIPT LATIN SMALL LETTER N
    { 0x2080, "zeroinferior" },  // SUBSCRIPT ZERO
    { 0x2081, "oneinferior" },  // SUBSCRIPT ONE
    { 0x2082, "twoinferior" },  // SUBSCRIPT TWO
    { 0x2083, "threeinferior" },  // SUBSCRIPT THREE
    { 0x2084, "fourinferior" },  // SUBSCRIPT FOUR
    { 0x2085, "fiveinferior" },  // SUBSCRIPT FIVE
    { 0x2086, "sixinferior" },  // SUBSCRIPT SIX
    { 0x2087, "seveninferior" },  // SUBSCRIPT SEVEN
    { 0x2088, "eightinferior" },  // SUBSCRIPT EIGHT
    { 0x2089, "nineinferior" },  // SUBSCRIPT NINE
    { 0x208D, "parenleftinferior" },  // SUBSCRIPT LEFT PARENTHESIS
    { 0x208E, "parenrightinferior" },  // SUBSCRIPT RIGHT PARENTHESIS
    { 0x20A1, "colonmonetary" },  // COLON SIGN
    { 0x20A3, "franc" },  // FRENCH FRANC SIGN
    { 0x20A4, "lira" },  // LIRA SIGN
    { 0x20A7, "peseta" },  // PESETA SIGN
    { 0x20AA, "afii57636" },  // NEW SHEQEL SIGN
    { 0x20AB, "dong" },  // DONG SIGN
    { 0x20AC, "Euro" },  // EURO SIGN
    { 0x2105, "afii61248" },  // CARE OF
    { 0x2111, "Ifraktur" },  // BLACK-LETTER CAPITAL I
    { 0x2113, "afii61289" },  // SCRIPT SMALL L
    { 0x2116, "afii61352" },  // NUMERO SIGN
    { 0x2118, "weierstrass" },  // SCRIPT CAPITAL P
    { 0x211C, "Rfraktur" },  // BLACK-LETTER CAPITAL R
    { 0x211E, "prescription" },  // PRESCRIPTION TAKE
    { 0x2122, "trademark" },  // TRADE MARK SIGN
    { 0x2126, "Omega" },  // OHM SIGN
    { 0x212E, "estimated" },  // ESTIMATED SYMBOL
    { 0x2135, "aleph" },  // ALEF SYMBOL
    { 0x2153, "onethird" },  // VULGAR FRACTION ONE THIRD
    { 0x2154, "twothirds" },  // VULGAR FRACTION TWO THIRDS
    { 0x215B, "oneeighth" },  // VULGAR FRACTION ONE EIGHTH
    { 0x215C, "threeeighths" },  // VULGAR FRACTION THREE EIGHTHS
    { 0x215D, "fiveeighths" },  // VULGAR FRACTION FIVE EIGHTHS
    { 0x215E, "seveneighths" },  // VULGAR FRACTION SEVEN EIGHTHS
    { 0x2190, "arrowleft" },  // LEFTWARDS ARROW
    { 0x2191, "arrowup" },  // UPWARDS ARROW
    { 0x2192, "arrowright" },  // RIGHTWARDS ARROW
    { 0x2193, "arrowdown" },  // DOWNWARDS ARROW
    { 0x2194, "arrowboth" },  // LEFT RIGHT ARROW
    { 0x2195, "arrowupdn" },  // UP DOWN ARROW
    { 0x21A8, "arrowupdnbse" },  // UP DOWN ARROW WITH BASE
    { 0x21B5, "carriagereturn" },  // DOWNWARDS ARROW WITH CORNER LEFTWARDS
    { 0x21D0, "arrowdblleft" },  // LEFTWARDS DOUBLE ARROW
    { 0x21D1, "arrowdblup" },  // UPWARDS DOUBLE ARROW
    { 0x21D2, "arrowdblright" },  // RIGHTWARDS DOUBLE ARROW
    { 0x21D3, "arrowdbldown" },  // DOWNWARDS DOUBLE ARROW
    { 0x21D4, "arrowdblboth" },  // LEFT RIGHT DOUBLE ARROW
    { 0x2200, "universal" },  // FOR ALL
    { 0x2202, "partialdiff" },  // PARTIAL DIFFERENTIAL
    { 0x2203, "existential" },  // THERE EXISTS
    { 0x2205, "emptyset" },  // EMPTY SET
    { 0x2206, "Delta" },  // INCREMENT
    { 0x2207, "gradient" },  // NABLA
    { 0x2208, "element" },  // ELEMENT OF
    { 0x2209, "notelement" },  // NOT AN ELEMENT OF
    { 0x220B, "suchthat" },  // CONTAINS AS MEMBER
    { 0x220F, "product" },  // N-ARY PRODUCT
    { 0x2211, "summation" },  // N-ARY SUMMATION
    { 0x2212, "minus" },  // MINUS SIGN
    { 0x2215, "fraction" },  // DIVISION SLASH;Duplicate
    { 0x2217, "asteriskmath" },  // ASTERISK OPERATOR
    { 0x2219, "periodcentered" },  // BULLET OPERATOR;Duplicate
    { 0x221A, "radical" },  // SQUARE ROOT
    { 0x221D, "proportional" },  // PROPORTIONAL TO
    { 0x221E, "infinity" },  // INFINITY
    { 0x221F, "orthogonal" },  // RIGHT ANGLE
    { 0x2220, "angle" },  // ANGLE
    { 0x2227, "logicaland" },  // LOGICAL AND
    { 0x2228, "logicalor" },  // LOGICAL OR
    { 0x2229, "intersection" },  // INTERSECTION
    { 0x222A, "union" },  // UNION
    { 0x222B, "integral" },  // INTEGRAL
    { 0x2234, "therefore" },  // THEREFORE
    { 0x223C, "similar" },  // TILDE OPERATOR
    { 0x2245, "congruent" },  // APPROXIMATELY EQUAL TO
    { 0x2248, "approxequal" },  // ALMOST EQUAL TO
    { 0x2260, "notequal" },  // NOT EQUAL TO
    { 0x2261, "equivalence" },  // IDENTICAL TO
    { 0x2264, "lessequal" },  // LESS-THAN OR EQUAL TO
    { 0x2265, "greaterequal" },  // GREATER-THAN OR EQUAL TO
    { 0x2282, "propersubset" },  // SUBSET OF
    { 0x2283, "propersuperset" },  // SUPERSET OF
    { 0x2284, "notsubset" },  // NOT A SUBSET OF
    { 0x2286, "reflexsubset" },  // SUBSET OF OR EQUAL TO
    { 0x2287, "reflexsuperset" },  // SUPERSET OF OR EQUAL TO
    { 0x2295, "circleplus" },  // CIRCLED PLUS
    { 0x2297, "circlemultiply" },  // CIRCLED TIMES
    { 0x22A5, "perpendicular" },  // UP TACK
    { 0x22C5, "dotmath" },  // DOT OPERATOR
    { 0x2302, "house" },  // HOUSE
    { 0x2310, "revlogicalnot" },  // REVERSED NOT SIGN
    { 0x2320, "integraltp" },  // TOP HALF INTEGRAL
    { 0x2321, "integralbt" },  // BOTTOM HALF INTEGRAL
    { 0x2329, "angleleft" },  // LEFT-POINTING ANGLE BRACKET
    { 0x232A, "angleright" },  // RIGHT-POINTING ANGLE BRACKET
    { 0x2500, "SF100000" },  // BOX DRAWINGS LIGHT HORIZONTAL
    { 0x2502, "SF110000" },  // BOX DRAWINGS LIGHT VERTICAL
    { 0x250C, "SF010000" },  // BOX DRAWINGS LIGHT DOWN AND RIGHT
    { 0x2510, "SF030000" },  // BOX DRAWINGS LIGHT DOWN AND LEFT
    { 0x2514, "SF020000" },  // BOX DRAWINGS LIGHT UP AND RIGHT
    { 0x2518, "SF040000" },  // BOX DRAWINGS LIGHT UP AND LEFT
    { 0x251C, "SF080000" },  // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
    { 0x2524, "SF090000" },  // BOX DRAWINGS LIGHT VERTICAL AND LEFT
    { 0x252C, "SF060000" },  // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
    { 0x2534, "SF070000" },  // BOX DRAWINGS LIGHT UP AND HORIZONTAL
    { 0x253C, "SF050000" },  // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
    { 0x2550, "SF430000" },  // BOX DRAWINGS DOUBLE HORIZONTAL
    { 0x2551, "SF240000" },  // BOX DRAWINGS DOUBLE VERTICAL
    { 0x2552, "SF510000" },  // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
    { 0x2553, "SF520000" },  // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
    { 0x2554, "SF390000" },  // BOX DRAWINGS DOUBLE DOWN AND RIGHT
    { 0x2555, "SF220000" },  // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
    { 0x2556, "SF210000" },  // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
    { 0x2557, "SF250000" },  // BOX DRAWINGS DOUBLE DOWN AND LEFT
    { 0x2558, "SF500000" },  // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
    { 0x2559, "SF490000" },  // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
    { 0x255A, "SF380000" },  // BOX DRAWINGS DOUBLE UP AND RIGHT
    { 0x255B, "SF280000" },  // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
    { 0x255C, "SF270000" },  // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
    { 0x255D, "SF260000" },  // BOX DRAWINGS DOUBLE UP AND LEFT
    { 0x255E, "SF360000" },  // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
    { 0x255F, "SF370000" },  // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
    { 0x2560, "SF420000" },  // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
    { 0x2561, "SF190000" },  // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
    { 0x2562, "SF200000" },  // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
    { 0x2563, "SF230000" },  // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
    { 0x2564, "SF470000" },  // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
    { 0x2565, "SF480000" },  // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
    { 0x2566, "SF410000" },  // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
    { 0x2567, "SF450000" },  // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
    { 0x2568, "SF460000" },  // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
    { 0x2569, "SF400000" },  // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
    { 0x256A, "SF540000" },  // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
    { 0x256B, "SF530000" },  // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
    { 0x256C, "SF440000" },  // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
    { 0x2580, "upblock" },  // UPPER HALF BLOCK
    { 0x2584, "dnblock" },  // LOWER HALF BLOCK
    { 0x2588, "block" },  // FULL BLOCK
    { 0x258C, "lfblock" },  // LEFT HALF BLOCK
    { 0x2590, "rtblock" },  // RIGHT HALF BLOCK
    { 0x2591, "ltshade" },  // LIGHT SHADE
    { 0x2592, "shade" },  // MEDIUM SHADE
    { 0x2593, "dkshade" },  // DARK SHADE
    { 0x25A0, "filledbox" },  // BLACK SQUARE
    { 0x25A1, "H22073" },  // WHITE SQUARE
    { 0x25AA, "H18543" },  // BLACK SMALL SQUARE
    { 0x25AB, "H18551" },  // WHITE SMALL SQUARE
    { 0x25AC, "filledrect" },  // BLACK RECTANGLE
    { 0x25B2, "triagup" },  // BLACK UP-POINTING TRIANGLE
    { 0x25BA, "triagrt" },  // BLACK RIGHT-POINTING POINTER
    { 0x25BC, "triagdn" },  // BLACK DOWN-POINTING TRIANGLE
    { 0x25C4, "triaglf" },  // BLACK LEFT-POINTING POINTER
    { 0x25CA, "lozenge" },  // LOZENGE
    { 0x25CB, "circle" },  // WHITE CIRCLE
    { 0x25CF, "H18533" },  // BLACK CIRCLE
    { 0x25D8, "invbullet" },  // INVERSE BULLET
    { 0x25D9, "invcircle" },  // INVERSE WHITE CIRCLE
    { 0x25E6, "openbullet" },  // WHITE BULLET
    { 0x263A, "smileface" },  // WHITE SMILING FACE
    { 0x263B, "invsmileface" },  // BLACK SMILING FACE
    { 0x263C, "sun" },  // WHITE SUN WITH RAYS
    { 0x2640, "female" },  // FEMALE SIGN
    { 0x2642, "male" },  // MALE SIGN
    { 0x2660, "spade" },  // BLACK SPADE SUIT
    { 0x2663, "club" },  // BLACK CLUB SUIT
    { 0x2665, "heart" },  // BLACK HEART SUIT
    { 0x2666, "diamond" },  // BLACK DIAMOND SUIT
    { 0x266A, "musicalnote" },  // EIGHTH NOTE
    { 0x266B, "musicalnotedbl" },  // BEAMED EIGHTH NOTES
    { 0xFB00, "ff" },  // LATIN SMALL LIGATURE FF
    { 0xFB01, "fi" },  // LATIN SMALL LIGATURE FI
    { 0xFB02, "fl" },  // LATIN SMALL LIGATURE FL
    { 0xFB03, "ffi" },  // LATIN SMALL LIGATURE FFI
    { 0xFB04, "ffl" },  // LATIN SMALL LIGATURE FFL
    { 0xFB1F, "afii57705" },  // HEBREW LIGATURE YIDDISH YOD YOD PATAH
    { 0xFB2A, "afii57694" },  // HEBREW LETTER SHIN WITH SHIN DOT
    { 0xFB2B, "afii57695" },  // HEBREW LETTER SHIN WITH SIN DOT
    { 0xFB35, "afii57723" },  // HEBREW LETTER VAV WITH DAGESH
    { 0xFB4B, "afii57700" },  // HEBREW LETTER VAV WITH HOLAM
    // end of stuff from glyphlist.txt
    { 0xFFFF, 0 }
};

// ---------------------------------------------------------------------
// postscript font substitution dictionary. We assume every postscript printer has at least
// Helvetica, Times, Courier and Symbol

struct psfont {
    const char *psname;
    float slant;
    float xscale;
};

static const psfont Arial[] = {
    {"Arial", 0, 84.04 },
    { "Arial-Italic", 0, 84.04 },
    { "Arial-Bold", 0, 88.65 },
    { "Arial-BoldItalic", 0, 88.65 }
};

static const psfont AvantGarde[] = {
    { "AvantGarde-Book", 0, 87.43 },
    { "AvantGarde-BookOblique", 0, 88.09 },
    { "AvantGarde-Demi", 0, 88.09 },
    { "AvantGarde-DemiOblique", 0, 87.43 },
};

static const psfont Bookman [] = {
    { "Bookman-Light", 0, 93.78 },
    { "Bookman-LightItalic", 0, 91.42 },
    { "Bookman-Demi", 0, 99.86 },
    { "Bookman-DemiItalic", 0, 101.54 }
};

static const psfont Charter [] = {
    { "CharterBT-Roman", 0, 84.04 },
    { "CharterBT-Italic", 0.0, 81.92 },
    { "CharterBT-Bold", 0, 88.99 },
    { "CharterBT-BoldItalic", 0.0, 88.20 }
};

static const psfont Courier [] = {
    { "Courier", 0, 100. },
    { "Courier-Oblique", 0, 100. },
    { "Courier-Bold", 0, 100. },
    { "Courier-BoldOblique", 0, 100. }
};

static const psfont Garamond [] = {
    { "Garamond-Antiqua", 0, 78.13 },
    { "Garamond-Kursiv", 0, 78.13 },
    { "Garamond-Halbfett", 0, 78.13 },
    { "Garamond-KursivHalbfett", 0, 78.13 }
};

static const psfont GillSans [] = { // ### some estimated value for xstretch
    { "GillSans", 0, 82 },
    { "GillSans-Italic", 0, 82 },
    { "GillSans-Bold", 0, 82 },
    { "GillSans-BoldItalic", 0, 82 }
};

static const psfont Helvetica [] = {
    { "Helvetica", 0, 84.04 },
    { "Helvetica-Oblique", 0, 84.04 },
    { "Helvetica-Bold", 0, 88.65 },
    { "Helvetica-BoldOblique", 0, 88.65 }
};

static const psfont Letter [] = {
    { "LetterGothic", 0, 83.32 },
    { "LetterGothic-Italic", 0, 83.32 },
    { "LetterGothic-Bold", 0, 83.32 },
    { "LetterGothic-Bold", 0.2, 83.32 }
};

static const psfont LucidaSans [] = {
    { "LucidaSans", 0, 94.36 },
    { "LucidaSans-Oblique", 0, 94.36 },
    { "LucidaSans-Demi", 0, 98.10 },
    { "LucidaSans-DemiOblique", 0, 98.08 }
};

static const psfont LucidaSansTT [] = {
    { "LucidaSans-Typewriter", 0, 100.50 },
    { "LucidaSans-TypewriterOblique", 0, 100.50 },
    { "LucidaSans-TypewriterBold", 0, 100.50 },
    { "LucidaSans-TypewriterBoldOblique", 0, 100.50 }
};

static const psfont LucidaBright [] = {
    { "LucidaBright", 0, 93.45 },
    { "LucidaBright-Italic", 0, 91.98 },
    { "LucidaBright-Demi", 0, 96.22 },
    { "LucidaBright-DemiItalic", 0, 96.98 }
};

static const psfont Palatino [] = {
    { "Palatino-Roman", 0, 82.45 },
    { "Palatino-Italic", 0, 76.56 },
    { "Palatino-Bold", 0, 83.49 },
    { "Palatino-BoldItalic", 0, 81.51 }
};

static const psfont Symbol [] = {
    { "Symbol", 0, 82.56 },
    { "Symbol", 0.2, 82.56 },
    { "Symbol", 0, 82.56 },
    { "Symbol", 0.2, 82.56 }
};

static const psfont Tahoma [] = {
    { "Tahoma", 0, 83.45 },
    { "Tahoma", 0.2, 83.45 },
    { "Tahoma-Bold", 0, 95.59 },
    { "Tahoma-Bold", 0.2, 95.59 }
};

static const psfont Times [] = {
    { "Times-Roman", 0, 82.45 },
    { "Times-Italic", 0, 82.45 },
    { "Times-Bold", 0, 82.45 },
    { "Times-BoldItalic", 0, 82.45 }
};

static const psfont Verdana [] = {
    { "Verdana", 0, 96.06 },
    { "Verdana-Italic", 0, 96.06 },
    { "Verdana-Bold", 0, 107.12 },
    { "Verdana-BoldItalic", 0, 107.10 }
};

static const psfont Utopia [] = { // ###
    { "Utopia-Regular", 0, 84.70 },
    { "Utopia-Regular", 0.2, 84.70 },
    { "Utopia-Bold", 0, 88.01 },
    { "Utopia-Bold", 0.2, 88.01 }
};

static const psfont * const SansSerifReplacements[] = {
    Helvetica, 0
        };
static const psfont * const SerifReplacements[] = {
    Times, 0
        };
static const psfont * const FixedReplacements[] = {
    Courier, 0
        };
static const psfont * const TahomaReplacements[] = {
    Verdana, AvantGarde, Helvetica, 0
        };
static const psfont * const VerdanaReplacements[] = {
    Tahoma, AvantGarde, Helvetica, 0
        };

static const struct {
    const char * input; // spaces are stripped in here, and everything lowercase
    const psfont * ps;
    const psfont *const * replacements;
} postscriptFonts [] = {
    { "arial", Arial, SansSerifReplacements },
    { "arialmt", Arial, SansSerifReplacements },
    { "arialunicodems", Arial, SansSerifReplacements },
    { "avantgarde", AvantGarde, SansSerifReplacements },
    { "bookman", Bookman, SerifReplacements },
    { "charter", Charter, SansSerifReplacements },
    { "bitstreamcharter", Charter, SansSerifReplacements },
        { "bitstreamcyberbit", Times, SerifReplacements }, // ###
    { "courier", Courier, 0 },
    { "couriernew", Courier, 0 },
    { "fixed", Courier, 0 },
    { "garamond", Garamond, SerifReplacements },
    { "gillsans", GillSans, SansSerifReplacements },
    { "helvetica", Helvetica, 0 },
    { "letter", Letter, FixedReplacements },
    { "lucida", LucidaSans, SansSerifReplacements },
    { "lucidasans", LucidaSans, SansSerifReplacements },
    { "lucidabright", LucidaBright, SerifReplacements },
    { "lucidasanstypewriter", LucidaSansTT, FixedReplacements },
    { "luciduxsans", LucidaSans, SansSerifReplacements },
    { "luciduxserif", LucidaBright, SerifReplacements },
    { "luciduxmono", LucidaSansTT, FixedReplacements },
    { "palatino", Palatino, SerifReplacements },
    { "symbol", Symbol, 0 },
    { "tahoma", Tahoma, TahomaReplacements },
    { "terminal", Courier, 0 },
    { "times", Times, 0 },
    { "timesnewroman", Times, 0 },
    { "verdana", Verdana, VerdanaReplacements },
    { "utopia", Utopia, SerifReplacements },
    { 0, 0, 0 }
};


// ------------------------------End of static data ----------------------------------

// make sure DSC comments are not longer than 255 chars per line.
static QString wrapDSC( const QString &str )
{
    QString dsc = str.simplifyWhiteSpace();
    const uint wrapAt = 254;
    QString wrapped;
    if ( dsc.length() < wrapAt )
	wrapped = dsc;
    else {
	wrapped = dsc.left( wrapAt );
	QString tmp = dsc.mid( wrapAt );
	while ( tmp.length() > wrapAt-3 ) {
	    wrapped += "\n%%+" + tmp.left( wrapAt-3 );
	    tmp = tmp.mid( wrapAt-3 );
	}
	wrapped += "\n%%+" + tmp;
    }
    return wrapped + "\n";
}

// ----------------------------- Internal class declarations -----------------------------

class QPSPrinterFontPrivate;

class QPSPrinterPrivate {
public:
    QPSPrinterPrivate( QPrinter *prt, int filedes );
    ~QPSPrinterPrivate();

    void matrixSetup( QPainter * );
    void clippingSetup( QPainter * );
    void setClippingOff( QPainter * );
    void orientationSetup();
    void resetDrawingTools( QPainter * );
    void emitHeader( bool finished );
    void setFont( const QFont &, int script );
    void drawImage( QPainter *, float x, float y, float w, float h, const QImage & );
    void initPage( QPainter *paint );
    void flushPage( bool last = FALSE );

    QPrinter   *printer;
    int         pageCount;
    bool        dirtyMatrix;
    bool        dirtyNewPage;
    bool        epsf;
    QString     fontsUsed;

    // outstream is the stream the build up pages are copied to. It points to buffer
    // at the start, and is reset to use the outDevice after emitHeader has been called.
    QTextStream outStream;

    // stores the descriptions of the first pages. outStream operates on this buffer
    // until we call emitHeader
    QBuffer *buffer;
    int pagesInBuffer;

    // the device the output is in the end streamed to.
    QIODevice * outDevice;
    int fd;

    // buffer for the current page. Needed becaus we might have page fonts.
    QBuffer *pageBuffer;
    QTextStream pageStream;

    QDict<QString> headerFontNames;
    QDict<QString> pageFontNames;
    QDict<QPSPrinterFontPrivate> fonts;
    QPSPrinterFontPrivate *currentFontFile;
    int headerFontNumber;
    int pageFontNumber;
    QBuffer * fontBuffer;
    QTextStream fontStream;
    bool dirtyClipping;
    bool firstClipOnPage;
    QRect boundingBox;
    QImage * savedImage;
    QPen cpen;
    QBrush cbrush;
    bool dirtypen;
    bool dirtybrush;
    QColor bkColor;
    bool dirtyBkColor;
    Qt::BGMode bkMode;
    bool dirtyBkMode;
    QTextCodec * currentFontCodec;
    QString currentFont;
    QFontMetrics fm;
    int textY;
    QFont currentUsed;
    int scriptUsed;
    QFont currentSet;
    float scale;
    QStringList fontpath;
};


class QPSPrinterFontPrivate {
public:
    QPSPrinterFontPrivate();
    virtual ~QPSPrinterFontPrivate() {}
    virtual QString postScriptFontName() { return psname; }
    virtual QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                             QPSPrinterPrivate *d );
    virtual void download(QTextStream& s, bool global);
    virtual void drawText( QTextStream &stream, uint spaces, const QPoint &p,
                           const QString &text, QPSPrinterPrivate *d, QPainter *paint);
    virtual unsigned short mapUnicode( unsigned short unicode );
    void downloadMapping( QTextStream &s, bool global );
    virtual QString glyphName(unsigned short glyphindex);
    virtual void restore();

    virtual unsigned short unicode_for_glyph(int glyphindex) { return glyphindex; }
    virtual unsigned short glyph_for_unicode(unsigned short unicode) { return unicode; }
    unsigned short insertIntoSubset( unsigned short unicode );
    virtual bool embedded() { return FALSE; }

protected:
    QString psname;
    QStringList replacementList;

    QMap<unsigned short, unsigned short> subset;      // unicode subset in the global font
    QMap<unsigned short, unsigned short> page_subset; // subset added in this page
    int subsetCount;
    int pageSubsetCount;
    bool           global_dict;
  bool           downloaded;
};

// ------------------- end of class declarations ---------------------------

// --------------------------------------------------------------
//   beginning of font related methods
// --------------------------------------------------------------


static int getPsFontType( const QFont &f )
{
    int weight = f.weight();
    bool italic = f.italic();

    int type = 0; // used to look up in the psname array
  // get the right modification, or build something
  if ( weight > QFont::Normal && italic )
      type = 3;
  else if ( weight > QFont::Normal )
      type = 2;
  else if ( italic )
      type = 1;
  return type;
}

static int addPsFontNameExtension( const QFont &f, QString &ps, const psfont *psf = 0 )
{
    int type = getPsFontType( f );

  if ( psf ) {
      ps = QString::fromLatin1( psf[type].psname );
  } else {
      switch ( type ) {
          case 1:
              ps.append( QString::fromLatin1("-Italic") );
              break;
          case 2:
              ps.append( QString::fromLatin1("-Bold") );
              break;
          case 3:
              ps.append( QString::fromLatin1("-BoldItalic") );
              break;
          case 0:
          default:
              break;
      }
  }
  return type;
}

/* static  */ // doesn't work because we need the friend decl from qfontdatabase
QString qt_makePSFontName( const QFont &f, int *listpos, int *ftype)
{
  QString ps;
  int i;

  QString familyName = f.family();
  QString foundry, family;
  QFontDatabase::parseFontName( familyName, foundry, family );
  family = family.lower();

  // try to make a "good" postscript name
  ps = family.simplifyWhiteSpace();
  i = 0;
  while( (unsigned int)i < ps.length() ) {
    if ( i == 0 || ps[i-1] == ' ' ) {
      ps[i] = ps[i].upper();
      if ( i )
        ps.remove( i-1, 1 );
      else
        i++;
    } else {
      i++;
    }
  }
  
  if ( ps.isEmpty() )
      ps = "Helvetica";

  // see if the table has a better name
  i = 0;
  QString lowerName = ps.lower();
  while( postscriptFonts[i].input &&
         postscriptFonts[i].input != lowerName )
    i++;
  const psfont *psf = postscriptFonts[i].ps;

  int type = addPsFontNameExtension( f, ps, psf );

  if ( listpos )
      *listpos = i;
  if ( ftype )
      *ftype = type;
  return ps;
}

static QString makePSFontName( const QFont &f, int *listpos = 0, int *ftype = 0 )
{
    return qt_makePSFontName( f, listpos, ftype );
}

static void appendReplacements( QStringList &list, const psfont * const * replacements, int type, float xscale = 100. )
{
    // iterate through the replacement fonts
    while ( *replacements ) {
        const psfont *psf = *replacements;
        QString ps = "[ /" + QString::fromLatin1( psf[type].psname ) + " " + QString::number( xscale / psf[type].xscale ) + " " +
             QString::number( psf[type].slant ) + " ]";
        list.append( ps );
        ++replacements;
    }
}

static QStringList makePSFontNameList( const QFont &f, const QString &psname = QString::null, bool useNameForLookup = FALSE )
{
    int i;
    int type;
    QStringList list;
    QString ps = psname;

    if ( !ps.isEmpty() && !useNameForLookup ) {
        QString best = "[ /" + ps + " 1.0 0.0 ]";
        list.append( best );
    }

    ps = makePSFontName( f, &i, &type );

    const psfont *psf = postscriptFonts[i].ps;
    const psfont * const * replacements = postscriptFonts[i].replacements;
    float xscale = 100;
    if ( psf ) {
        // xscale for the "right" font is always 1. We scale the replacements...
        xscale = psf->xscale;
        ps = "[ /" + QString::fromLatin1( psf[type].psname ) + " 1.0 " +
             QString::number( psf[type].slant ) + " ]";
    } else {
        ps = "[ /" + ps + " 1.0 0.0 ]";
        // only add default replacement fonts in case this font was unknown.
        QFontInfo fi( f );
        if ( fi.fixedPitch() ) {
            replacements = FixedReplacements;
        } else {
            replacements = SansSerifReplacements;
            // 100 is courier, but most fonts are not as wide as courier. Using 100
            // here would make letters overlap for some fonts. This value is empirical.
            xscale = 83;
        }
    }
    list.append( ps );

    if ( replacements )
        appendReplacements( list, replacements, type, xscale);
    return list;
}

static void emitPSFontNameList( QTextStream &s, const QString &psname, const QStringList &list )
{
    s << "/" << psname << "List [\n";
    s << list.join("\n  ");
    s << "\n] d\n";
}


static QString *fixed_ps_header = 0;

static void cleanup()
{
    delete fixed_ps_header;
    fixed_ps_header = 0;
}


static QString wordwrap( const QString & s )
{
    QString result;

    int ip; // input pointer
    int oll; // output line length
    int cils, cols; // canidate input line start, -o-
    bool needws; // need to insert ws before next character
    bool havews; // have just inserted something like ws

    ip = 0;
    oll = 0;
    cils = cols = 0;
    havews = FALSE;
    needws = FALSE;
    while( ip < (int)s.length() ) {
        if ( oll > 79 && cils > 0 ) {
            result.truncate( cols );
            ip = cils;
            result += '\n';
            oll = 0;
            cils = 0;
            havews = TRUE;
        }
        if ( havews && oll > 0 ) {
            cils = ip;
            cols = result.length();
        }
        if ( isspace( (uchar) s[ip] ) ) {
            if ( !havews )
                needws = TRUE;
            cils = ip+1;
            cols = result.length();
        } else if ( s[ip] == '/'  || s[ip] == '{' || s[ip] == '}' ||
                    s[ip] == '[' || s[ip] == ']' ) {
            havews = s[ip] != '/';
            needws = FALSE;
            cils = ip;
            cols = result.length();
            result += s[ip];
            oll++;
        } else {
            if ( needws ) {
                cols = result.length();
                cils = ip;
                result += ' ';
                oll++;
                needws = FALSE;
            }
            result += s[ip];
            oll++;
            havews = FALSE;
        }
        ip++;
    }
    return result;
}


static void makeFixedStrings()
{
    if ( fixed_ps_header )
        return;
    qAddPostRoutine( cleanup );

    fixed_ps_header = new QString;
    const char * const * headerLine = ps_header;
    while ( *headerLine ) {
        fixed_ps_header->append( QString::fromLatin1(*headerLine++) );
        fixed_ps_header->append( '\n' );
    }

    *fixed_ps_header = wordwrap( *fixed_ps_header );
}

static float pointSize( const QFont &f, float scale )
{
    float psize;
    if ( f.pointSize() != -1 )
	psize = f.pointSize()/scale;
    else
	psize = f.pixelSize();
    return psize;
}


// ========================== FONT CLASSES  ===============


QPSPrinterFontPrivate::QPSPrinterFontPrivate()
{
    global_dict = FALSE;
    downloaded  = FALSE;
    // map 0 to .notdef
    subset.insert( 0, 0 );
    subsetCount = 1;
    pageSubsetCount = 0;
}

unsigned short QPSPrinterFontPrivate::insertIntoSubset( unsigned short u )
{
    unsigned short retval = 0;
    if ( subset.find(u) == subset.end() ) {
        if ( !downloaded ) { // we need to add to the page subset
            subset.insert( u, subsetCount ); // mark it as used
            //printf("GLOBAL SUBSET ADDED %04x = %04x\n",u, subsetCount);
            retval = subsetCount;
            subsetCount++;
        } else if ( page_subset.find(u) == page_subset.end() ) {
            page_subset.insert( u, pageSubsetCount ); // mark it as used
            //printf("PAGE SUBSET ADDED %04x = %04x\n",u, pageSubsetCount);
            retval = pageSubsetCount + (subsetCount/256 + 1) * 256;
            pageSubsetCount++;
        }
    } else {
        qWarning("QPSPrinterFont::internal error");
    }
    return retval;
}

void QPSPrinterFontPrivate::restore()
{
    page_subset.clear();
    pageSubsetCount = 0;
    //qDebug("restore for font %s\n",psname.latin1());
}

void QPSPrinterFontPrivate::drawText( QTextStream &stream, uint spaces, const QPoint &p,
                                 const QString &text, QPSPrinterPrivate *d, QPainter *paint)
{
    if ( text.length() == 0 )
        return;

    float x = p.x();
    if ( spaces > 0 )
        x += spaces * d->fm.width( ' ' );

    int y = p.y();
    if ( y != d->textY || d->textY == 0 )
        stream << y << " Y";
    d->textY = y;

    float w = d->fm.width( text );
    stream << "<";
    QString s;
    int i;
    for (i=0; i < (int) text.length(); i++) {
        ushort u = mapUnicode(text.at(i).unicode());
        ushort high = u / 256;
        ushort low  = u % 256;
        stream << s.sprintf("%02x",high);
        stream << s.sprintf("%02x",low);
        //printf("i=%d high=%02x low=%02x unicode=%04x\n",i,high,low,u);
    }
    stream << ">";

    stream << w << " " << x;

    if ( paint->font().underline() )
        stream << ' ' << y + d->fm.underlinePos() + d->fm.lineWidth()
               << " " << d->fm.lineWidth() << " Tl";
    if ( paint->font().strikeOut() )
        stream << ' ' << y + d->fm.strikeOutPos()
               << " " << d->fm.lineWidth() << " Tl";
    stream << " T\n";

}


QString QPSPrinterFontPrivate::defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                                   QPSPrinterPrivate *d )
{
    QString fontName;
    fontName.sprintf( "/%s-Uni", ps.latin1());

    if ( d->buffer ) {
        ++d->headerFontNumber;
        d->fontStream << "/F" << d->headerFontNumber << " "
                      << pointSize( f, d->scale ) << fontName << " DF\n";
        fontName.sprintf( "F%d", d->headerFontNumber );
        d->headerFontNames.insert( key, new QString( fontName ) );
    } else {
        ++d->pageFontNumber;
        stream << "/F" << d->pageFontNumber << " "
               << pointSize( f, d->scale ) << fontName << " DF\n";
        fontName.sprintf( "F%d", d->pageFontNumber );
        d->pageFontNames.insert( key, new QString( fontName ) );
    }
    return fontName;
}

unsigned short QPSPrinterFontPrivate::mapUnicode( unsigned short unicode )
{
    QMap<unsigned short, unsigned short>::iterator res;
    res = subset.find( unicode );
    unsigned short offset = 0;
    bool found = FALSE;
    if ( res != subset.end() ) {
        found = TRUE;
    } else {
        if ( downloaded ) {
            res = page_subset.find( unicode );
            offset = (subsetCount/256 + 1) * 256;
            if ( res != page_subset.end() )
                found = TRUE;
        }
    }
    if ( !found ) {
        return insertIntoSubset( unicode );
    }
    //qDebug("mapping unicode %x to %x", unicode, offset+*res);
    return offset + *res;
}

QString QPSPrinterFontPrivate::glyphName( unsigned short glyphindex )
{
    QString glyphname;
    int l = 0;
    unsigned short unicode = unicode_for_glyph( glyphindex );
    if ( !unicode && glyphindex ) {
	glyphname.sprintf("G%04x", glyphindex );
    } else {
	while( unicodetoglyph[l].u < unicode )
	    l++;
	if ( unicodetoglyph[l].u == unicode )
	    glyphname = unicodetoglyph[l].g;
	else
	    glyphname.sprintf("U%04x", unicode);
    }
    return glyphname;
}

void QPSPrinterFontPrivate::download(QTextStream &s, bool global)
{
    //printf("defining mapping for printer font %s\n",psname.latin1());
    downloadMapping( s, global );
}

void QPSPrinterFontPrivate::downloadMapping( QTextStream &s, bool global )
{
    int rangeOffset = 0;
    int numRanges = subsetCount/256 + 1;
    int range;
    QMap<unsigned short, unsigned short> *subsetDict = &subset;
    if ( !global ) {
        rangeOffset = numRanges;
        numRanges = pageSubsetCount/256 + 1;
        subsetDict = &page_subset;
    }
    // build up inverse table
    unsigned short *inverse = new unsigned short[numRanges * 256];
    memset( inverse, 0, numRanges * 256 * sizeof( unsigned short ) );

    QMap<unsigned short, unsigned short>::iterator it;
    for ( it = subsetDict->begin(); it != subsetDict->end(); ++it) {
        const unsigned short &mapped = *it;
        inverse[mapped] = it.key();
    }

    QString vector;
    QString glyphname;

    for (range=0; range < numRanges; range++) {
        //printf("outputing range %04x\n",range*256);
        vector.sprintf("%% Font Page %04x\n",range + rangeOffset);
        QString dummy;
        vector += "/";
        vector += psname;
        vector += dummy.sprintf("-ENC-%02x [\n",range + rangeOffset);

        QString line;
        for(int k=0; k<256; k++ ) {
            int c = range*256 + k;
            unsigned short unicode = inverse[c];
            glyphname = glyphName( glyph_for_unicode(unicode) );
            if ( line.length() + glyphname.length() > 76 ) {
                vector += line + "\n";
                line = "";
            }
            line += "/" + glyphname;
        }
        vector += line + "] def\n";
        s << vector;
    }

    delete [] inverse;

    // DEFINE BASE FONTS

    for (range=0; range < numRanges; range++) {
        QString dummy;
        s << "/";
        s << psname;
        s << "-Uni-";
        s << dummy.sprintf("%02x",range + rangeOffset);
        s << " ";
        s << psname;
        s << "-ENC-";
        s << dummy.sprintf("%02x",range + rangeOffset);
        if ( embedded() ) {
            s << " /";
            s << psname;
            s << " MFEmb\n";
        } else {
            s << " " << psname << "List";
            s << " MF\n";
        }
    }

    // === write header ===
    //   int VMMin;
    //   int VMMax;

    s << wrapDSC( "%%BeginFont: " + psname )
      << "%!PS-AdobeFont-1.0 Composite Font\n"
      << wrapDSC( "%%FontName: " + psname + "-Uni" )
      << "%%Creator: Composite font created by Qt\n";

    /* Start the dictionary which will eventually */
    /* become the font. */
    s << "25 dict begin\n"; // need to verify. Sivan

    s << "/FontName /";
    s << psname;
    s << "-Uni";
    s << " def\n";
    s << "/PaintType 0 def\n";

    // This is concatenated with the base fonts, so it should perform
    // no transformation. Sivan
    s << "/FontMatrix[1 0 0 1 0 0]def\n";

    s << "/FontType ";
    s << 0;
    s << " def\n";

    // now come composite font structures
    // FMapTypes:
    // 2: 8/8, 8 bits select the font, 8 the glyph

    s << "/FMapType 2 def\n";

    // The encoding in a composite font is used for indirection.
    // Every char is split into a font-number and a character-selector.
    // PostScript prints glyph number character-selector from the font
    // FDepVector[ Encoding[ font-number ] ].

    s << "/Encoding [";
    for (range=0; range < rangeOffset + numRanges; range++) {
        if (range % 16 == 0)
            s << "\n";
        else
            s << " ";
        s << range;
    }
    s << "]def\n";

  // Descendent fonts

    s << "/FDepVector [\n";
    for (range=0; range < rangeOffset + numRanges; range++) {
        QString dummy;
        s << "/";
        s << psname;
        s << "-Uni-";
        s << dummy.sprintf("%02x",range);
        s << " findfont\n";
    }
    s << "]def\n";

  // === trailer ===

    s << "FontName currentdict end definefont pop\n";
    s << "%%EndFont\n";
}


// ================== TTF ====================

typedef Q_UINT8  BYTE;
typedef Q_UINT16 USHORT;
typedef Q_UINT16 uFWord;
typedef Q_INT16 SHORT;
typedef Q_INT16 FWord;
typedef Q_UINT32   ULONG;
typedef Q_INT32  FIXED;

typedef struct {
  Q_INT16 whole;
  Q_UINT16 fraction;
} Fixed; // 16.16 bit fixed-point number

static float f2dot14( ushort s )
{
    float f = ((float)( s & 0x3fff ))/ 16384.;
    f += (s & 0x8000) ? ( (s & 0x4000) ? -1 : -2 ) : ( (s & 0x4000) ? 1 : 0 );
    return f;
}

typedef struct {
  int*    epts_ctr;                     /* array of contour endpoints */
  int     num_pts, num_ctr;             /* number of points, number of coutours */
  FWord*  xcoor, *ycoor;                /* arrays of x and y coordinates */
  BYTE*   tt_flags;                     /* array of TrueType flags */
  double* area_ctr;
  char*   check_ctr;
  int*    ctrset;               /* in contour index followed by out contour index */
} charproc_data;


class QPSPrinterFontTTF
  : public QPSPrinterFontPrivate {
public:
  QPSPrinterFontTTF(const QFont &f, QByteArray& data);
  virtual void    download(QTextStream& s, bool global);
   //  virtual ~QPSPrinterFontTTF();

      virtual bool embedded() { return TRUE; }
private:
  QByteArray     data;
  QMemArray<ushort> uni2glyph; // to speed up lookups
  QMemArray<ushort> glyph2uni; // to speed up lookups
  bool           defective; // if we can't process this file

  BYTE*   getTable(const char *);
  QString glyphName(unsigned short glyphindex);
      void uni2glyphSetup();
      unsigned short unicode_for_glyph(int glyphindex);
      unsigned short glyph_for_unicode(unsigned short unicode);
  int   topost(FWord x) { return (int)( ((int)(x) * 1000 + HUPM) / unitsPerEm ); }

#ifdef Q_PRINTER_USE_TYPE42
  void sfnts_pputBYTE(BYTE n,QTextStream& s,
                                    int& string_len, int& line_len, bool& in_string);
  void sfnts_pputUSHORT(USHORT n,QTextStream& s,
                                      int& string_len, int& line_len, bool& in_string);
  void sfnts_pputULONG(ULONG n,QTextStream& s,
                                     int& string_len, int& line_len, bool& in_string);
  void sfnts_end_string(QTextStream& s,
                                      int& string_len, int& line_len, bool& in_string);
  void sfnts_new_table(ULONG length,QTextStream& s,
                                     int& string_len, int& line_len, bool& in_string);
  void sfnts_glyf_table(ULONG oldoffset,
                                      ULONG correct_total_length,
                                      QTextStream& s,
                                      int& string_len, int& line_len, bool& in_string);
  void download_sfnts(QTextStream& s);
#endif

  void subsetGlyph(int charindex,bool* glyphset);

  void charproc(int charindex, QTextStream& s);
  BYTE* charprocFindGlyphData(int charindex);
  void charprocComposite(BYTE *glyph, QTextStream& s);
  void charprocLoad(BYTE *glyph, charproc_data* cd);

  int target_type;                      /* 42 or 3 */

  int numTables;                        /* number of tables present */
  QString PostName;                     /* Font's PostScript name */
  QString FullName;                     /* Font's full name */
  QString FamilyName;                   /* Font's family name */
  QString Style;                        /* Font's style string */
  QString Copyright;                    /* Font's copyright string */
  QString Version;                      /* Font's version string */
  QString Trademark;                    /* Font's trademark string */
  int llx,lly,urx,ury;          /* bounding box */

  Fixed TTVersion;                      /* Truetype version number from offset table */
  Fixed MfrRevision;                    /* Revision number of this font */

  BYTE *offset_table;           /* Offset table in memory */
  BYTE *post_table;                     /* 'post' table in memory */

  BYTE *loca_table;                     /* 'loca' table in memory */
  BYTE *glyf_table;                     /* 'glyf' table in memory */
  BYTE *hmtx_table;                     /* 'hmtx' table in memory */

  USHORT numberOfHMetrics;
  int unitsPerEm;                       /* unitsPerEm converted to int */
  int HUPM;                             /* half of above */

  int numGlyphs;                        /* from 'post' table */

  int indexToLocFormat;         /* short or long offsets */

};


static ULONG getULONG(BYTE *p)
{
  int x;
  ULONG val=0;

  for(x=0; x<4; x++) {
    val *= 0x100;
    val += p[x];
  }

  return val;
}

static USHORT getUSHORT(BYTE *p)
{
  int x;
  USHORT val=0;

  for(x=0; x<2; x++) {
    val *= 0x100;
    val += p[x];
  }

  return val;
}

static Fixed getFixed(BYTE *s)
{
  Fixed val={0,0};

  val.whole = ((s[0] * 256) + s[1]);
  val.fraction = ((s[2] * 256) + s[3]);

  return val;
}

static FWord getFWord(BYTE* s)  { return (FWord)  getUSHORT(s); }
static uFWord getuFWord(BYTE* s) { return (uFWord) getUSHORT(s); }
static SHORT getSHORT(BYTE* s)  { return (SHORT)  getUSHORT(s); }

#if 0
static const char * const Apple_CharStrings[]={
  ".notdef",".null","nonmarkingreturn","space","exclam","quotedbl","numbersign",
  "dollar","percent","ampersand","quotesingle","parenleft","parenright",
  "asterisk","plus", "comma","hyphen","period","slash","zero","one","two",
  "three","four","five","six","seven","eight","nine","colon","semicolon",
  "less","equal","greater","question","at","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",
  "bracketleft","backslash","bracketright","asciicircum","underscore","grave",
  "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","braceleft","bar","braceright","asciitilde",
  "Adieresis","Aring","Ccedilla","Eacute","Ntilde","Odieresis","Udieresis",
  "aacute","agrave","acircumflex","adieresis","atilde","aring","ccedilla",
  "eacute","egrave","ecircumflex","edieresis","iacute","igrave","icircumflex",
  "idieresis","ntilde","oacute","ograve","ocircumflex","odieresis","otilde",
  "uacute","ugrave","ucircumflex","udieresis","dagger","degree","cent",
  "sterling","section","bullet","paragraph","germandbls","registered",
  "copyright","trademark","acute","dieresis","notequal","AE","Oslash",
  "infinity","plusminus","lessequal","greaterequal","yen","mu","partialdiff",
  "summation","product","pi","integral","ordfeminine","ordmasculine","Omega",
  "ae","oslash","questiondown","exclamdown","logicalnot","radical","florin",
  "approxequal","Delta","guillemotleft","guillemotright","ellipsis",
  "nobreakspace","Agrave","Atilde","Otilde","OE","oe","endash","emdash",
  "quotedblleft","quotedblright","quoteleft","quoteright","divide","lozenge",
  "ydieresis","Ydieresis","fraction","currency","guilsinglleft","guilsinglright",
  "fi","fl","daggerdbl","periodcentered","quotesinglbase","quotedblbase",
  "perthousand","Acircumflex","Ecircumflex","Aacute","Edieresis","Egrave",
  "Iacute","Icircumflex","Idieresis","Igrave","Oacute","Ocircumflex","apple",
  "Ograve","Uacute","Ucircumflex","Ugrave","dotlessi","circumflex","tilde",
  "macron","breve","dotaccent","ring","cedilla","hungarumlaut","ogonek","caron",
  "Lslash","lslash","Scaron","scaron","Zcaron","zcaron","brokenbar","Eth","eth",
  "Yacute","yacute","Thorn","thorn","minus","multiply","onesuperior",
  "twosuperior","threesuperior","onehalf","onequarter","threequarters","franc",
  "Gbreve","gbreve","Idot","Scedilla","scedilla","Cacute","cacute","Ccaron",
  "ccaron","dmacron","markingspace","capslock","shift","propeller","enter",
  "markingtabrtol","markingtabltor","control","markingdeleteltor",
  "markingdeletertol","option","escape","parbreakltor","parbreakrtol",
  "newpage","checkmark","linebreakltor","linebreakrtol","markingnobreakspace",
  "diamond","appleoutline"};
#endif


#ifdef Q_PRINTER_USE_TYPE42
#ifndef _OS_OSF_
extern "C" char* getenv(char*);
#endif
#endif

QPSPrinterFontTTF::QPSPrinterFontTTF(const QFont &f, QByteArray& d)
{
  data = d;
  defective = FALSE;

  BYTE *ptr;

  target_type = 3;  // will work on any printer
  //target_type = 42; // works with gs, faster, better quality

#ifdef Q_PRINTER_USE_TYPE42
  char* environment_preference = getenv("QT_TTFTOPS");
  if (environment_preference) {
    if (QString(environment_preference) == "42")
      target_type = 42;
    else if (QString(environment_preference) == "3")
      target_type = 3;
    else
      qWarning("The value of QT_TTFTOPS must be 42 or 3");
  }
#endif
  offset_table = (unsigned char*) data.data(); /* first 12 bytes */

  /* Determine how many directory entries there are. */
  numTables = getUSHORT( offset_table + 4 );

  /* Extract information from the "Offset" table. */
  TTVersion = getFixed( offset_table );

  /* Load the "head" table and extract information from it. */
  ptr = getTable("head");
  if ( !ptr ) {
      defective = TRUE;
      return;
  }
  MfrRevision = getFixed( ptr + 4 );            /* font revision number */
  unitsPerEm = getUSHORT( ptr + 18 );
  HUPM = unitsPerEm / 2;
#ifdef DEBUG_TRUETYPE
  printf("unitsPerEm=%d",(int)unitsPerEm);
#endif
  llx = topost( getFWord( ptr + 36 ) );         /* bounding box info */
  lly = topost( getFWord( ptr + 38 ) );
  urx = topost( getFWord( ptr + 40 ) );
  ury = topost( getFWord( ptr + 42 ) );
  indexToLocFormat = getSHORT( ptr + 50 );      /* size of 'loca' data */
  if(indexToLocFormat != 0 && indexToLocFormat != 1) {
    qWarning("TrueType font is unusable because indexToLocFormat != 0");
    defective = TRUE;
    return;
  }
  if( getSHORT(ptr+52) != 0 ) {
    qWarning("TrueType font is unusable because glyphDataFormat != 0");
    defective = TRUE;
    return;
  }

  // === Load information from the "name" table ===

  /* Set default values to avoid future references to */
  /* undefined pointers. */

  psname = FullName = FamilyName = Version = Style = "unknown";
  Copyright = "No copyright notice";
  Trademark = "No trademark notice";

  BYTE* table_ptr = getTable("name");           /* pointer to table */
  if ( !table_ptr ) {
      defective = TRUE;
      return;
  }
  int numrecords = getUSHORT( table_ptr + 2 );  /* number of names */
  char* strings = (char *)table_ptr + getUSHORT( table_ptr + 4 ); /* start of string storage */

  BYTE* ptr2 = table_ptr + 6;
  for(int x=0; x < numrecords; x++,ptr2+=12) {
      int platform = getUSHORT(ptr2);
    //int encoding = getUSHORT(ptr2+2);
    //int language = getUSHORT(ptr2+4);
    int nameid   = getUSHORT(ptr2+6);
    int length   = getUSHORT(ptr2+8);
    int offset   = getUSHORT(ptr2+10);

    if( platform == 1 && nameid == 0 )
      Copyright.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 1 )
      FamilyName.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 2 )
      Style.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 4 )
      FullName.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 5 )
      Version.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 6 )
      psname.setLatin1(strings+offset,length);

    if( platform == 1 && nameid == 7 )
      Trademark.setLatin1(strings+offset,length);

  }

  //read_cmap(font);

  /* We need to have the PostScript table around. */

  post_table = getTable("post");
#if 0
  if ( post_table ) {
      Fixed post_format = getFixed( post_table );

      if( post_format.whole != 2 || post_format.fraction != 0 ) {
          qWarning("TrueType font does not have a format 2.0 'post' table");
          QString dummy;
          qWarning(dummy.sprintf("post format is %d.%d",post_format.whole,post_format.fraction));
          // Sivan Feb 2001: no longer defective.
          // defective = true;
      }
  }
#endif  
  BYTE *maxp = getTable("maxp");
  if ( !maxp ) {
      defective = TRUE;
      return;
  }
  numGlyphs = getUSHORT( maxp + 4 );
//  qDebug("number of glyphs is %d", numGlyphs);
  replacementList = makePSFontNameList( f, psname );
  uni2glyphSetup();
}

void QPSPrinterFontTTF::download(QTextStream& s,bool global)
{
    //qDebug("target type=%d", target_type);
    global_dict = global;
    QMap<unsigned short, unsigned short> *subsetDict = &subset;
    if ( !global )
        subsetDict = &page_subset;

    downloaded  = TRUE;

    emitPSFontNameList( s, psname, replacementList );
    if (defective) {
        s << "% Font ";
        s << FullName;
        s << " cannot be downloaded\n";
        return;
    }

    // === write header ===
    int VMMin;
    int VMMax;

    s << wrapDSC( "%%BeginFont: " + FullName );
    if( target_type == 42 ) {
        s << "%!PS-TrueTypeFont-"
          << TTVersion.whole
          << "."
          << TTVersion.fraction
          << "-"
          << MfrRevision.whole
          << "."
          << MfrRevision.fraction
          << "\n";
    } else {
        /* If it is not a Type 42 font, we will use a different format. */
        s << "%!PS-Adobe-3.0 Resource-Font\n";
    }     /* See RBIIp 641 */

    if( Copyright != (char*)NULL ) {
        s << wrapDSC( "%%Copyright: " + Copyright );
    }

    if( target_type == 42 )
        s << "%%Creator: Converted from TrueType to type 42 by Qt\n";
    else
        s << "%%Creator: Converted from TrueType by Qt\n";

    /* If VM usage information is available, print it. */
    if( target_type == 42 && post_table)
    {
        VMMin = (int)getULONG( post_table + 16 );
        VMMax = (int)getULONG( post_table + 20 );
        if( VMMin > 0 && VMMax > 0 )
            s << "%%VMUsage: " << VMMin << " " << VMMax << "\n";
    }

    /* Start the dictionary which will eventually */
    /* become the font. */
    if( target_type != 3 ) {
        s << "15 dict begin\n";
    }  else {
        s << "25 dict begin\n";

        /* Type 3 fonts will need some subroutines here. */
        s << "/_d{bind def}bind def\n";
        s << "/_m{moveto}_d\n";
        s << "/_l{lineto}_d\n";
        s << "/_cl{closepath eofill}_d\n";
        s << "/_c{curveto}_d\n";
        s << "/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d\n";
        s << "/_e{exec}_d\n";
    }

    s << "/FontName /";
    s << psname;
    s << " def\n";
    s << "/PaintType 0 def\n";

    if(target_type == 42)
        s << "/FontMatrix[1 0 0 1 0 0]def\n";
    else
        s << "/FontMatrix[.001 0 0 .001 0 0]def\n";

    s << "/FontBBox[";
    s<< llx;
    s << " ";
    s<< lly;
    s << " ";
    s<< urx;
    s << " ";
    s<< ury;
    s << "]def\n";

    s << "/FontType ";
    s<< target_type;
    s << " def\n";

    // === write encoding ===

    s << "/Encoding StandardEncoding def\n";

    // === write fontinfo dict ===

    /* We create a sub dictionary named "FontInfo" where we */
    /* store information which though it is not used by the */
    /* interpreter, is useful to some programs which will */
    /* be printing with the font. */
    s << "/FontInfo 10 dict dup begin\n";

    /* These names come from the TrueType font's "name" table. */
    s << "/FamilyName (";
    s << FamilyName;
    s << ") def\n";

    s << "/FullName (";
    s << FullName;
    s << ") def\n";

    s << "/Notice (";
    s << Copyright;
    s << " ";
    s << Trademark;
    s << ") def\n";

    /* This information is not quite correct. */
    s << "/Weight (";
    s << Style;
    s << ") def\n";

    /* Some fonts have this as "version". */
    s << "/Version (";
    s << Version;
    s << ") def\n";

    /* Some information from the "post" table. */
    if ( post_table ) {
        Fixed ItalicAngle = getFixed( post_table + 4 );
        s << "/ItalicAngle ";
        s << ItalicAngle.whole;
        s << ".";
        s << ItalicAngle.fraction;
        s << " def\n";

        s << "/isFixedPitch ";
        s << (getULONG( post_table + 12 ) ? "true" : "false" );
        s << " def\n";

        s << "/UnderlinePosition ";
        s << (int)getFWord( post_table + 8 );
        s << " def\n";

        s << "/UnderlineThickness ";
        s << (int)getFWord( post_table + 10 );
        s << " def\n";
    }
    s << "end readonly def\n";

#ifdef Q_PRINTER_USE_TYPE42
    /* If we are generating a type 42 font, */
    /* emmit the sfnts array. */
    if( target_type == 42 )
        download_sfnts(s);
#endif
    /* If we are generating a Type 3 font, we will need to */
    /* have the 'loca' and 'glyf' tables arround while */
    /* we are generating the CharStrings. */
    if(target_type == 3)
    {
        BYTE *ptr;                        /* We need only one value */
        ptr = getTable("hhea");
        numberOfHMetrics = getUSHORT(ptr + 34);

        loca_table = getTable("loca");
        glyf_table = getTable("glyf");
        hmtx_table = getTable("hmtx");
    }

    // ===  CharStrings array ===

    // subsetting. We turn a char subset into a glyph subset
    // and we mark as used the base glyphs of used composite glyphs.

    bool glyphset[65536];
    for(int c=0; c < 65536; c++)
        glyphset[c] = FALSE;
    glyphset[0] = TRUE; // always output .notdef

    QMap<unsigned short, unsigned short>::iterator it;
    for( it = subsetDict->begin(); it != subsetDict->end(); ++it ) {
        unsigned short c = it.key();
        if ( glyph_for_unicode( c ) ) {
            subsetGlyph( glyph_for_unicode( c ), glyphset );
        }
    }
    int nGlyphs = numGlyphs;
    if ( target_type == 3 ) {
        nGlyphs = 0;;
        for(int c=0; c < 65536; c++)
            if ( glyphset[c] ) nGlyphs++;
    }

    s << "/CharStrings ";
    s << nGlyphs;
    s << " dict dup begin\n";

    // Emmit one key-value pair for each glyph.
    for(int x=0; x < 65536; x++) {
        if(target_type == 42) {
            s << "/";
            s << glyphName( x );
            s << " ";
            s << x;
            s << " def\n";
        } else { /* type 3 */
            if (!glyphset[x]) continue;

	    //qDebug("emitting charproc for glyph %d, name=%s", x, glyphName(x).latin1() );
            s << "/";
            s << glyphName( x );
            s << "{";
            charproc(x,s);
            s << "}_d\n";     /* "} bind def" */
        }
    }

    s << "end readonly def\n";

    // === trailer ===

    /* If we are generating a type 3 font, we need to provide */
    /* a BuildGlyph and BuildChar proceedures. */
    if( target_type == 3 ) {
        s << "\n";

        s << "/BuildGlyph\n";
        s << " {exch begin\n";              /* start font dictionary */
        s << " CharStrings exch\n";
        s << " 2 copy known not{pop /.notdef}if\n";
        s << " true 3 1 roll get exec\n";
        s << " end}_d\n";

        s << "\n";

        /* This proceedure is for compatiblity with */
        /* level 1 interpreters. */
        s << "/BuildChar {\n";
        s << " 1 index /Encoding get exch get\n";
        s << " 1 index /BuildGlyph get exec\n";
        s << "}_d\n";

        s << "\n";

    }

    /* If we are generating a type 42 font, we need to check to see */
    /* if this PostScript interpreter understands type 42 fonts.  If */
    /* it doesn't, we will hope that the Apple TrueType rasterizer */
    /* has been loaded and we will adjust the font accordingly. */
    /* I found out how to do this by examining a TrueType font */
    /* generated by a Macintosh.  That is where the TrueType interpreter */
    /* setup instructions and part of BuildGlyph came from. */
    else if( target_type == 42 ) {
        s << "\n";

        /* If we have no "resourcestatus" command, or FontType 42 */
        /* is unknown, leave "true" on the stack. */
        s << "systemdict/resourcestatus known\n";
        s << " {42 /FontType resourcestatus\n";
        s << "   {pop pop false}{true}ifelse}\n";
        s << " {true}ifelse\n";

        /* If true, execute code to produce an error message if */
        /* we can't find Apple's TrueDict in VM. */
        s << "{/TrueDict where{pop}{(%%[ Error: no TrueType rasterizer ]%%)= flush}ifelse\n";

        /* Since we are expected to use Apple's TrueDict TrueType */
        /* reasterizer, change the font type to 3. */
        s << "/FontType 3 def\n";

        /* Define a string to hold the state of the Apple */
        /* TrueType interpreter. */
        s << " /TrueState 271 string def\n";

        /* It looks like we get information about the resolution */
        /* of the printer and store it in the TrueState string. */
        s << " TrueDict begin sfnts save\n";
        s << " 72 0 matrix defaultmatrix dtransform dup\n";
        s << " mul exch dup mul add sqrt cvi 0 72 matrix\n";
        s << " defaultmatrix dtransform dup mul exch dup\n";
        s << " mul add sqrt cvi 3 -1 roll restore\n";
        s << " TrueState initer end\n";

        /* This BuildGlyph procedure will look the name up in the */
        /* CharStrings array, and then check to see if what it gets */
        /* is a procedure.  If it is, it executes it, otherwise, it */
        /* lets the TrueType rasterizer loose on it. */

        /* When this proceedure is executed the stack contains */
        /* the font dictionary and the character name.  We */
        /* exchange arguments and move the dictionary to the */
        /* dictionary stack. */
        s << " /BuildGlyph{exch begin\n";
        /* stack: charname */

        /* Put two copies of CharStrings on the stack and consume */
        /* one testing to see if the charname is defined in it, */
        /* leave the answer on the stack. */
        s << "  CharStrings dup 2 index known\n";
        /* stack: charname CharStrings bool */

        /* Exchange the CharStrings dictionary and the charname, */
        /* but if the answer was false, replace the character name */
        /* with ".notdef". */
        s << "    {exch}{exch pop /.notdef}ifelse\n";
        /* stack: CharStrings charname */

        /* Get the value from the CharStrings dictionary and see */
        /* if it is executable. */
        s << "  get dup xcheck\n";
        /* stack: CharStrings_entry */

        /* If is a proceedure.  Execute according to RBIIp 277-278. */
        s << "    {currentdict systemdict begin begin exec end end}\n";

        /* Is a TrueType character index, let the rasterizer at it. */
        s << "    {TrueDict begin /bander load cvlit exch TrueState render end}\n";

        s << "    ifelse\n";

        /* Pop the font's dictionary off the stack. */
        s << " end}bind def\n";

        /* This is the level 1 compatibility BuildChar procedure. */
        /* See RBIIp 281. */
        s << " /BuildChar{\n";
        s << "  1 index /Encoding get exch get\n";
        s << "  1 index /BuildGlyph get exec\n";
        s << " }bind def\n";

        /* Here we close the condition which is true */
        /* if the printer has no built-in TrueType */
        /* rasterizer. */
        s << "}if\n";
        s << "\n";
    } /* end of if Type 42 not understood. */

    s << "FontName currentdict end definefont pop\n";

    downloadMapping(s, global);
    s << "%%EndFont\n";
}

BYTE* QPSPrinterFontTTF::getTable(const char* name)
{
    BYTE *ptr;
    int x;

    /* We must search the table directory. */
    ptr = offset_table + 12;
    x=0;
    while (x != numTables) {
        if( strncmp((const char *)ptr,name,4) == 0 ) {
            ULONG offset;
            //ULONG length;
            BYTE *table;

            offset = getULONG( ptr + 8 );
            //length = getULONG( ptr + 12 );

            table = offset_table + offset;
            return table;
        }

        x++;
        ptr += 16;
    }

    return 0;
}

QString QPSPrinterFontTTF::glyphName(unsigned short charindex)
{
    return QPSPrinterFontPrivate::glyphName( charindex );
#if 0
    // I don't see why we would need to use this at all!
    USHORT c;
    if ((c=unicode_for_glyph(charindex)) != 0x000) {
        //fprintf(stdout,"glyph %04x char %04x\n",charindex,c);
        for (int i=0; unicodetoglyph[i].u != 0xffff; i++) {
            //if (unicodetoglyph[i].u >  c) break;
            if (unicodetoglyph[i].u == c) {
                //qDebug("using name %s for char %04x\n",unicodetoglyph[i].g,c);
                return unicodetoglyph[i].g;
            }
        }
    }

    Fixed post_format = getFixed( post_table );

    if( post_format.whole != 2 || post_format.fraction != 0 ) {
        QString name;
        ushort  u;
        // We must have a notdef glyph. I am not sure how it is uncoded in
        // unicode, so I assume it's the first glyph.
        if (charindex == 0)
            name = ".notdef";
        else if ((u=unicode_for_glyph(charindex)) != 0x0000)
            name.sprintf("uni%04X",u);
        else
            name.sprintf("glyph%04X",charindex); // may be part of a composite etc
        return name;
    }

    int GlyphIndex = (int)getUSHORT( post_table + 34 + (charindex * 2) );

    if( GlyphIndex <= 257 ) {           /* If a standard Apple name, */
        //qDebug("post name for glyph is %s 1\n",Apple_CharStrings[GlyphIndex]);
        return QString(Apple_CharStrings[GlyphIndex]);
    } else {                    /* Otherwise, use one */
                                /* of the pascal strings. */
        GlyphIndex -= 258;

        /* Set pointer to start of Pascal strings. */
        char* ptr = (char*)(post_table + 34 + (numGlyphs * 2));

        int len = (int)*(ptr++);        /* Step thru the strings */
        while(GlyphIndex--) {   /* until we get to the one */
                                /* that we want. */
            ptr += len;
            len = (int)*(ptr++);
        }

        return QString::fromLatin1(ptr,len);
    }
#endif
}

void QPSPrinterFontTTF::uni2glyphSetup()
{
  uni2glyph.resize(65536);
  int i;
  for (i=0; i<65536; i++) uni2glyph[i] = 0x0000;
  glyph2uni.resize(65536);
  for (i=0; i<65536; i++) glyph2uni[i] = 0x0000;

  unsigned char* cmap = getTable("cmap");
  int pos = 0;

  //USHORT version = getUSHORT(cmap + pos);
  pos += 2;
  USHORT nmaps   = getUSHORT(cmap + pos); pos += 2;

  //fprintf(stderr,"cmap version %d (should be 0), %d maps\n",version,nmaps);

  ULONG offset = 0;
  for (i=0; i<nmaps; i++) {
    USHORT platform = getUSHORT(cmap+pos); pos+=2;
    USHORT encoding = getUSHORT(cmap+pos); pos+=2;
           offset   = getULONG( cmap+pos); pos+=4;
           //fprintf(stderr,"[%d] plat %d enc %d\n",i,platform,encoding);
    if (platform == 3 && encoding == 1) break; // unicode
  }
  if (i==nmaps) {
    qWarning("Font does not have unicode encoding\n");
    return; // no unicode encoding!
  }

  //fprintf(stderr,"Doing Unicode encoding\n");

  pos = offset;
  USHORT format = getUSHORT(cmap+pos); pos+=2;
  //fprintf(stderr,"Unicode cmap format %d\n",format);

  if (format != 4) {
    //qWarning("Unicode cmap format is not 4");
    return;
  }

  pos += 2; // length
  pos += 2; // version
  USHORT segcount = getUSHORT(cmap+pos) / 2; pos+=2;

  //fprintf(stderr,"Unicode cmap seg count %d\n",segcount);

  // skip search data
  pos += 2;
  pos += 2;
  pos += 2;

  unsigned char* endcode    = cmap + offset + 14;
  unsigned char* startcode  = cmap + offset + 16 + 2*segcount;
  unsigned char* iddelta    = cmap + offset + 16 + 4*segcount;
  unsigned char* idrangeoff = cmap + offset + 16 + 6*segcount;
  //unsigned char* glyphid    = cmap + offset + 16 + 8*segcount;
  for (i=0; i<segcount; i++) {
    USHORT endcode_i    = getUSHORT(endcode   +2*i);
    USHORT startcode_i  = getUSHORT(startcode +2*i);
    SHORT iddelta_i    = getSHORT(iddelta   +2*i);
    USHORT idrangeoff_i = getUSHORT(idrangeoff+2*i);

//     fprintf(stderr,"[%d] %04x-%04x (%x %x)\n",
//         i,startcode_i,endcode_i,iddelta_i,idrangeoff_i);
    if (endcode_i == 0xffff) break; // last dummy segment

    if (idrangeoff_i == 0) {
      for (USHORT c = startcode_i; c <= endcode_i; c++) {
        USHORT g = c + iddelta_i; // glyph index
        if ( g != 0 ) {
            uni2glyph[g] = c;
            glyph2uni[c] = g;
        }
      }
    } else {
      for (USHORT c = startcode_i; c <= endcode_i; c++) {
        USHORT g = getUSHORT(idrangeoff+2*i
                             + 2*(c - startcode_i)
                             + idrangeoff_i);
        if ( g != 0 ) {
            uni2glyph[g] = c;
            glyph2uni[c] = g;
        }
      }
    }
  }
}

USHORT QPSPrinterFontTTF::unicode_for_glyph(int glyphindex)
{
  return uni2glyph[glyphindex];
}

USHORT QPSPrinterFontTTF::glyph_for_unicode(unsigned short unicode)
{
  return glyph2uni[unicode];
}

#ifdef Q_PRINTER_USE_TYPE42
// ****************** SNFTS ROUTINES *******

/*-------------------------------------------------------------------
** sfnts routines
** These routines generate the PostScript "sfnts" array which
** contains one or more strings which contain a reduced version
** of the TrueType font.
**
** A number of functions are required to accomplish this rather
** complicated task.
-------------------------------------------------------------------*/

// Write a BYTE as a hexadecimal value as part of the sfnts array.

void QPSPrinterFontTTF::sfnts_pputBYTE(BYTE n,QTextStream& s,
                                  int& string_len, int& line_len, bool& in_string)
{
  static const char hexdigits[]="0123456789ABCDEF";

  if(!in_string) {
    s << "<";
    string_len = 0;
    line_len++;
    in_string = TRUE;
  }

  s << hexdigits[ n / 16 ] ;
  s << hexdigits[ n % 16 ] ;
  string_len++;
  line_len+=2;

  if(line_len > 70) {
    s << "\n";
    line_len=0;
  }
}

// Write a USHORT as a hexadecimal value as part of the sfnts array.

void QPSPrinterFontTTF::sfnts_pputUSHORT(USHORT n,QTextStream& s,
                                  int& string_len, int& line_len, bool& in_string)
{
  sfnts_pputBYTE(n / 256,s, string_len, line_len, in_string);
  sfnts_pputBYTE(n % 256,s, string_len, line_len, in_string);
}


// Write a ULONG as part of the sfnts array.

void QPSPrinterFontTTF::sfnts_pputULONG(ULONG n,QTextStream& s,
                                  int& string_len, int& line_len, bool& in_string)
{
  int x1 = n % 256;   n /= 256;
  int x2 = n % 256;   n /= 256;
  int x3 = n % 256;   n /= 256;

  sfnts_pputBYTE(n,s , string_len, line_len, in_string);
  sfnts_pputBYTE(x3,s, string_len, line_len, in_string);
  sfnts_pputBYTE(x2,s, string_len, line_len, in_string);
  sfnts_pputBYTE(x1,s, string_len, line_len, in_string);
}

/*
** This is called whenever it is
** necessary to end a string in the sfnts array.
**
** (The array must be broken into strings which are
** no longer than 64K characters.)
*/
void QPSPrinterFontTTF::sfnts_end_string(QTextStream& s,
                                    int& string_len, int& line_len, bool& in_string)
{
  if(in_string) {
    string_len=0;               /* fool sfnts_pputBYTE() */

    // s << "\n% dummy byte:\n";

    // extra byte for pre-2013 compatibility
    sfnts_pputBYTE(0, s, string_len, line_len, in_string);

    s << ">";
    line_len++;
  }

  in_string=FALSE;
}

/*
** This is called at the start of each new table.
** The argement is the length in bytes of the table
** which will follow.  If the new table will not fit
** in the current string, a new one is started.
*/
void QPSPrinterFontTTF::sfnts_new_table(ULONG length,QTextStream& s,
                                   int& string_len, int& line_len, bool& in_string)
{
  if( (string_len + length) > 65528 )
    sfnts_end_string(s, string_len, line_len, in_string);
}

/*
** We may have to break up the 'glyf' table.  That is the reason
** why we provide this special routine to copy it into the sfnts
** array.
*/
void QPSPrinterFontTTF::sfnts_glyf_table(ULONG oldoffset,
                                    ULONG correct_total_length,
                                    QTextStream& s,
                                    int& string_len, int& line_len, bool& in_string)

{
  int x;
  ULONG off;
  ULONG length;
  int c;
  ULONG total=0;                /* running total of bytes written to table */

  loca_table = getTable("loca");

  int font_off = oldoffset;

  /* Copy the glyphs one by one */
  for(x=0; x < numGlyphs; x++) {
    /* Read the glyph offset from the index-to-location table. */
    if(indexToLocFormat == 0) {
      off = getUSHORT( loca_table + (x * 2) );
      off *= 2;
      length = getUSHORT( loca_table + ((x+1) * 2) );
      length *= 2;
      length -= off;
    } else {
      off = getULONG( loca_table + (x * 4) );
      length = getULONG( loca_table + ((x+1) * 4) );
      length -= off;
    }

    //  fprintf(stderr,"glyph length=%d",(int)length);

    /* Start new string if necessary. */
    sfnts_new_table( (int)length, s, string_len, line_len, in_string );

    /*
    ** Make sure the glyph is padded out to a
    ** two byte boundary.
    */
    if( length % 2 ) {
      qWarning("TrueType font contains a 'glyf' table without 2 byte padding");
      defective = TRUE;
      return;
    }

    /* Copy the bytes of the glyph. */
    while( length-- ) {
      c = offset_table[ font_off ];
      font_off++;

      sfnts_pputBYTE(c, s, string_len, line_len, in_string);
      total++;          /* add to running total */
    }
  }

  /* Pad out to full length from table directory */
  while( total < correct_total_length ) {
    sfnts_pputBYTE(0, s, string_len, line_len, in_string);
    total++;
  }

  /* Look for unexplainable descrepancies between sizes */
  if( total != correct_total_length ) {
    qWarning("QPSPrinterFontTTF::sfnts_glyf_table: total != correct_total_length");
    defective = TRUE;
    return;
  }
}

/*
** Here is the routine which ties it all together.
**
** Create the array called "sfnts" which
** holds the actual TrueType data.
*/

void QPSPrinterFontTTF::download_sfnts(QTextStream& s)
{
  // tables worth including in a type 42 font
  char *table_names[]= {
    "cvt ",
    "fpgm",
    "glyf",
    "head",
    "hhea",
    "hmtx",
    "loca",
    "maxp",
    "prep"
  };

  struct {                      /* The location of each of */
    ULONG oldoffset;    /* the above tables. */
    ULONG newoffset;
    ULONG length;
    ULONG checksum;
  } tables[9];

  int c;                        /* Input character. */
  int diff;
  int count;                    /* How many `important' tables did we find? */

  BYTE* ptr = offset_table + 12; // original table directory
  ULONG nextoffset=0;
  count=0;

  /*
  ** Find the tables we want and store there vital
  ** statistics in tables[].
  */
  for(int x=0; x < 9; x++ ) {
    do {
      diff = strncmp( (char*)ptr, table_names[x], 4 );

      if( diff > 0 ) {          /* If we are past it. */
        tables[x].length = 0;
        diff = 0;
      }
      else if( diff < 0 ) {             /* If we haven't hit it yet. */
        ptr += 16;
      }
      else if( diff == 0 ) {    /* Here it is! */
        tables[x].newoffset = nextoffset;
        tables[x].checksum = getULONG( ptr + 4 );
        tables[x].oldoffset = getULONG( ptr + 8 );
        tables[x].length = getULONG( ptr + 12 );
        nextoffset += ( ((tables[x].length + 3) / 4) * 4 );
        count++;
        ptr += 16;
      }
    } while(diff != 0);
  } /* end of for loop which passes over the table directory */

  /* Begin the sfnts array. */

  s << "/sfnts[<";

  bool in_string=TRUE;
  int  string_len=0;
  int  line_len=8;

  /* Generate the offset table header */
  /* Start by copying the TrueType version number. */
  ptr = offset_table;
  for(int x=0; x < 4; x++)
    sfnts_pputBYTE( *(ptr++) , s, string_len, line_len, in_string );

  /* Now, generate those silly numTables numbers. */
  sfnts_pputUSHORT(count,s, string_len, line_len, in_string);           /* number of tables */
  if( count == 9 ) {
    sfnts_pputUSHORT(7,s, string_len, line_len, in_string);             /* searchRange */
    sfnts_pputUSHORT(3,s, string_len, line_len, in_string);             /* entrySelector */
    sfnts_pputUSHORT(81,s, string_len, line_len, in_string);            /* rangeShift */
  }
  else {
    qWarning("Fewer than 9 tables selected");
  }

  /* Now, emmit the table directory. */
  for(int x=0; x < 9; x++) {
    if( tables[x].length == 0 ) /* Skip missing tables */
      continue;

    /* Name */
    sfnts_pputBYTE( table_names[x][0], s, string_len, line_len, in_string);
    sfnts_pputBYTE( table_names[x][1], s, string_len, line_len, in_string);
    sfnts_pputBYTE( table_names[x][2], s, string_len, line_len, in_string);
    sfnts_pputBYTE( table_names[x][3], s, string_len, line_len, in_string);

    /* Checksum */
    sfnts_pputULONG( tables[x].checksum, s, string_len, line_len, in_string );

    /* Offset */
    sfnts_pputULONG( tables[x].newoffset + 12 + (count * 16), s,
                     string_len, line_len, in_string );

    /* Length */
    sfnts_pputULONG( tables[x].length, s,
                     string_len, line_len, in_string );
  }

  /* Now, send the tables */
  for(int x=0; x < 9; x++) {
    if( tables[x].length == 0 ) /* skip tables that aren't there */
      continue;

    /* 'glyf' table gets special treatment */
    if( strcmp(table_names[x],"glyf")==0 ) {
      sfnts_glyf_table(tables[x].oldoffset,tables[x].length, s,
                       string_len, line_len, in_string);
    } else { // other tables should not exceed 64K (not always true; Sivan)
      if( tables[x].length > 65535 ) {
        qWarning("TrueType font has a table which is too long");
        defective = TRUE;
        return;
      }

      /* Start new string if necessary. */
      sfnts_new_table(tables[x].length, s,
                      string_len, line_len, in_string);

      int font_off = tables[x].oldoffset;
      /* Copy the bytes of the table. */
      for( int y=0; y < (int)tables[x].length; y++ ) {
        c = offset_table[ font_off ];
        font_off++;

        sfnts_pputBYTE(c, s, string_len, line_len, in_string);
      }
    }

    /* Padd it out to a four byte boundary. */
    int y=tables[x].length;
    while( (y % 4) != 0 ) {
      sfnts_pputBYTE(0, s, string_len, line_len, in_string);
      y++;
    }

  } /* End of loop for all tables */

  /* Close the array. */
  sfnts_end_string(s, string_len, line_len, in_string);
  s << "]def\n";
}
#endif

// ****************** Type 3 CharProcs *******

/*
** This routine is used to break the character
** procedure up into a number of smaller
** procedures.  This is necessary so as not to
** overflow the stack on certain level 1 interpreters.
**
** Prepare to push another item onto the stack,
** starting a new proceedure if necessary.
**
** Not all the stack depth calculations in this routine
** are perfectly accurate, but they do the job.
*/
static int stack_depth = 0;
static void stack(int num_pts, int newnew, QTextStream& s)
{
  if( num_pts > 25 ) {          /* Only do something of we will */
                                /* have a log of points. */
    if(stack_depth == 0) {
      s << "{";
      stack_depth=1;
    }

    stack_depth += newnew;              /* Account for what we propose to add */

    if(stack_depth > 100) {
      s << "}_e{";
      stack_depth = 3 + newnew; /* A rough estimate */
    }
  }
}

static void stack_end(QTextStream& s)                   /* called at end */
{
  if(stack_depth) {
    s << "}_e";
    stack_depth=0;
  }
}

// postscript drawing commands

static void PSMoveto(FWord x, FWord y, QTextStream& ts)
{
  ts << x;
  ts << " ";
  ts << y;
  ts << " _m\n";
}

static void PSLineto(FWord x, FWord y, QTextStream& ts)
{
  ts << x;
  ts << " ";
  ts << y;
  ts << " _l\n";
}

/* Emmit a PostScript "curveto" command. */
static void PSCurveto(FWord* xcoor, FWord* ycoor,
                      FWord x, FWord y, int s, int t, QTextStream& ts)
{
  int N, i;
  double sx[3], sy[3], cx[4], cy[4];

  N = t-s+2;
  for(i=0; i<N-1; i++) {
    sx[0] = i==0?xcoor[s-1]:(xcoor[i+s]+xcoor[i+s-1])/2;
    sy[0] = i==0?ycoor[s-1]:(ycoor[i+s]+ycoor[i+s-1])/2;
    sx[1] = xcoor[s+i];
    sy[1] = ycoor[s+i];
    sx[2] = i==N-2?x:(xcoor[s+i]+xcoor[s+i+1])/2;
    sy[2] = i==N-2?y:(ycoor[s+i]+ycoor[s+i+1])/2;
    cx[3] = sx[2];
    cy[3] = sy[2];
    cx[1] = (2*sx[1]+sx[0])/3;
    cy[1] = (2*sy[1]+sy[0])/3;
    cx[2] = (sx[2]+2*sx[1])/3;
    cy[2] = (sy[2]+2*sy[1])/3;

    ts << (int)cx[1];
    ts << " ";
    ts << (int)cy[1];
    ts << " ";
    ts << (int)cx[2];
    ts << " ";
    ts << (int)cy[2];
    ts << " ";
    ts << (int)cx[3];
    ts << " ";
    ts << (int)cy[3];
    ts << " _c\n";
  }
}

/* The PostScript bounding box. */
/* Variables to hold the character data. */

//void load_char(struct TTFONT *font, BYTE *glyph);
//void clear_data();

//void PSMoveto(FWord x, FWord y, QTextStream& ts);
//void PSLineto(FWord x, FWord y, QTextStream& ts);
//void PSCurveto(FWord x, FWord y, int s, int t, QTextStream& ts);

//double area(FWord *x, FWord *y, int n);
//int nextinctr(int co, int ci);
//int nextoutctr(int co);
//int nearout(int ci);
//double intest(int co, int ci);
#define sqr(x) ((x)*(x))

#define NOMOREINCTR -1
#define NOMOREOUTCTR -1

/*
** Find the area of a contour?
*/
static double area(FWord *x, FWord *y, int n)
{
  int i;
  double sum;

  sum=x[n-1]*y[0]-y[n-1]*x[0];
  for (i=0; i<=n-2; i++) sum += x[i]*y[i+1] - y[i]*x[i+1];
  return sum;
}

static int nextoutctr(int /*co*/, charproc_data* cd)
{
  int j;

  for(j=0; j<cd->num_ctr; j++)
    if (cd->check_ctr[j]==0 && cd->area_ctr[j] < 0) {
      cd->check_ctr[j]=1;
      return j;
    }

  return NOMOREOUTCTR;
} /* end of nextoutctr() */

static int nextinctr(int co, int /*ci*/, charproc_data* cd)
{
  int j;

  for(j=0; j<cd->num_ctr; j++)
    if (cd->ctrset[2*j+1]==co)
      if (cd->check_ctr[ cd->ctrset[2*j] ]==0) {
        cd->check_ctr[ cd->ctrset[2*j] ]=1;
        return cd->ctrset[2*j];
      }

  return NOMOREINCTR;
}

static double intest(int co, int ci, charproc_data* cd)
{
  int i, j, start, end;
  double r1, r2, a;
  FWord xi[3], yi[3];

  j=start=(co==0)?0:(cd->epts_ctr[co-1]+1);
  end=cd->epts_ctr[co];
  i=(ci==0)?0:(cd->epts_ctr[ci-1]+1);
  xi[0] = cd->xcoor[i];
  yi[0] = cd->ycoor[i];
  r1=sqr(cd->xcoor[start] - xi[0])
     + sqr(cd->ycoor[start] - yi[0]);

  for (i=start; i<=end; i++) {
    r2 = sqr(cd->xcoor[i] - xi[0])+sqr(cd->ycoor[i] - yi[0]);
    if (r2 < r1) {
      r1=r2; j=i;
    }
  }
  xi[1]=cd->xcoor[j-1]; yi[1]=cd->ycoor[j-1];
  xi[2]=cd->xcoor[j+1]; yi[2]=cd->ycoor[j+1];
  if (j==start) { xi[1]=cd->xcoor[end]; yi[1]=cd->ycoor[end]; }
  if (j==end) { xi[2]=cd->xcoor[start]; yi[2]=cd->ycoor[start]; }
  a=area(xi, yi, 3);

  return a;
} /* end of intest() */

/*
** find the nearest out contour to a specified in contour.
*/
static int nearout(int ci, charproc_data* cd)
{
  int k = 0;                    /* !!! is this right? */
  int co;
  double a, a1=0;

  for (co=0; co < cd->num_ctr; co++) {
    if(cd->area_ctr[co] < 0) {
      a=intest(co,ci, cd);
      if (a<0 && a1==0) {
        k=co;
        a1=a;
      }
      if(a<0 && a1!=0 && a>a1) {
        k=co;
        a1=a;
      }
    }
  }

  return k;
} /* end of nearout() */


/*
** We call this routine to emmit the PostScript code
** for the character we have loaded with load_char().
*/
static void PSConvert(QTextStream& s, charproc_data* cd)
{
  int i,j,k,fst,start_offpt;
  int end_offpt=0;

  cd->area_ctr = new double[cd->num_ctr];
  memset(cd->area_ctr, 0, (cd->num_ctr*sizeof(double)));

  cd->check_ctr = new char[cd->num_ctr];
  memset(cd->check_ctr, 0, (cd->num_ctr*sizeof(char)));

  cd->ctrset = new int[2*(cd->num_ctr)];
  memset(cd->ctrset, 0, (cd->num_ctr*2*sizeof(int)));

  cd->check_ctr[0]=1;
  cd->area_ctr[0]=area(cd->xcoor, cd->ycoor, cd->epts_ctr[0]+1);

  for (i=1; i<cd->num_ctr; i++)
    cd->area_ctr[i]=area(cd->xcoor+cd->epts_ctr[i-1]+1,
                         cd->ycoor+cd->epts_ctr[i-1]+1,
                         cd->epts_ctr[i]-cd->epts_ctr[i-1]);

  for (i=0; i<cd->num_ctr; i++) {
    if (cd->area_ctr[i]>0) {
      cd->ctrset[2*i]=i;
      cd->ctrset[2*i+1]=nearout(i,cd);
    } else {
      cd->ctrset[2*i]=-1;
      cd->ctrset[2*i+1]=-1;
    }
  }

  /* Step thru the coutours. */
  /* I believe that a contour is a detatched */
  /* set of curves and lines. */
  i=j=k=0;
  while (i < cd->num_ctr ) {
    fst = j = (k==0) ? 0 : (cd->epts_ctr[k-1]+1);

    /* Move to the first point on the contour. */
    stack(cd->num_pts,3,s);
    PSMoveto(cd->xcoor[j],cd->ycoor[j],s);
    start_offpt = 0;            /* No off curve points yet. */

    /* Step thru the remaining points of this contour. */
    for(j++; j <= cd->epts_ctr[k]; j++) {
      if (!(cd->tt_flags[j]&1)) { /* Off curve */
        if (!start_offpt)
          { start_offpt = end_offpt = j; }
        else
          end_offpt++;
      } else {                  /* On Curve */
        if (start_offpt) {
          stack(cd->num_pts,7,s);
          PSCurveto(cd->xcoor,cd->ycoor,
                    cd->xcoor[j],cd->ycoor[j],
                    start_offpt,end_offpt,s);
          start_offpt = 0;
        } else {
          stack(cd->num_pts,3,s);
          PSLineto(cd->xcoor[j], cd->ycoor[j],s);
        }
      }
    }

    /* Do the final curve or line */
    /* of this coutour. */
    if (start_offpt) {
      stack(cd->num_pts,7,s);
      PSCurveto(cd->xcoor,cd->ycoor,
                cd->xcoor[fst],cd->ycoor[fst],
                start_offpt,end_offpt,s);
    } else {
      stack(cd->num_pts,3,s);
      PSLineto(cd->xcoor[fst],cd->ycoor[fst],s);
    }

    k=nextinctr(i,k,cd);

    if (k==NOMOREINCTR)
      i=k=nextoutctr(i,cd);

    if (i==NOMOREOUTCTR)
      break;
  }

  /* Now, we can fill the whole thing. */
  stack(cd->num_pts,1,s);
  s << "_cl";           /* "closepath eofill" */

  /* Free our work arrays. */
  delete [] cd->area_ctr;
  delete [] cd->check_ctr;
  delete [] cd->ctrset;
}


/*
** Load the simple glyph data pointed to by glyph.
** The pointer "glyph" should point 10 bytes into
** the glyph data.
*/
void QPSPrinterFontTTF::charprocLoad(BYTE *glyph, charproc_data* cd)
{
  int x;
  BYTE c, ct;

  /* Read the contour endpoints list. */
  cd->epts_ctr = new int[cd->num_ctr];
  //cd->epts_ctr = (int *)myalloc(cd->num_ctr,sizeof(int));
  for (x = 0; x < cd->num_ctr; x++) {
    cd->epts_ctr[x] = getUSHORT(glyph);
    glyph += 2;
  }

  /* From the endpoint of the last contour, we can */
  /* determine the number of points. */
  cd->num_pts = cd->epts_ctr[cd->num_ctr-1]+1;
#ifdef DEBUG_TRUETYPE
  fprintf(stderr,"num_pts=%d",num_pts);
  fprintf(stderr,"% num_pts=%d\n",num_pts);
#endif

  /* Skip the instructions. */
  x = getUSHORT(glyph);
  glyph += 2;
  glyph += x;

  /* Allocate space to hold the data. */
  //cd->tt_flags = (BYTE *)myalloc(num_pts,sizeof(BYTE));
  //cd->xcoor    = (FWord *)myalloc(num_pts,sizeof(FWord));
  //cd->ycoor    = (FWord *)myalloc(num_pts,sizeof(FWord));
  cd->tt_flags = new BYTE[cd->num_pts];
  cd->xcoor    = new FWord[cd->num_pts];
  cd->ycoor    = new FWord[cd->num_pts];

  /* Read the flags array, uncompressing it as we go. */
  /* There is danger of overflow here. */
  for (x = 0; x < cd->num_pts; ) {
    cd->tt_flags[x++] = c = *(glyph++);

    if (c&8) {          /* If next byte is repeat count, */
      ct = *(glyph++);

      if( (x + ct) > cd->num_pts ) {
        qWarning("Fatal Error in TT flags");
        return;
      }

      while (ct--)
        cd->tt_flags[x++] = c;
    }
  }

  /* Read the x coordinates */
  for (x = 0; x < cd->num_pts; x++) {
    if (cd->tt_flags[x] & 2) {          /* one byte value with */
                                        /* external sign */
      c = *(glyph++);
      cd->xcoor[x] = (cd->tt_flags[x] & 0x10) ? c : (-1 * (int)c);
    } else if(cd->tt_flags[x] & 0x10) { /* repeat last */
      cd->xcoor[x] = 0;
    } else {                            /* two byte signed value */
      cd->xcoor[x] = getFWord(glyph);
      glyph+=2;
    }
  }

  /* Read the y coordinates */
  for(x = 0; x < cd->num_pts; x++) {
    if (cd->tt_flags[x] & 4) {          /* one byte value with */
                                        /* external sign */
      c = *(glyph++);
      cd->ycoor[x] = (cd->tt_flags[x] & 0x20) ? c : (-1 * (int)c);
    } else if (cd->tt_flags[x] & 0x20) {        /* repeat last value */
      cd->ycoor[x] = 0;
    } else {                    /* two byte signed value */
      cd->ycoor[x] = getUSHORT(glyph);
      glyph+=2;
    }
  }

  /* Convert delta values to absolute values. */
  for(x = 1; x < cd->num_pts; x++) {
    cd->xcoor[x] += cd->xcoor[x-1];
    cd->ycoor[x] += cd->ycoor[x-1];
  }

  for(x=0; x < cd->num_pts; x++) {
    cd->xcoor[x] = topost(cd->xcoor[x]);
    cd->ycoor[x] = topost(cd->ycoor[x]);
  }
}

#define ARG_1_AND_2_ARE_WORDS 1
#define ARGS_ARE_XY_VALUES 2
#define ROUND_XY_TO_GRID 4
#define WE_HAVE_A_SCALE 8
/* RESERVED 16 */
#define MORE_COMPONENTS 32
#define WE_HAVE_AN_X_AND_Y_SCALE 64
#define WE_HAVE_A_TWO_BY_TWO 128
#define WE_HAVE_INSTRUCTIONS 256
#define USE_MY_METRICS 512

void QPSPrinterFontTTF::subsetGlyph(int charindex,bool* glyphset)
{
  USHORT flags;
  USHORT glyphIndex;
  charproc_data cd;

  glyphset[charindex] = TRUE;
  //printf("subsetting %s ==> ",glyphName(charindex).latin1());

  /* Get a pointer to the data. */
  BYTE* glyph = charprocFindGlyphData( charindex );

  /* If the character is blank, it has no bounding box, */
  /* otherwise read the bounding box. */
  if( glyph == (BYTE*)NULL ) {
    cd.num_ctr=0;
  } else {
    cd.num_ctr = getSHORT(glyph);
    /* Advance the pointer past bounding box. */
    glyph += 10;
  }

  if( cd.num_ctr < 0 ) { // composite
    /* Once around this loop for each component. */
    do {
      flags = getUSHORT(glyph); /* read the flags word */
      glyph += 2;
      glyphIndex = getUSHORT(glyph);    /* read the glyphindex word */
      glyph += 2;

      glyphset[ glyphIndex ] = TRUE;
      //printf("subset contains: %d %s ",glyphIndex, glyphName(glyphIndex).latin1());

      if(flags & ARG_1_AND_2_ARE_WORDS) {
        glyph += 2;
        glyph += 2;
      } else {
        glyph += 1;
        glyph += 1;
      }

      if(flags & WE_HAVE_A_SCALE) {
        glyph += 2;
      } else if(flags & WE_HAVE_AN_X_AND_Y_SCALE) {
        glyph += 2;
        glyph += 2;
      } else if(flags & WE_HAVE_A_TWO_BY_TWO) {
        glyph += 2;
        glyph += 2;
        glyph += 2;
        glyph += 2;
      } else {
      }
    } while(flags & MORE_COMPONENTS);
  }
  //printf("\n");
}


/*
** Emmit PostScript code for a composite character.
*/
void QPSPrinterFontTTF::charprocComposite(BYTE *glyph, QTextStream& s)
{
  USHORT flags;
  USHORT glyphIndex;
  int arg1;
  int arg2;
  float xscale = 1;
  float yscale = 1;
#ifdef DEBUG_TRUETYPE
  float scale01 = 0;
  float scale10 = 0;
#endif

  /* Once around this loop for each component. */
  do {
      flags = getUSHORT(glyph);   /* read the flags word */
      glyph += 2;

      glyphIndex = getUSHORT(glyph);      /* read the glyphindex word */
      glyph += 2;

      if(flags & ARG_1_AND_2_ARE_WORDS) {
                                /* The tt spec. seems to say these are signed. */
	  arg1 = getSHORT(glyph);
	  glyph += 2;
	  arg2 = getSHORT(glyph);
	  glyph += 2;
      } else {                    /* The tt spec. does not clearly indicate */
                                /* whether these values are signed or not. */
	  arg1 = (char)*(glyph++);
	  arg2 = (char)*(glyph++);
      }

      if(flags & WE_HAVE_A_SCALE) {
	  xscale = yscale = f2dot14( getUSHORT(glyph) );
	  glyph += 2;
      } else if(flags & WE_HAVE_AN_X_AND_Y_SCALE) {
	  xscale = f2dot14( getUSHORT(glyph) );
	  glyph += 2;
	  yscale = f2dot14( getUSHORT(glyph) );
	  glyph += 2;
      } else if(flags & WE_HAVE_A_TWO_BY_TWO) {
	  xscale = f2dot14( getUSHORT(glyph) );
	  glyph += 2;
#ifdef DEBUG_TRUETYPE
	  scale01 = f2dot14( getUSHORT(glyph) );
#endif
	  glyph += 2;
#ifdef DEBUG_TRUETYPE
	  scale10 = f2dot14( getUSHORT(glyph) );
#endif
	  glyph += 2;
	  yscale = f2dot14( getUSHORT(glyph) );
	  glyph += 2;
      }

      /* Debugging */
#ifdef DEBUG_TRUETYPE
      s << "% flags=" << flags << ", arg1=" << arg1 << ", arg2=" << arg2 << ", xscale=" << xscale << ", yscale=" << yscale <<
	  ", scale01=" << scale01 << ", scale10=" << scale10 << endl;
#endif


      if ( (flags & ARGS_ARE_XY_VALUES) != ARGS_ARE_XY_VALUES ) {
	  s << "% unimplemented shift, arg1=" << arg1;
	  s << ", arg2=" << arg2 << "\n";
	  arg1 = arg2 = 0;
      }

      /* If we have an (X,Y) shif and it is non-zero, */
      /* translate the coordinate system. */
      if ( flags & (WE_HAVE_A_TWO_BY_TWO|WE_HAVE_AN_X_AND_Y_SCALE) ) {
#if 0
	  // code similar to this would be needed for two_by_two
	  s << "gsave [ " << xscale << " " << scale01 << " " << scale10 << " "
	    << yscale << " " << topost(arg1) << " " << topost(arg2) << "] SM\n";
#endif
	  if ( flags & WE_HAVE_A_TWO_BY_TWO )
	      s << "% Two by two transformation, unimplemented\n";
	  s << "gsave " << topost(arg1);
	  s << " " << topost(arg2);
	  s << " translate\n";
	  s << xscale << " " << yscale << " scale\n";
      } else if ( flags & ARGS_ARE_XY_VALUES && ( arg1 != 0 || arg2 != 0 ) ) {
	  s << "gsave " << topost(arg1);
	  s << " " << topost(arg2);
	  s << " translate\n";
      }

      /* Invoke the CharStrings procedure to print the component. */
      s << "false CharStrings /";
      s << glyphName( glyphIndex );
      s << " get exec\n";

      //  printf("false CharStrings /%s get exec\n",
      //ttfont_CharStrings_getname(font,glyphIndex));

      /* If we translated the coordinate system, */
      /* put it back the way it was. */
      if( (flags & ARGS_ARE_XY_VALUES && (arg1 != 0 || arg2 != 0) ) ||
	  ( flags & (WE_HAVE_A_TWO_BY_TWO|WE_HAVE_AN_X_AND_Y_SCALE) ) ) {
	  s << "grestore ";
      }
  } while (flags & MORE_COMPONENTS);
}

/*
** Return a pointer to a specific glyph's data.
*/
BYTE* QPSPrinterFontTTF::charprocFindGlyphData(int charindex)
{
  ULONG off;
  ULONG length;

  /* Read the glyph offset from the index to location table. */
  if(indexToLocFormat == 0) {
    off = getUSHORT( loca_table + (charindex * 2) );
    off *= 2;
    length = getUSHORT( loca_table + ((charindex+1) * 2) );
    length *= 2;
    length -= off;
  } else {
    off = getULONG( loca_table + (charindex * 4) );
    length = getULONG( loca_table + ((charindex+1) * 4) );
    length -= off;
  }

  if(length > 0)
    return glyf_table + off;
  else
    return (BYTE*)NULL;
}

void QPSPrinterFontTTF::charproc(int charindex, QTextStream& s)
{
  int llx,lly,urx,ury;
  int advance_width;
  charproc_data cd;

#ifdef DEBUG_TRUETYPE
  s << "% tt_type3_charproc for ";
  s << charindex;
  s << "\n";
#endif

  /* Get a pointer to the data. */
  BYTE* glyph = charprocFindGlyphData( charindex );

  /* If the character is blank, it has no bounding box, */
  /* otherwise read the bounding box. */
  if( glyph == (BYTE*)NULL ) {
    llx=lly=urx=ury=0;  /* A blank char has an all zero BoundingBox */
    cd.num_ctr=0;               /* Set this for later if()s */
  } else {
    /* Read the number of contours. */
    cd.num_ctr = getSHORT(glyph);

    /* Read PostScript bounding box. */
    llx = getFWord(glyph + 2);
    lly = getFWord(glyph + 4);
    urx = getFWord(glyph + 6);
    ury = getFWord(glyph + 8);

    /* Advance the pointer. */
    glyph += 10;
  }

  /* If it is a simple character, load its data. */
  if (cd.num_ctr > 0)
    charprocLoad(glyph, &cd);
  else
    cd.num_pts=0;

  /* Consult the horizontal metrics table to determine */
  /* the character width. */
  if( charindex < numberOfHMetrics )
    advance_width = getuFWord( hmtx_table + (charindex * 4) );
  else
    advance_width = getuFWord( hmtx_table + ((numberOfHMetrics-1) * 4) );

  /* Execute setcachedevice in order to inform the font machinery */
  /* of the character bounding box and advance width. */
  stack(cd.num_pts,7,s);
  s << topost(advance_width);
  s << " 0 ";
  s << topost(llx);
  s << " ";
  s << topost(lly);
  s << " ";
  s << topost(urx);
  s << " ";
  s << topost(ury);
  s << " _sc\n";

  /* If it is a simple glyph, convert it, */
  /* otherwise, close the stack business. */
  if( cd.num_ctr > 0 ) {        // simple
    PSConvert(s,&cd);
    delete [] cd.tt_flags;
    delete [] cd.xcoor;
    delete [] cd.ycoor;
    delete [] cd.epts_ctr;
  } else if( cd.num_ctr < 0 ) { // composite
    charprocComposite(glyph,s);
  }

  stack_end(s);
} /* end of tt_type3_charproc() */


// ================== PFA ====================

class QPSPrinterFontPFA
  : public QPSPrinterFontPrivate {
public:
  QPSPrinterFontPFA(const QFont &f, QByteArray& data);
  virtual void    download(QTextStream& s, bool global);
      virtual bool embedded() { return TRUE; }
private:
  QByteArray     data;
};

QPSPrinterFontPFA::QPSPrinterFontPFA(const QFont &f, QByteArray& d)
{
  data = d;

  int pos = 0;
  char* p = data.data();
  QString fontname;

  if (p[ pos ] != '%' || p[ pos+1 ] != '!') { // PFA marker
    qWarning("invalid pfa file");
    return;
  }

  char* fontnameptr = strstr(p+pos,"/FontName");
  if (fontnameptr == NULL)
    return;

  fontnameptr += strlen("/FontName") + 1;
  while (*fontnameptr == ' ' || *fontnameptr == '/') fontnameptr++;
  int l=0;
  while (fontnameptr[l] != ' ') l++;

  psname = QString::fromLatin1(fontnameptr,l);
  replacementList = makePSFontNameList( f, psname );
}

void QPSPrinterFontPFA::download(QTextStream& s, bool global)
{
  char* p = data.data();

  emitPSFontNameList( s, psname, replacementList );
  s << "% Font resource\n";
  for (int i=0; i < (int)data.size(); i++) s << p[i];
  s << "% End of font resource\n";
  downloadMapping( s, global );
}

// ================== PFB ====================

class QPSPrinterFontPFB
  : public QPSPrinterFontPrivate {
public:
  QPSPrinterFontPFB(const QFont &f, QByteArray& data);
  virtual void    download(QTextStream& s, bool global);
      virtual bool embedded() { return TRUE; }
private:
  QByteArray     data;
};

QPSPrinterFontPFB::QPSPrinterFontPFB(const QFont &f, QByteArray& d)
{
  data = d;

  int pos = 0;
  int len;
  //  int typ;
  unsigned char* p = (unsigned char*) data.data();
  QString fontname;

  if (p[ pos ] != 0x80) { // PFB marker
    qWarning("pfb file does not start with 0x80");
    return;
  }
  pos++;
  //  typ = p[ pos ]; // 1=ascii 2=binary 3=done
  pos++;
  len = p[ pos ];          pos++;
  len |= (p[ pos ] << 8) ; pos++;
  len |= (p[ pos ] << 16); pos++;
  len |= (p[ pos ] << 24); pos++;

  //printf("font block type %d len %d\n",typ,len);

  char* fontnameptr = strstr((char*)p+pos,"/FontName");
  if (fontnameptr == NULL)
    return;

  fontnameptr += strlen("/FontName") + 1;
  while (*fontnameptr == ' ' || *fontnameptr == '/') fontnameptr++;
  int l=0;
  while (fontnameptr[l] != ' ') l++;

  psname = QString::fromLatin1(fontnameptr,l);
  replacementList = makePSFontNameList( f, psname );
}

void QPSPrinterFontPFB::download(QTextStream& s, bool global)
{
  unsigned char* p = (unsigned char*) data.data();
  int pos;
  int len;
  int typ;

  int hexcol = 0;
  int line_length = 64;

  emitPSFontNameList( s, psname, replacementList );
  s << "% Font resource\n";

  pos = 0;
  typ = -1;
  while (typ != 3) { // not end of file
    if (p[ pos ] != 0x80) // PFB marker
      return; // pfb file does not start with 0x80
    pos++;
    typ = p[ pos ]; // 1=ascii 2=binary 3=done
    pos++;

    if (typ == 3) break;

    len = p[ pos ];         pos++;
    len |= (p[ pos ] << 8) ; pos++;
    len |= (p[ pos ] << 16); pos++;
    len |= (p[ pos ] << 24); pos++;

    //printf("font block type %d len %d\n",typ,len);

    if (typ==1) {
      for (int j=0; j<len; j++) {
        if (hexcol) { s << "\n"; hexcol = 0; }
        //qWarning(QString::fromLatin1((char*)(p+pos),1));
        if (p[pos] == '\r' || p[pos] == '\n') {
          s << "\n";
        while (p[pos] == '\r' || p[pos] == '\n') pos++;
        } else {
        s << QString::fromLatin1((char*)(p+pos),1);
        pos++;
        }
      }
    }
    if (typ==2) {
      static const char *hexchar = "0123456789abcdef";
      for (int j=0; j<len; j++) {
        /* trim hexadecimal lines to line_length columns */
        if (hexcol >= line_length) {
          s << "\n";
          hexcol = 0;
        }
        s << QString::fromLatin1(hexchar+((p[pos] >> 4) & 0xf),1);
        s << QString::fromLatin1(hexchar+((p[pos]     ) & 0xf),1);
        pos++;
        //putc(hexchar[(*p >> 4) & 0xf], ofp);
        //putc(hexchar[*p & 0xf], ofp);
        hexcol += 2;
      }

      //pos += len;
    }
  }
  s << "% End of font resource\n";
  downloadMapping( s, global );
}

// ================== AFontFileNotFound ============



class QPSPrinterFontNotFound
  : public QPSPrinterFontPrivate {
public:
  QPSPrinterFontNotFound(const QFont& f);
  virtual void    download(QTextStream& s, bool global);
private:
  QByteArray     data;
};

QPSPrinterFontNotFound::QPSPrinterFontNotFound(const QFont& f)
{
    psname = makePSFontName( f );
    replacementList = makePSFontNameList( f );
}

void QPSPrinterFontNotFound::download(QTextStream& s, bool)
{
    emitPSFontNameList( s, psname, replacementList );
  s << "% No embeddable font for ";
  s << psname;
  s << " found\n";
  QPSPrinterFontPrivate::download(s, TRUE);
}

// =================== A font file for asian ============

class QPSPrinterFontAsian
  : public QPSPrinterFontPrivate {
public:
      void download(QTextStream& s, bool global);
      QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                          QPSPrinterPrivate *d ) = 0;
      QString emitDef( QTextStream &stream, const QString &ps, const QString &latinName,
                    const QFont &f, float slant, const QString &key, const QString &key2, QPSPrinterPrivate *d );
};

void QPSPrinterFontAsian::download(QTextStream& s, bool)
{
    emitPSFontNameList( s, psname, replacementList );
  s << "% Asian postscript font requested. Using ";
  s << psname << endl;
}


QString QPSPrinterFontAsian::emitDef( QTextStream &stream, const QString &ps, const QString &latinName,
                                   const QFont &f, float slant, const QString &key, const QString &key2, QPSPrinterPrivate *d)
{
    QString fontName;
    QString lfontName;

    QString *tmp = d->headerFontNames.find( key );

    if ( d->buffer ) {
        if ( tmp ) {
            fontName = *tmp;
        } else {
            lfontName.sprintf( "/F%d ", ++d->headerFontNumber );
            d->fontStream << lfontName << " " << slant << "/" << latinName << " MSF\n";
            d->headerFontNames.insert( key2, new QString( fontName ) );
            fontName.sprintf( "/F%das ", d->headerFontNumber );
            d->fontStream << fontName << slant << ps << " MSF\n";
            d->headerFontNames.insert( key2, new QString( fontName ) );
        }
        ++d->headerFontNumber;
        d->fontStream << "/F" << d->headerFontNumber << " "
                      << pointSize( f, d->scale ) << lfontName << " DF\n";
        d->fontStream << "/F" << d->headerFontNumber << "as "
                      << pointSize( f, d->scale ) << fontName << " DF\n";
        fontName.sprintf( "F%d", d->headerFontNumber );
        d->headerFontNames.insert( key, new QString( fontName ) );
    } else {
        if ( !tmp )
            tmp = d->pageFontNames.find( key );
        if ( tmp ) {
            fontName = *tmp;
        } else {
            lfontName.sprintf( "/F%d ", ++d->pageFontNumber );
            stream << lfontName << " " << slant << "/" << latinName << " MSF\n";
            d->pageFontNames.insert( key2, new QString( fontName ) );
            fontName.sprintf( "/F%das ", d->pageFontNumber );
            stream << fontName << slant << ps << " MSF\n";
            d->pageFontNames.insert( key2, new QString( fontName ) );
        }
        ++d->pageFontNumber;
        stream << "/F" << d->pageFontNumber << " "
               << pointSize( f, d->scale ) << lfontName << " DF\n";
        stream << "/F" << d->pageFontNumber << "as "
               << pointSize( f, d->scale ) << fontName << " DF\n";
        fontName.sprintf( "F%d", d->pageFontNumber );
        d->pageFontNames.insert( key, new QString( fontName ) );
    }
    return fontName;

}

#ifndef QT_NO_TEXTCODEC

// ----------- Japanese --------------

class QPSPrinterFontJapanese
  : public QPSPrinterFontAsian {
public:
      QPSPrinterFontJapanese(const QFont& f);
      void drawText( QTextStream &stream, uint spaces, const QPoint &p,
                     const QString &text, QPSPrinterPrivate *d, QPainter *paint);
      QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                          QPSPrinterPrivate *d );
private:
    const QTextCodec *convJP;
};

QPSPrinterFontJapanese::QPSPrinterFontJapanese(const QFont& f)
{
    psname = makePSFontName( f );
    // now try to convert that to some japanese postscript font in a sensible way.
    // as we don't support font substitution at the moment, we just use the
    // two fonts that should be available on any system: Ryumin-Light and GothicBBB-Medium

    if ( psname.contains( "Helvetica" ) || psname.contains( "Bold" ) )
        if ( psname.contains("Italic") || psname.contains("Oblique") ) {
            psname = "GothicBBB-Oblique";
        } else {
            psname = "GothicBBB";
        }
    else
        if ( psname.contains("Italic") || psname.contains("Oblique") ) {
            psname = "Ryumin-Oblique";
        } else {
            psname = "Ryumin";
        }
    convJP = 0;
    replacementList = makePSFontNameList( f );
}


QString QPSPrinterFontJapanese::defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                                       QPSPrinterPrivate *d )
{
    float slant = 0;
    QString latinName;
    // do correct mapping
    if ( ps == "GothicBBB-Oblique" ) {
        slant = 0.2;
        ps = "/GothicBBB-Medium-H";
        latinName = "Helvetica-Oblique";
    } else if ( ps == "GothicBBB" ) {
        ps = "/GothicBBB-Medium-H";
        latinName = "Helvetica";
    } else if ( ps == "Ryumin-Oblique" ) {
        slant = 0.2;
        ps = "/Ryumin-Light-H";
        latinName = "Times-Italic";
    } else if ( ps == "Ryumin" ) {
        ps = "/Ryumin-Light-H";
        latinName = "Times-Roman";
    }
    return emitDef( stream, ps, latinName, f, slant, key, ps, d );
}

void QPSPrinterFontJapanese::drawText( QTextStream &stream, uint spaces, const QPoint &p,
                                  const QString &text, QPSPrinterPrivate *d, QPainter *paint)
{
    if ( !convJP )
        convJP = QTextCodec::codecForName( "jisx0208.1983-0" );

    int x = p.x();
    if ( spaces > 0 )
        x += spaces * d->fm.width( ' ' );
    int y = p.y();
    if ( y != d->textY || d->textY == 0 )
        stream << y << " Y";
    d->textY = y;
    QString mdf;
    if ( paint->font().underline() )
        mdf += " " + QString().setNum( y + d->fm.underlinePos() + d->fm.lineWidth() ) +
               " " + QString::number( d->fm.lineWidth() ) + " Tl";
    if ( paint->font().strikeOut() )
        mdf += " " + QString().setNum( y + d->fm.strikeOutPos() ) +
               " " + QString::number( d->fm.lineWidth() ) + " Tl";
    int code = 0, codeOld = 0;
    QChar ch;
    QCString out, oneChar;
    int l = text.length();
    for ( int i = 0; i <= l; i++ ) {
        out += oneChar;
        oneChar = "";
        codeOld = code;
        if ( i < l ) {
            ch = text.at(i);
            if ( !ch.row() ) {
                code = 0;
                if ( ch == '(' || ch == ')' || ch == '\\' )
                    oneChar += "\\";
                oneChar += ch.cell();
            } else {
                code = 1;
                if ( !convJP )
                    ch = QChar( 0x2222 ); // box
                else
                    ch = convJP->characterFromUnicode( text, i );
                char chj = ch.row();
                if ( chj == '(' || chj == ')' || chj == '\\' )
                    oneChar += "\\";
                oneChar += chj;
                chj = ch.cell();
                if ( chj == '(' || chj == ')' || chj == '\\' )
                    oneChar += "\\";
                oneChar += chj;
            }
        }
        if ( !out.isEmpty() && (code != codeOld || i == l) ) {
            if ( codeOld == 0 ) {
                stream << " " << d->currentFont << " F";
            } else {
                stream << " " << d->currentFont << "as F";
            }
            int w = d->fm.width( out );
            stream << "(" << out << ")" << w << " " << x << mdf << " T";
            if ( i < l ) {
                stream << " ";
            } else {
                stream << "\n";
            }
            x += w;
            out = "";
        }
    }
}

// ----------- Korean --------------

// sans serif
static const psfont SMGothic [] = {
    { "SMGothic-Medium-KSC-EUC-H", 0, 100. },
    { "SMGothic-Medium-KSC-EUC-H", 0.2, 100. },
    { "SMGothic-DemiBold-KSC-EUC-H", 0, 100. },
    { "SMGothic-DemiBold-KSC-EUC-H", 0.2, 100. }
};

// serif
static const psfont SMMyungjo [] = {
    { "SMMyungjo-Light-KSC-EUC-H", 0, 100. },
    { "SMMyungjo-Light-KSC-EUC-H", 0.2, 100. },
    { "SMMyungjo-Bold-KSC-EUC-H", 0, 100. },
    { "SMMyungjo-Bold-KSC-EUC-H", 0.2, 100. }
};

static const psfont MKai [] = {
    { "MingMT-Light-KSC-EUC-H", 0, 100. },
    { "MingMT-Light-KSC-EUC-H", 0.2, 100. },
    { "MKai-Medium-KSC-EUC-H", 0, 100. },
    { "MKai-Medium-KSC-EUC-H", 0.2, 100. },
};


static const psfont Munhwa [] = {
    { "Munhwa-Regular-KSC-EUC-H", 0, 100. },
    { "Munhwa-Regular-KSC-EUC-H", 0.2, 100. },
    { "Munhwa-Bold-KSC-EUC-H", 0, 100. },
    { "Munhwa-Bold-KSC-EUC-H", 0.2, 100. }
};

static const psfont * const KoreanReplacements[] = {
    SMMyungjo, SMGothic, Munhwa, MKai, Helvetica, 0
        };

class QPSPrinterFontKorean
  : public QPSPrinterFontAsian {
public:
      QPSPrinterFontKorean(const QFont& f);
      QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                          QPSPrinterPrivate *d );
};

QPSPrinterFontKorean::QPSPrinterFontKorean(const QFont& f)
{
    int type = getPsFontType( f );
    psname = SMMyungjo[type].psname;
    appendReplacements( replacementList, KoreanReplacements, type );
}


QString QPSPrinterFontKorean::defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                                       QPSPrinterPrivate *d )
{
    return QPSPrinterFontPrivate::defineFont( stream, ps, f, key, d );
}

// ----------- traditional chinese ------------

static const psfont MSung [] = {
    { "MSung-Light-B5-H", 0, 100. },
    { "MSung-Light-B5-H", 0.2, 100. },
    { "MSung-Light-B5-H", 0, 100. },
    { "MSung-Light-B5-H", 0.2, 100. },
};

static const psfont MOESung [] = {
    { "MOESung-Regular-B5-H", 0, 100. },
    { "MOESung-Regular-B5-H", 0.2, 100. },
    { "MOESung-Regular-B5-H", 0, 100. },
    { "MOESung-Regular-B5-H", 0.2, 100. },
};

static const psfont * const MSungReplacements[] = {
    MSung, MOESung, Helvetica, 0
        };

class QPSPrinterFontTraditionalChinese
  : public QPSPrinterFontAsian {
public:
      QPSPrinterFontTraditionalChinese(const QFont& f);
      QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                          QPSPrinterPrivate *d );
};

QPSPrinterFontTraditionalChinese::QPSPrinterFontTraditionalChinese(const QFont& f)
{
    int type = getPsFontType( f );
    psname = MSung[type].psname;
    appendReplacements( replacementList, MSungReplacements, type );
}


QString QPSPrinterFontTraditionalChinese::defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                                       QPSPrinterPrivate *d )
{
    return QPSPrinterFontPrivate::defineFont( stream, ps, f, key, d );
}

// ----------- simplified chinese ------------



static const psfont Simplified [] = {
    { "MSung-Light-GBK-EUC-H", 0, 100. },
    { "MSung-Light-GBK-EUC-H", 0.2, 100. },
    { "MKai-Medium-GBK-EUC-H", 0, 100. },
    { "MKai-Medium-GBK-EUC-H", 0.2, 100. },
};

static const psfont MSungGBK [] = {
    { "MSung-Light-GBK-EUC-H", 0, 100. },
    { "MSung-Light-GBK-EUC-H", 0.2, 100. },
    { "MSung-Light-GBK-EUC-H", 0, 100. },
    { "MSung-Light-GBK-EUC-H", 0.2, 100. },
};

static const psfont FangSong [] = {
    { "CFangSong-Light-GBK-EUC-H", 0, 100. },
    { "CFangSong-Light-GBK-EUC-H", 0.2, 100. },
    { "CFangSong-Light-GBK-EUC-H", 0, 100. },
    { "CFangSong-Light-GBK-EUC-H", 0.2, 100. },
};

static const psfont * const SimplifiedReplacements[] = {
    Simplified, MSungGBK, FangSong, Helvetica, 0
        };

class QPSPrinterFontSimplifiedChinese
  : public QPSPrinterFontAsian {
public:
      QPSPrinterFontSimplifiedChinese(const QFont& f);
      QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                          QPSPrinterPrivate *d );
};

QPSPrinterFontSimplifiedChinese::QPSPrinterFontSimplifiedChinese(const QFont& f)
{
    int type = getPsFontType( f );
    psname = MSungGBK[type].psname;
    appendReplacements( replacementList, SimplifiedReplacements, type );
}


QString QPSPrinterFontSimplifiedChinese::defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                                       QPSPrinterPrivate *d )
{
    return QPSPrinterFontPrivate::defineFont( stream, ps, f, key, d );
}
#endif


// ================== QPSPrinterFont ====================

class QPSPrinterFont {
public:
  QPSPrinterFont(const QFont& f, int script, QPSPrinterPrivate *priv);
  ~QPSPrinterFont();
  QString postScriptFontName()     { return p->postScriptFontName(); }
    QString defineFont( QTextStream &stream, QString ps, const QFont &f, const QString &key,
                             QPSPrinterPrivate *d )
    { return p->defineFont( stream, ps, f, key, d ); }
    void    download(QTextStream& s, bool global) { p->download(s, global); }
    QPSPrinterFontPrivate *handle() { return p; }
    QString xfontname;
private:
  QByteArray       data;
  QPSPrinterFontPrivate* p;
};

QPSPrinterFont::~QPSPrinterFont()
{
    // the dict in QFontPrivate does deletion for us.
    //  delete p;
}


QPSPrinterFont::QPSPrinterFont(const QFont& f, int script, QPSPrinterPrivate *priv)
    : p(0)
{
    QString fontfilename;
    QString fontname;


#ifdef Q_WS_X11
    // append qsettings fontpath
    QSettings settings;
    bool embed = settings.readBoolEntry( "/qt/embedFonts", TRUE );
#endif

    // ### implement similar code for QWS and WIN
    xfontname = makePSFontName( f );

#ifdef Q_WS_X11
    bool xlfd = FALSE;
    if ( embed ) {
	f.d->load( (QFont::Script)script );
	QFontStruct *fs = f.d->x11data.fontstruct[script];
	//qDebug("fs = %p, script=%d", fs, script);

#ifndef QT_NO_XFTFREETYPE
	if ( qt_has_xft && fs && fs != (QFontStruct *)-1 && fs->xfthandle ) {
	    // ### cache filename directly!
	    //qDebug("fontstruct name: %s", fs->name.data());
	    XftPattern *pattern = XftNameParse(fs->name.data());
	    //qDebug("xfthandle=%p", font);
	    char *filename = 0;
	    XftResult res;
	    XftPattern *f = XftFontMatch(qt_xdisplay(), 0, pattern, &res);
	    XftPatternGetString (f, XFT_FILE, 0, &filename);
	    //qDebug("filename for font is '%s'", filename);
	    if ( filename ) {
		fontfilename = QString::fromLatin1( filename );
		xfontname = fontfilename;
	    }
	    XftPatternDestroy( f );
	    XftPatternDestroy( pattern );
	} else
#endif
	{
	    QString rawName;
	    if ( fs && fs != (QFontStruct *)-1 )
		rawName = fs->name;
	    int index = rawName.find('-');
	    if (index == 0) {
		// this is an XLFD font name
		for (int i=0; i < 6; i++) {
		    index = rawName.find('-',index+1);
		}
		xfontname = rawName.mid(0,index);
		xlfd = TRUE;
	    }
	}
    }
#endif // Q_WS_X11
    // ### somehow the font dict doesn't seem to work without this. Don't ask me why...
    priv->fonts.size();
    p = priv->fonts.find(xfontname);
    if ( p )
	return;

#ifdef Q_WS_X11
    if ( embed && xlfd ) {

	if ( priv->fontpath.isEmpty() ) {
	    int npaths;
	    char** font_path;
	    font_path = XGetFontPath( qt_xdisplay(), &npaths);
	    bool xfsconfig_read = FALSE;
	    for (int i=0; i<npaths; i++) {
		// If we're using xfs, append font paths from /etc/X11/fs/config
		// can't hurt, and chances are we'll get all fonts that way.
		if (((font_path[i])[0] != '/') && !xfsconfig_read) {
		    // We're using xfs -> read its config
		    bool finished = FALSE;
		    QFile f("/etc/X11/fs/config");
		    if ( !f.exists() )
			f.setName("/usr/X11R6/lib/X11/fs/config");
		    if ( !f.exists() )
			f.setName("/usr/X11/lib/X11/fs/config");
		    if ( f.exists() ) {
			f.open(IO_ReadOnly);
			while(f.status()==IO_Ok && !finished) {
			    QString fs;
			    f.readLine(fs, 1024);
			    fs=fs.stripWhiteSpace();
			    if (fs.left(9)=="catalogue" && fs.contains('=')) {
				fs=fs.mid(fs.find('=')+1).stripWhiteSpace();
				while(f.status()==IO_Ok && fs.right(1)==",") {
				    if (!fs.contains(":unscaled"))
					priv->fontpath += fs.left(fs.length()-1);
				    f.readLine(fs, 1024);
				    fs=fs.stripWhiteSpace();
				}
				finished = TRUE;
			    }
			}
			f.close();
		    }
		    xfsconfig_read = TRUE;
		} else if(!strstr(font_path[i], ":unscaled")) {
		    // Fonts paths marked :unscaled are always bitmapped fonts
		    // -> we can as well ignore them now and save time
		    priv->fontpath += font_path[i];
		}
	    }
	    XFreeFontPath(font_path);

	    // append qsettings fontpath
	    QStringList fontpath = settings.readListEntry( "/qt/fontPath", ':' );
	    if ( !fontpath.isEmpty() )
		priv->fontpath += fontpath;
	}

	for (QStringList::Iterator it=priv->fontpath.begin(); it!=priv->fontpath.end() && fontfilename.isEmpty(); it++) {
	    if ((*it).left(1) != "/") continue; // not a path name, a font server
	    QString fontmapname;
	    int num = 0;
	    // search font.dir and font.scale for the right file
	    while ( num < 2 ) {
		if ( num == 0 )
		    fontmapname = (*it) + "/fonts.scale";
		else
		    fontmapname = (*it) + "/fonts.dir";
		//qWarning(fontmapname);
		QFile fontmap(fontmapname);
		if (fontmap.open(IO_ReadOnly)) {
		    while (!fontmap.atEnd()) {
			QString mapping;
			fontmap.readLine(mapping,512);
			// fold to lower (since X folds to lowercase)
			//qWarning(xfontname);
			//qWarning(mapping);
			if (mapping.lower().contains(xfontname.lower())) {
			    int index = mapping.find(' ',0);
			    QString ffn = mapping.mid(0,index);
				// remove the most common bitmap formats
			    if( !ffn.contains( ".pcf" ) && !ffn.contains( " .bdf" ) &&
				!ffn.contains( ".spd" ) && !ffn.contains( ".phont" ) ) {
				fontfilename = (*it) + QString("/") + ffn;
				if ( QFile::exists(fontfilename) ) {
				    //qDebug("found font file %s", fontfilename.latin1());
				    break;
				} else // unset fontfilename
				    fontfilename = QString();
			    }
			}
		    }
		    fontmap.close();
		}
		num++;
	    }
	}
    }
#endif

    //qDebug("font=%s, fontname=%s, p=%p", f.family().latin1(), xfontname.latin1(), p);

	// memory mapping would be better here
    if (fontfilename.length() > 0) { // maybe there is no file name
	QFile fontfile(fontfilename);
	if ( fontfile.exists() ) {
	    //printf("font name %s size = %d\n",fontfilename.latin1(),fontfile.size());
	    data = QByteArray( fontfile.size() );

	    fontfile.open(IO_Raw | IO_ReadOnly);
	    fontfile.readBlock(data.data(), fontfile.size());
	    fontfile.close();
	}
    }

    enum { NONE, PFB, PFA, TTF } type = NONE;

    if (!data.isNull() && data.size() > 0) {
        unsigned char* d = (unsigned char *)data.data();
        if (d[0] == 0x80 && d[6] == '%' && d[7] == '!' && d[8] == 'P' && d[9] == 'S' )
            type = PFB;
        else if (d[0] == '%' && d[1] == '!' && d[2] == 'P' && d[3] == 'S')
            type = PFA;
        else if (d[0]==0x00 && d[1]==0x01 && d[2]==0x00 && d[3]==0x00)
            type = TTF;
        else
            type = NONE;
    } else
        type = NONE;

        //qWarning(xfontname);
    switch (type) {
        case TTF :
            p = new QPSPrinterFontTTF(f, data);
            break;
        case PFB:
            p = new QPSPrinterFontPFB(f, data);
            break;
        case PFA:
            p = new QPSPrinterFontPFA(f, data);
            break;
        case NONE:
        default:

#ifndef QT_NO_TEXTCODEC
	    if ( script == QFont::Hiragana || script == QFont::Katakana ) {
		p = new QPSPrinterFontJapanese( f );
	    } else if ( script == QFont::Hangul) {
		p = new QPSPrinterFontKorean( f );
	    } else if ( script == QFont::Han ) {
		QString name = QString::fromLatin1( QTextCodec::codecForLocale()->name() );
		if ( name == "eucJP" || name == "JIS7" || name == "Shift-JIS" )
		    p = new QPSPrinterFontJapanese( f );
		else if ( name == "eucKR" )
		    p = new QPSPrinterFontKorean( f );
		else if ( name == "Big5" )
		    p = new QPSPrinterFontTraditionalChinese( f );
		else if ( name == "GBK" )
		    p = new QPSPrinterFontSimplifiedChinese( f );
		else {
		    //qDebug("didnt find font for %s", xfontname.latin1());
		    p = new QPSPrinterFontNotFound( f );
		}
	    } else
#endif
		//qDebug("didnt find font for %s", xfontname.latin1());
		p=new QPSPrinterFontNotFound( f ); break;
    }
    //qDebug("inserting %s int dict (%p)", xfontname.latin1(), p);
    priv->fonts.insert( xfontname, p );
}

// ================= END OF PS FONT METHODS ============


QPSPrinterPrivate::QPSPrinterPrivate( QPrinter *prt, int filedes )
    : buffer( 0 ), outDevice( 0 ), fd( filedes ), pageBuffer( 0 ), fonts(27, FALSE), fontBuffer(0), savedImage( 0 ),
      dirtypen( FALSE ), dirtybrush( FALSE ), dirtyBkColor( FALSE ), dirtyBkMode( FALSE ),
      currentFontCodec( 0 ), fm( QFont() ), textY( 0 )
{
    printer = prt;
    headerFontNames.setAutoDelete( TRUE );
    pageFontNames.setAutoDelete( TRUE );
    fonts.setAutoDelete( TRUE );
    currentFontFile = 0;
    scale = 1.;
    scriptUsed = -1;
}

QPSPrinterPrivate::~QPSPrinterPrivate()
{
    delete pageBuffer;
}

void QPSPrinterPrivate::setFont( const QFont & fnt, int script )
{
    QFont f = fnt;
    if ( f.rawMode() ) {
        QFont fnt( QString::fromLatin1("Helvetica"), 12 );
        setFont( fnt, QFont::Unicode );
        return;
    }
    if ( f.pointSize() == 0 ) {
#if defined(CHECK_RANGE)
        qWarning( "QPrinter: Cannot set a font with zero point size." );
#endif
        f.setPointSize(QApplication::font().pointSize());
        if ( f.pointSize() == 0 )
            f.setPointSize( 11 );
    }

    if ( !fixed_ps_header )
        makeFixedStrings();

    QPSPrinterFont ff( f, script, this );
    QString ps = ff.postScriptFontName();

    QString s = ps;
    s.append( ' ' );
    s.prepend( ' ' );
    // Sivan: I moved the downloading until after we draw text
    // to allow subsetting.

    QString key;

    if ( f.pointSize() != -1 )
	key.sprintf( "%s %d", ff.xfontname.ascii(), f.pointSize() );
    else
	key.sprintf( "%s px%d", ff.xfontname.ascii(), f.pixelSize() );
    QString * tmp;
    if ( !buffer )
        tmp = pageFontNames.find( key );
    else
        tmp = headerFontNames.find( key );

    QString fontName;
    if ( tmp )
        fontName = *tmp;

    if ( fontName.isEmpty() ) {
        fontName = ff.defineFont( pageStream, ps, f, key, this );
    }
    pageStream << fontName << " F\n";

    ps.append( ' ' );
    ps.prepend( ' ' );
    if ( !fontsUsed.contains( ps ) )
        fontsUsed += ps;

    QTextCodec * codec = 0;
// ###
// #ifndef QT_NO_TEXTCODEC
//     i = 0;
//     do {
//      if ( unicodevalues[i].cs == f.charSet() )
//          codec = QTextCodec::codecForMib( unicodevalues[i++].mib );
//     } while( codec == 0 && unicodevalues[i++].cs != unicodevalues_LAST );
// #endif
    currentFont = fontName;
    currentFontCodec = codec;
    currentFontFile = ff.handle();
    scriptUsed = script;
}


static void ps_r7( QTextStream& stream, const char * s, int l )
{
    int i = 0;
    uchar line[79];
    int col = 0;

    while( i < l ) {
        line[col++] = s[i++];
        if ( col >= 76 ) {
            line[col++] = '\n';
            line[col++] = '\0';
            stream << (const char *)line;
            col = 0;
        }
    }
    if ( col > 0 ) {
        while( (col&3) != 0 )
            line[col++] = '%'; // use a comment as padding
        line[col++] = '\n';
        line[col++] = '\0';
        stream << (const char *)line;
    }
}


static const int quoteSize = 3; // 1-8 pixels
static const int maxQuoteLength = 4+16+32+64+128; // magic extended quote
static const int quoteReach = 10; // ... 1-1024 pixels back
static const int tableSize = 1024; // 2 ** quoteReach;

static const int hashSize = 29;

static const int None = INT_MAX;


static void emitBits( QByteArray & out, int & byte, int & bit,
                      int numBits, uint data )
{
    int b = 0;
    uint d = data;
    while( b < numBits ) {
        if ( bit == 0 )
            out[byte] = 0;
        if ( d & 1 )
            out[byte] = (uchar)out[byte] | ( 1 << bit );
        d = d >> 1;
        b++;
        bit++;
        if ( bit > 6 ) {
            bit = 0;
            byte++;
            if ( byte == (int)out.size() )
                out.resize( byte*2 );
        }
    }
}


QByteArray compress( const QImage & image, bool gray ) {
    int size = image.width()*image.height();
    int pastPixel[tableSize];
    int mostRecentPixel[hashSize];
    QRgb *pixel = new QRgb[size+1];

    int i = 0;
    if ( image.depth() == 8 ) {
        for( int y=0; y < image.height(); y++ ) {
            uchar * s = image.scanLine( y );
            for( int x=0; x < image.width(); x++ ) {
                pixel[i] = image.color( s[x] );
		// commented out, as it doesn't seem to work correctly with 8bit images.
#if 0
               if ( qAlpha( pixel[i] )< 0x40 ) // 25% alpha, convert to white
                   pixel[i] = qRgb( 0xff, 0xff, 0xff );
               else
#endif
                    pixel[i] &= RGB_MASK;
                i++;
            }
        }
    } else {
        for( int y=0; y < image.height(); y++ ) {
            QRgb * s = (QRgb*)(image.scanLine( y ));
            for( int x=0; x < image.width(); x++ )
                for( x=0; x < image.width(); x++ ) {
                    pixel[i] = (*s++);
                    if ( qAlpha( pixel[i] ) < 0x40 ) // 25% alpha, convert to white -
                        pixel[i] = qRgb( 0xff, 0xff, 0xff );
                    else
                        pixel[i] &= RGB_MASK;
                    i++;
           }
        }
    }

    pixel[size] = 0;

    /* this compression function emits blocks of data, where each
       block is an unquoted series of pixels, or a quote from earlier
       pixels. if the six-letter string "banana" were a six-pixel
       image, it might be unquoted "ban" followed by a 3-pixel quote
       from -2.  note that the final "a" is then copied from the
       second "a", which is copied from the first "a" in the same copy
       operation.

       the scanning for quotable blocks uses a cobol-like loop and a
       hash table: we know how many pixels we need to quote, hash the
       first and last pixel we need, and then go backwards in time
       looking for some spot where those pixels of those two colours
       occur at the right distance from each other.

       when we find a spot, we'll try a string-compare of all the
       intervening pixels. we only do a maximum of 128 both-ends
       compares or 64 full-string compares. it's more important to be
       fast than get the ultimate in compression. */

    for( i=0; i < hashSize; i++ )
        mostRecentPixel[i] = None;
    int index = 0;
    int emittedUntil = 0;
    QByteArray out( 49 );
    int outOffset = 0;
    int outBit = 0;
    /* we process pixels serially, emitting as necessary/possible. */
    while( index <= size ) {
        int bestCandidate = None;
        int bestLength = 0;
        i = index % tableSize;
        int h = pixel[index] % hashSize;
        int start, end;
        start = end = pastPixel[i] = mostRecentPixel[h];
        mostRecentPixel[h] = index;
        /* if our first candidate quote is unusable, or we don't need
           to quote because we've already emitted something for this
           pixel, just skip. */
        if ( start < index - tableSize || index >= size ||
             emittedUntil > index)
            start = end = None;
        int attempts = 0;
        /* scan for suitable quote candidates: not too far back, and
           if we've found one that's as big as it can get, don't look
           for more */
        while( start != None && end != None &&
               bestLength < maxQuoteLength &&
               start >= index - tableSize &&
               end >= index - tableSize + bestLength ) {
            /* scan backwards, looking for something good enough to
               try a (slow) string comparison. we maintain indexes to
               the start and the end of the quote candidate here */
            while( start != None && end != None &&
                   ( pixel[start] != pixel[index] ||
                     pixel[end] != pixel[index+bestLength] ) ) {
                if ( attempts++ > 128 ) {
                    start = None;
                } else if ( pixel[end] % hashSize ==
                            pixel[index+bestLength] % hashSize ) {
                    /* we move the area along the end index' chain */
                    end = pastPixel[end%tableSize];
                    start = end - bestLength;
                } else if ( pixel[start] % hashSize ==
                            pixel[index] % hashSize ) {
                    /* ... or along the start index' chain */
                    start = pastPixel[start%tableSize];
                    end = start + bestLength;
                } else {
#if 0
                    /* this should never happen: both the start and
                       the end pointers ran off their tracks. */
                    qDebug( "oops! %06x %06x %06x %06x %5d %5d %5d %d",
                            pixel[start], pixel[end],
                            pixel[index], pixel[index+bestLength],
                            start, end, index, bestLength );
#endif
                    /* but if it should happen, no problem. we'll just
                       say we found nothing, and the compression will
                       be a bit worse. */
                    start = None;
                }
                /* if we've moved either index too far to use the
                   quote candidate, let's just give up here. there's
                   also a guard against "start" insanity. */
                if ( start < index - tableSize || start < 0 || start >= index )
                    start = None;
                if ( end < index - tableSize + bestLength || end < bestLength )
                    end = None;
            }
            /* ok, now start and end point to an area of suitable
               length whose first and last points match, or one/both
               is/are set to None. */
            if ( start != None && end != None ) {
                /* slow string compare... */
                int length = 0;
                while( length < maxQuoteLength &&
                       index+length < size &&
                       pixel[start+length] == pixel[index+length] )
                    length++;
                /* if we've found something that overlaps the index
                   point, maybe we can move the quote point back?  if
                   we're copying 10 pixels from 8 pixels back (an
                   overlap of 2), that'll be faster than copying from
                   4 pixels back (an overlap of 6). */
                if ( start + length > index && length > 0 ) {
                    int d = index-start;
                    int equal = TRUE;
                    while( equal && start + length > index &&
                           start > d && start-d >= index-tableSize ) {
                        int i = 0;
                        while( equal && i < d ) {
                            if( pixel[start+i] != pixel[start+i-d] )
                                equal = FALSE;
                            i++;
                        }
                        if ( equal )
                            start -= d;
                    }
                }
                /* if what we have is longer than the best previous
                   candidate, we'll use this one. */
                if ( length > bestLength ) {
                    attempts = 0;
                    bestCandidate = start;
                    bestLength = length;
                    if ( length < maxQuoteLength && index + length < size )
                        end = mostRecentPixel[pixel[index+length]%hashSize];
                } else {
                    /* and if it ins't, we'll try some more. but we'll
                       count each string compare extra, since they're
                       so expensive. */
                    attempts += 2;
                    if ( attempts > 128 ) {
                        start = None;
                    } else if ( pastPixel[start%tableSize] + bestLength <
                                pastPixel[end%tableSize] ) {
                        start = pastPixel[start%tableSize];
                        end = start + bestLength;
                    } else {
                        end = pastPixel[end%tableSize];
                        start = end - bestLength;
                    }
                }
                /* again, if we can't make use of the current quote
                   candidate, we don't try any more */
                if ( start < index - tableSize || start < 0 || start > size+1 )
                    start = None;
                if ( end < index - tableSize + bestLength || end < 0 || end > size+1 )
                    end = None;
            }
        }
        /* at this point, bestCandidate is a candidate of bestLength
         length, or else it's None. if we have such a candidate, or
         we're at the end, we have to emit all unquoted data. */
        if ( index == size || bestCandidate != None ) {
            /* we need a double loop, because there's a maximum length
               on the "unquoted data" section. */
            while( emittedUntil < index ) {
                int l = QMIN( 8, index - emittedUntil );
                emitBits( out, outOffset, outBit,
                          1, 0 );
                emitBits( out, outOffset, outBit,
                          quoteSize, l-1 );
                while( l-- ) {
                    if ( gray ) {
                        emitBits( out, outOffset, outBit,
                                  8, qGray( pixel[emittedUntil] ) );
                    } else {
                        emitBits( out, outOffset, outBit,
                                  8, qRed( pixel[emittedUntil] ) );
                        emitBits( out, outOffset, outBit,
                                  8, qGreen( pixel[emittedUntil] ) );
                        emitBits( out, outOffset, outBit,
                                  8, qBlue( pixel[emittedUntil] ) );
                    }
                    emittedUntil++;
                }
            }
        }
        /* if we have some quoted data to output, do it. */
        if ( bestCandidate != None ) {
            emitBits( out, outOffset, outBit,
                      1, 1 );
            if ( bestLength < 5 ) {
                emitBits( out, outOffset, outBit,
                          quoteSize, bestLength - 1 );
            } else if ( bestLength - 4 <= 16 ) {
                emitBits( out, outOffset, outBit,
                          quoteSize, 4 );
                emitBits( out, outOffset, outBit,
                          4, bestLength - 1 - 4 );
            } else if ( bestLength - 4 - 16 <= 32 ) {
                emitBits( out, outOffset, outBit,
                          quoteSize, 5 );
                emitBits( out, outOffset, outBit,
                          5, bestLength - 1 - 4 - 16 );
            } else if ( bestLength - 4 - 16 - 32 <= 64 ) {
                emitBits( out, outOffset, outBit,
                          quoteSize, 6 );
                emitBits( out, outOffset, outBit,
                          6, bestLength - 1 - 4 - 16 - 32 );
            } else /* if ( bestLength - 4 - 16 - 32 - 64 <= 128 ) */ {
                emitBits( out, outOffset, outBit,
                          quoteSize, 7 );
                emitBits( out, outOffset, outBit,
                          7, bestLength - 1 - 4 - 16 - 32 - 64 );
            }
            emitBits( out, outOffset, outBit,
                      quoteReach, index - bestCandidate - 1 );
            emittedUntil += bestLength;
        }
        index++;
    }
    /* we've output all the data; time to clean up and finish off the
       last characters. */
    if ( outBit )
        outOffset++;
    out.truncate( outOffset );
    i = 0;
    /* we have to make sure the date is encoded in a stylish way :) */
    while( i < outOffset ) {
        uchar c = out[i];
        c += 42;
        if ( c > 'Z' && ( c != 't' || i == 0 || out[i-1] != 'Q' ) )
            c += 84;
        out[i] = c;
        i++;
    }
    delete [] pixel;
    return out;
}

#undef XCOORD
#undef YCOORD
#undef WIDTH
#undef HEIGHT
#undef POINT
#undef RECT
#undef INT_ARG

#define XCOORD(x)       (float)(x)
#define YCOORD(y)       (float)(y)
#define WIDTH(w)        (float)(w)
#define HEIGHT(h)       (float)(h)

#define POINT(index)    XCOORD(p[index].point->x()) << ' ' <<           \
                        YCOORD(p[index].point->y()) << ' '
#define RECT(index)     XCOORD(p[index].rect->normalize().x())  << ' ' <<     \
                        YCOORD(p[index].rect->normalize().y())  << ' ' <<     \
                        WIDTH (p[index].rect->normalize().width()) << ' ' <<  \
                        HEIGHT(p[index].rect->normalize().height()) << ' '
#define INT_ARG(index)  p[index].ival << ' '

static char returnbuffer[13];
static const char * color( const QColor &c, QPrinter * printer )
{
    if ( c == Qt::black )
        qstrcpy( returnbuffer, "B " );
    else if ( c == Qt::white )
        qstrcpy( returnbuffer, "W " );
    else if ( c.red() == c.green() && c.red() == c.blue() )
        sprintf( returnbuffer, "%d d2 ", c.red() );
    else if ( printer->colorMode() == QPrinter::GrayScale )
        sprintf( returnbuffer, "%d d2 ",
                 qGray( c.red(), c.green(),c.blue() ) );
    else
        sprintf( returnbuffer, "%d %d %d ",
                 c.red(), c.green(), c.blue() );
    return returnbuffer;
}


static const char * psCap( Qt::PenCapStyle p )
{
    if ( p == Qt::SquareCap )
        return "2 ";
    else if ( p == Qt::RoundCap )
        return "1 ";
    return "0 ";
}


static const char * psJoin( Qt::PenJoinStyle p ) {
    if ( p == Qt::BevelJoin )
        return "2 ";
    else if ( p == Qt::RoundJoin )
        return "1 ";
    return "0 ";
}



void QPSPrinterPrivate::drawImage( QPainter *paint, float x, float y, float w, float h,
                            const QImage &img )
{
    int width  = img.width();
    int height = img.height();
    float scaleX = (float)width/w;
    float scaleY = (float)height/h;

    if ( img.isNull() )
        return;

    if ( width * height > 21830 ) { // 65535/3, tolerance for broken printers
        int images, subheight;
        images = ( width * height + 21829 ) / 21830;
        subheight = ( height + images-1 ) / images;
        while ( subheight * width > 21830 ) {
            images++;
            subheight = ( height + images-1 ) / images;
        }
        int suby = 0;
        while( suby < height ) {
            drawImage( paint, x, y + suby*scaleY, w, QMIN( subheight, height-suby )*scaleY,
                       img.copy( 0, suby, width, QMIN( subheight, height-suby ) ) );
            suby += subheight;
        }
    } else {
        bool gray = (printer->colorMode() == QPrinter::GrayScale) ||
                    img.allGray();

        if ( x || y )
            pageStream << x << " " << y << " TR\n";
        if ( gray ) {
            pageStream << "/sl " << width*height << " string d\n";
            pageStream << "sl rG\n";
            QByteArray out;
            out = ::compress( img.convertDepth( 8 ), TRUE );
            ps_r7( pageStream, out, out.size() );
            pageStream << "pop\n";
            pageStream << width << ' ' << height << " 8[" << scaleX << " 0 0 " << scaleY << " 0 0]{sl}image\n";
        } else {
            pageStream << "/sl " << width*3*height << " string d\n";
            pageStream << "sl rC\n";
            QByteArray out;
            if ( img.depth() < 8 )
                out = ::compress( img.convertDepth( 8 ), FALSE );
            else if ( img.depth() > 8 && img.depth() < 24 )
                out = ::compress( img.convertDepth( 24 ), FALSE );
            else
                out = ::compress( img, FALSE );
            ps_r7( pageStream, out, out.size() );
            pageStream << "pop\n";
            pageStream << width << ' ' << height << " 8[" << scaleX << " 0 0 " << scaleY << " 0 0]{sl}QCI\n";
        }
        if ( x || y )
            pageStream << -x << " " << -y << " TR\n";
    }
}


void QPSPrinterPrivate::matrixSetup( QPainter *paint )
{
#ifndef QT_NO_TRANSFORMATIONS
    QWMatrix tmp;
    if ( paint->hasViewXForm() ) {
        QRect viewport = paint->viewport();
        QRect window   = paint->window();
        tmp.translate( viewport.x(), viewport.y() );
        tmp.scale( 1.0 * viewport.width()  / window.width(),
                   1.0 * viewport.height() / window.height() );
        tmp.translate( -window.x(), -window.y() );
    }
    if ( paint->hasWorldXForm() ) {
        tmp = paint->worldMatrix() * tmp;
    }
    pageStream << "["
           << tmp.m11() << ' ' << tmp.m12() << ' '
           << tmp.m21() << ' ' << tmp.m22() << ' '
           << tmp.dx()  << ' ' << tmp.dy()
           << "]ST\n";
#else
    QPoint p(0,0);
    p = paint->xForm(p);
    pageStream << "["
           << 0 << ' ' << 0 << ' '
           << 0 << ' ' << 0 << ' '
           << p.x()    << ' ' << p.y()
           << "]ST\n";
#endif
    dirtyMatrix = FALSE;
}

void QPSPrinterPrivate::orientationSetup()
{
    if ( printer->orientation() == QPrinter::Landscape )
        pageStream << "QLS\n";
}


void QPSPrinterPrivate::emitHeader( bool finished )
{
    QString title = printer->docName();
    QString creator = printer->creator();
    if ( !creator )                             // default creator
        creator = QString::fromLatin1("Qt " QT_VERSION_STR);
    outDevice = new QFile();
    (void)((QFile *)outDevice)->open( IO_WriteOnly, fd );
    outStream.setDevice( outDevice );
    outStream << "%!PS-Adobe-1.0";
    QPaintDeviceMetrics m( printer );
    scale = 72. / ((float) m.logicalDpiY());
    int dpi = printer->resolution();
    printer->setResolution( 72 );
    if ( finished && pageCount == 1 && printer->numCopies() == 1 &&
         printer->fullPage() && qt_gen_epsf ) {
        if ( !boundingBox.isValid() )
            boundingBox.setRect( 0, 0, m.width(), m.height() );
        if ( printer->orientation() == QPrinter::Landscape )
            // ### fixme: won't work with resolution != 72
            outStream << " EPSF-3.0\n%%BoundingBox: "
                   << m.height() - boundingBox.bottom() << " " // llx
                   << m.width() - boundingBox.right() << " " // lly
                   << m.height() - boundingBox.top() << " " // urx
                   << m.width() - boundingBox.left();// ury
        else
            outStream << " EPSF-3.0\n%%BoundingBox: "
                   << boundingBox.left() << " "
                   << m.height() - boundingBox.bottom() - 1 << " "
                   << boundingBox.right() + 1 << " "
                   << m.height() - boundingBox.top();
    } else {
        int w = m.width();
        int h = m.height();
        if ( !printer->fullPage() ) {
            w += 2*printer->margins().width();
            h += 2*printer->margins().height();
        }
        // set a bounding box according to the DSC
        if ( printer->orientation() == QPrinter::Landscape )
            outStream << "\n%%BoundingBox: 0 0 " << h << " " << w;
        else
            outStream << "\n%%BoundingBox: 0 0 " << w << " " << h;
    }
    outStream << "\n" << wrapDSC( "%%Creator: " + creator );
    if ( !!title )
        outStream << wrapDSC( "%%Title: " + title );
    outStream << "%%CreationDate: " << QDateTime::currentDateTime().toString();
    outStream << "\n%%Orientation: ";
    if ( printer->orientation() == QPrinter::Landscape )
        outStream << "Landscape";
    else
        outStream << "Portrait";
    if ( finished )
        outStream << "\n%%Pages: " << pageCount << "\n"
		  << wrapDSC( "%%DocumentFonts: " + fontsUsed );
    else
        outStream << "%%Pages: (atend)"
               << "\n%%DocumentFonts: (atend)";
    outStream << "\n%%EndComments\n";

    if ( !fixed_ps_header )
        makeFixedStrings();

    outStream << "%%BeginProlog\n";
    const char * const prologLicense = "% Prolog copyright 1994-2001 Trolltech. "
                                 "You may copy this prolog in any way\n"
                                 "% that is directly related to this "
                                 "document. For other use of this prolog,\n"
                                 "% see your licensing agreement for Qt.\n";
    outStream << prologLicense << *fixed_ps_header << "\n";

    // we have to do this here, as scaling can affect this.
    QString lineStyles = "/LArr["                                       // Pen styles:
			 " [] []"                       //   solid line
			 " [ w s ] [ s w ]"                 //   dash line
			 " [ s s ] [ s s ]"                  //   dot line
			 " [ m s s s ] [ s m s s ]"      //   dash dot line
			 " [ m s s s s ] [ s m s s s s ]"         //   dash dot dot line
			 " ] d\n";
    lineStyles.replace( QRegExp( "w" ), QString::number( 10./scale ) );
    lineStyles.replace( QRegExp( "m" ), QString::number( 5./scale ) );
    lineStyles.replace( QRegExp( "s" ), QString::number( 3./scale ) );

    outStream << lineStyles;


    outStream << "/pageinit {\n";
    if ( !printer->fullPage() ) {
        if ( printer->orientation() == QPrinter::Portrait )
            outStream << printer->margins().width() << " "
                   << printer->margins().height() << " translate\n";
        else
            outStream << printer->margins().height() << " "
                   << printer->margins().width() << " translate\n";
    }
    if ( printer->orientation() == QPrinter::Portrait ) {
        outStream << "% " << m.widthMM() << "*" << m.heightMM()
               << "mm (portrait)\n0 " << m.height()
               << " translate " << scale << " -" << scale << " scale/defM matrix CM d } d\n";
    } else {
        outStream << "% " << m.heightMM() << "*" << m.widthMM()
               << " mm (landscape)\n 90 rotate " << scale << " -" << scale << " scale/defM matrix CM d } d\n";
    }
    outStream << "%%EndProlog\n";


    outStream << "%%BeginSetup\n";
    if ( printer->numCopies() > 1 ) {
	outStream << "/#copies " << printer->numCopies() << " def\n";
	outStream << "/NumCopies " << printer->numCopies() << " SPD\n";
	outStream << "/Collate " << (printer->collateCopies() ? "true" : "false") << " SPD\n";
    }
    if ( fontBuffer->buffer().size() ) {
        if ( pageCount == 1 || finished )
            outStream << "% Fonts and encodings used\n";
        else
            outStream << "% Fonts and encodings used on pages 1-"
                   << pageCount << "\n";
        QDictIterator<QPSPrinterFontPrivate> it(fonts);
        while (it.current()) {
          it.current()->download(outStream,TRUE); // true means its global
          ++it;
                }
        outStream.writeRawBytes( fontBuffer->buffer().data(),
                              fontBuffer->buffer().size() );
    }
    outStream << "%%EndSetup\n";

    outStream.writeRawBytes( buffer->buffer().data(),
                          buffer->buffer().size() );

    delete buffer;
    buffer = 0;
    fontStream.unsetDevice();
    delete fontBuffer;
    fontBuffer = 0;
    printer->setResolution( dpi );
}


/* Called whenever a restore has been done. Currently done at the top of a
  new page and whenever clipping is turned off. */
void QPSPrinterPrivate::resetDrawingTools( QPainter *paint )
{
    QPen   defaultPen;                  // default drawing tools
    QBrush defaultBrush;

    QColor c = paint->backgroundColor();
    if ( c != Qt::white )
        pageStream << color( c, printer ) << "BC\n";

    if ( paint->backgroundMode() != Qt::TransparentMode )
            pageStream << "/OMo true d\n";

    //currentUsed = currentSet;
    //setFont( currentSet );
    currentFontFile = 0;

    QBrush b = paint->brush();
    if ( b != defaultBrush ) {
        if ( b == Qt::CustomPattern ) {
#if defined(CHECK_RANGE)
            qWarning( "QPrinter: Pixmap brush not supported" );
#endif
        } else {
            cbrush = b;
        }
    }

    dirtypen = TRUE;
    dirtybrush = TRUE;

    if ( paint->hasViewXForm() || paint->hasWorldXForm() )
        matrixSetup( paint );
}


static void putRect( QTextStream &stream, const QRect &r )
{
    stream << r.x() << " "
           << r.y() << " "
           << r.width() << " "
           << r.height() << " ";
}


void QPSPrinterPrivate::setClippingOff( QPainter *paint )
{
        pageStream << "CLO\n";              // clipping off, includes a restore
        resetDrawingTools( paint );     // so drawing tools must be reset
}


void QPSPrinterPrivate::clippingSetup( QPainter *paint )
{
    if ( paint->hasClipping() ) {
        if ( !firstClipOnPage )
            setClippingOff( paint );
        const QRegion rgn = paint->clipRegion();
        QMemArray<QRect> rects = rgn.rects();
        int i;
        pageStream<< "CLSTART\n";           // start clipping
        for( i = 0 ; i < (int)rects.size() ; i++ ) {
            putRect( pageStream, rects[i] );
            pageStream << "ACR\n";          // add clip rect
            if ( pageCount == 1 )
                boundingBox = boundingBox.unite( rects[i] );
        }
        pageStream << "CLEND\n";            // end clipping
        firstClipOnPage = FALSE;
    } else {
        if ( !firstClipOnPage )      // no need to turn off if first on page
            setClippingOff( paint );
        // if we're painting without clipping, the bounding box must
        // be everything.  NOTE: this assumes that this function is
        // only ever called when something is to be painted.
        QPaintDeviceMetrics m( printer );
        if ( !boundingBox.isValid() )
            boundingBox.setRect( 0, 0, m.width(), m.height() );
    }
    dirtyClipping = FALSE;
}

void QPSPrinterPrivate::initPage(QPainter *paint)
{

    // a restore undefines all the fonts that have been defined
    // inside the scope (normally within pages) and all the glyphs that
    // have been added in the scope.

    QDictIterator<QPSPrinterFontPrivate> it(fonts);
    while (it.current()) {
      it.current()->restore();
      ++it;
    }
    if ( !buffer ) {
        pageFontNames.clear();
    }

    pageStream.unsetDevice();
    if ( pageBuffer )
        delete pageBuffer;
    pageBuffer = new QBuffer();
    pageBuffer->open( IO_WriteOnly );
    pageStream.setEncoding( QTextStream::Latin1 );
    pageStream.setDevice( pageBuffer );
    delete savedImage;
    savedImage = 0;
    textY = 0;
    dirtyClipping   = TRUE;
    firstClipOnPage = TRUE;


    resetDrawingTools( paint );
    dirtyNewPage      = FALSE;
    pageFontNumber = headerFontNumber;
}

void QPSPrinterPrivate::flushPage( bool last )
{
    if ( last && !pageBuffer )
	return;
    bool pageFonts = ( buffer == 0 );
    if ( buffer &&
//         ( last || pagesInBuffer++ > -1 ||
//           ( pagesInBuffer > 4 && buffer->size() > 262144 ) ) )
         (last || buffer->size() > 2000000) )
    {
//        qDebug("emiting header at page %d", pageCount );
        emitHeader( last );
    }
    outStream << "%%Page: "
              << pageCount << ' ' << pageCount << endl
              << "%%BeginPageSetup\n"
              << "QI\n";
    if ( pageFonts ) {
        //qDebug("page fonts for page %d", pageCount);
        // we have already downloaded the header. Maybe we have page fonts here
        QDictIterator<QPSPrinterFontPrivate> it(fonts);
        while (it.current()) {
            it.current()->download( outStream, FALSE ); // FALSE means its for the page only
            ++it;
        }
    }
    outStream  << "%%EndPageSetup\n";
    if ( pageBuffer )
	outStream.writeRawBytes( pageBuffer->buffer().data(),
				 pageBuffer->buffer().size() );
    outStream << "\nQP\n";
    pageCount++;
}

// ================ PSPrinter class ========================

QPSPrinter::QPSPrinter( QPrinter *prt, int fd )
    : QPaintDevice( QInternal::Printer | QInternal::ExternalDevice )
{
    d = new QPSPrinterPrivate( prt, fd );
}


QPSPrinter::~QPSPrinter()
{
    if ( d->fd >= 0 )
#if defined(_OS_WIN32_)
        ::_close( d->fd );
#else
        ::close( d->fd );
#endif
    delete d;
}


bool QPSPrinter::cmd( int c , QPainter *paint, QPDevCmdParam *p )
{
    if ( c == PdcBegin ) {              // start painting
        d->pagesInBuffer = 0;
        d->buffer = new QBuffer();
        d->buffer->open( IO_WriteOnly );
        d->outStream.setEncoding( QTextStream::Latin1 );
        d->outStream.setDevice( d->buffer );
        d->fontBuffer = new QBuffer();
        d->fontBuffer->open( IO_WriteOnly );
        d->fontStream.setEncoding( QTextStream::Latin1 );
        d->fontStream.setDevice( d->fontBuffer );
        d->headerFontNumber = 0;
        d->pageCount           = 1;                // initialize state
        d->dirtyMatrix         = TRUE;
        d->dirtyClipping    = TRUE;
        d->dirtyNewPage        = TRUE;
        d->firstClipOnPage  = TRUE;
        d->boundingBox = QRect( 0, 0, -1, -1 );
        d->fontsUsed = QString::fromLatin1("");

        QPaintDeviceMetrics m( d->printer );
        d->scale = 72. / ((float) m.logicalDpiY());

        return TRUE;
    }

    if ( c == PdcEnd ) {                        // painting done
        bool pageCountAtEnd = (d->buffer != 0);
        d->flushPage( TRUE );

        d->outStream << "%%Trailer\n";
        if ( pageCountAtEnd )
            d->outStream << "%%Pages: " << d->pageCount - 1 << "\n" <<
		wrapDSC( "%%DocumentFonts: " + d->fontsUsed );
        d->outStream << "%%EOF\n";
        d->outStream.unsetDevice();
	if ( d->outDevice )
	    d->outDevice->close();
        if ( d->fd >= 0 )
            ::close( d->fd );
        d->fd = -1;
        delete d->outDevice;
        d->outDevice = 0;
    }

    if ( c >= PdcDrawFirst && c <= PdcDrawLast ) {
        if ( !paint )
            return FALSE; // sanity
        if ( d->dirtyNewPage )
            d->initPage( paint );
        if ( d->dirtyMatrix )
            d->matrixSetup( paint );
        if ( d->dirtyClipping ) // Must be after matrixSetup and initPage
            d->clippingSetup( paint );
        if ( d->dirtypen ) {
            // we special-case for narrow solid lines with the default
            // cap and join styles
            if ( d->cpen.style() == Qt::SolidLine && d->cpen.width() == 0 &&
                 d->cpen.capStyle() == Qt::FlatCap &&
                 d->cpen.joinStyle() == Qt::MiterJoin )
                d->pageStream << color( d->cpen.color(), d->printer ) << "P1\n";
            else
                d->pageStream << (int)d->cpen.style() << ' ' << d->cpen.width()
                       << ' ' << color( d->cpen.color(), d->printer )
                       << psCap( d->cpen.capStyle() )
                       << psJoin( d->cpen.joinStyle() ) << "PE\n";
            d->dirtypen = FALSE;
        }
        if ( d->dirtybrush ) {
            // we special-case for nobrush and solid white, since
            // those are the two most common brushes
            if ( d->cbrush.style() == Qt::NoBrush )
                d->pageStream << "NB\n";
            else if ( d->cbrush.style() == Qt::SolidPattern &&
                      d->cbrush.color() == Qt::white )
                d->pageStream << "WB\n";
            else
                d->pageStream << (int)d->cbrush.style() << ' '
                       << color( d->cbrush.color(), d->printer ) << "BR\n";
            d->dirtybrush = FALSE;
        }
	if ( d->dirtyBkColor ) {
	    d->pageStream << color( d->bkColor, d->printer ) << "BC\n";
	    d->dirtyBkColor = FALSE;
	}
	if ( d->dirtyBkMode ) {
	    if ( d->bkMode == Qt::TransparentMode )
		d->pageStream << "/OMo false d\n";
	    else
		d->pageStream << "/OMo true d\n";
	    d->dirtyBkMode = FALSE;
	}
    }

    switch( c ) {
    case PdcDrawPoint:
        d->pageStream << POINT(0) << "P\n";
        break;
    case PdcMoveTo:
        d->pageStream << POINT(0) << "M\n";
        break;
    case PdcLineTo:
        d->pageStream << POINT(0) << "L\n";
        break;
    case PdcDrawLine:
        if ( p[0].point->y() == p[1].point->y() )
            d->pageStream << POINT(1) << p[0].point->x() << " HL\n";
        else if ( p[0].point->x() == p[1].point->x() )
            d->pageStream << POINT(1) << p[0].point->y() << " VL\n";
        else
            d->pageStream << POINT(1) << POINT(0) << "DL\n";
        break;
    case PdcDrawRect:
        d->pageStream << RECT(0) << "R\n";
        break;
    case PdcDrawRoundRect:
        d->pageStream << RECT(0) << INT_ARG(1) << INT_ARG(2) << "RR\n";
        break;
    case PdcDrawEllipse:
        d->pageStream << RECT(0) << "E\n";
        break;
    case PdcDrawArc:
        d->pageStream << RECT(0) << INT_ARG(1) << INT_ARG(2) << "A\n";
        break;
    case PdcDrawPie:
        d->pageStream << RECT(0) << INT_ARG(1) << INT_ARG(2) << "PIE\n";
        break;
    case PdcDrawChord:
        d->pageStream << RECT(0) << INT_ARG(1) << INT_ARG(2) << "CH\n";
        break;
    case PdcDrawLineSegments:
        if ( p[0].ptarr->size() > 0 ) {
            QPointArray a = *p[0].ptarr;
            QPoint pt;
            d->pageStream << "NP\n";
            for ( int i=0; i<(int)a.size(); i+=2 ) {
                pt = a.point( i );
                d->pageStream << XCOORD(pt.x()) << ' '
                       << YCOORD(pt.y()) << " MT\n";
                pt = a.point( i+1 );
                d->pageStream << XCOORD(pt.x()) << ' '
                       << YCOORD(pt.y()) << " LT\n";
            }
            d->pageStream << "QS\n";
        }
        break;
    case PdcDrawPolyline:
        if ( p[0].ptarr->size() > 1 ) {
            QPointArray a = *p[0].ptarr;
            QPoint pt = a.point( 0 );
            d->pageStream << "NP\n"
                   << XCOORD(pt.x()) << ' ' << YCOORD(pt.y()) << " MT\n";
            for ( int i=1; i<(int)a.size(); i++ ) {
                pt = a.point( i );
                d->pageStream << XCOORD(pt.x()) << ' '
                       << YCOORD(pt.y()) << " LT\n";
            }
            d->pageStream << "QS\n";
        }
        break;
    case PdcDrawPolygon:
        if ( p[0].ptarr->size() > 2 ) {
            QPointArray a = *p[0].ptarr;
            if ( p[1].ival )
                d->pageStream << "/WFi true d\n";
            QPoint pt = a.point(0);
            d->pageStream << "NP\n";
            d->pageStream << XCOORD(pt.x()) << ' '
                   << YCOORD(pt.y()) << " MT\n";
            for( int i=1; i<(int)a.size(); i++) {
                pt = a.point( i );
                d->pageStream << XCOORD(pt.x()) << ' '
                       << YCOORD(pt.y()) << " LT\n";
            }
            d->pageStream << "CP BF QS\n";
            if ( p[1].ival )
                d->pageStream << "/WFi false d\n";
        }
        break;
    case PdcDrawCubicBezier:
        if ( p[0].ptarr->size() == 4 ) {
            d->pageStream << "NP\n";
            QPointArray a = *p[0].ptarr;
            d->pageStream << XCOORD(a[0].x()) << ' '
                   << YCOORD(a[0].y()) << " MT ";
            for ( int i=1; i<4; i++ ) {
                d->pageStream << XCOORD(a[i].x()) << ' '
                       << YCOORD(a[i].y()) << ' ';
            }
            d->pageStream << "BZ\n";
        }
        break;
    case PdcDrawText2: {
        uint spaces = 0;
        QString tmp = *p[1].str;
        int script = p[2].ival;
        while( spaces < tmp.length() && tmp[(int)spaces] == ' ' )
            spaces++;
        if ( spaces )
            tmp = tmp.mid( spaces, tmp.length() );
        while ( tmp.length() > 0 && tmp[(int)tmp.length()-1].isSpace() )
            tmp.truncate( tmp.length()-1 );
        if( tmp.length() == 0 )
            break;
        if ( d->currentSet != d->currentUsed || d->scriptUsed != script || !d->currentFontFile ) {
            d->currentUsed = d->currentSet;
            d->setFont( d->currentSet, script );
        }
        if( d->currentFontFile ) // better not crash in case somethig goes wrong.
            d->currentFontFile->drawText( d->pageStream, spaces, *p[0].point, tmp, d, paint);
        break;
    }
    case PdcDrawText2Formatted:
        return FALSE;                   // uses QPainter instead
    case PdcDrawPixmap: {
        if ( p[1].pixmap->isNull() )
            break;
        QRect r = *p[0].rect;
        QImage img;
        img = *(p[1].pixmap);
        d->drawImage( paint, r.x(), r.y(), r.width(), r.height(), img );
        break;
    }
    case PdcDrawImage: {
        if ( p[1].image->isNull() )
            break;
        QRect r = *(p[0].rect);
        QImage img = *(p[1].image);
        d->drawImage( paint, r.x(), r.y(), r.width(), r.height(), img );
        break;
    }
    case PdcSetBkColor:
    {
	if ( d->bkColor != *(p[0].color) ) {
	    d->bkColor = *(p[0].color);
	    d->dirtyBkColor = TRUE;
	}
        break;
    }
    case PdcSetBkMode:
    {
	if ( d->bkMode != p[0].ival ) {
	    d->bkMode = (Qt::BGMode) p[0].ival;
	    d->dirtyBkMode = TRUE;
	}
        break;
    }
    case PdcSetROP:
#if defined(CHECK_RANGE)
        if ( p[0].ival != Qt::CopyROP )
            qWarning( "QPrinter: Raster operation setting not supported" );
#endif
        break;
    case PdcSetBrushOrigin:
        break;
    case PdcSetFont:
        d->currentSet = *(p[0].font);
	d->fm = paint->fontMetrics();
        // turn these off - they confuse the 'avoid font change' logic
        d->currentSet.setUnderline( FALSE );
        d->currentSet.setStrikeOut( FALSE );
        break;
    case PdcSetPen:
        if ( d->cpen != *(p[0].pen) ) {
            d->dirtypen = TRUE;
            d->cpen = *(p[0].pen);
        }
        break;
    case PdcSetBrush:
        if ( p[0].brush->style() == Qt::CustomPattern ) {
#if defined(CHECK_RANGE)
            qWarning( "QPrinter: Pixmap brush not supported" );
#endif
            return FALSE;
        }
        if ( d->cbrush != *(p[0].brush) ) {
            d->dirtybrush = TRUE;
            d->cbrush = *(p[0].brush);
        }
        break;
    case PdcSetTabStops:
    case PdcSetTabArray:
        return FALSE;
    case PdcSetUnit:
        break;
    case PdcSetVXform:
    case PdcSetWindow:
    case PdcSetViewport:
    case PdcSetWXform:
    case PdcSetWMatrix:
    case PdcRestoreWMatrix:
        d->dirtyMatrix = TRUE;
        break;
    case PdcSetClip:
        d->dirtyClipping = TRUE;
        break;
    case PdcSetClipRegion:
        d->dirtyClipping = TRUE;
        break;
    case NewPage:
        d->flushPage();
        d->dirtyNewPage = TRUE;
        //qDebug("new page");
        break;
    case AbortPrinting:
        break;
    default:
        break;
    }
    return TRUE;
}


#endif // QT_NO_PRINTER
