From 7d523fac5a54f8e695e56f4877fa852608465bc7 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Sun, 10 Jul 2011 15:24:15 -0500 Subject: Add Qt3 development HEAD version --- main/dependencies/qt3/src/kernel/qpsprinter.cpp | 6582 +++++++++++++++++++++++ 1 file changed, 6582 insertions(+) create mode 100644 main/dependencies/qt3/src/kernel/qpsprinter.cpp (limited to 'main/dependencies/qt3/src/kernel/qpsprinter.cpp') diff --git a/main/dependencies/qt3/src/kernel/qpsprinter.cpp b/main/dependencies/qt3/src/kernel/qpsprinter.cpp new file mode 100644 index 000000000..039b5875c --- /dev/null +++ b/main/dependencies/qt3/src/kernel/qpsprinter.cpp @@ -0,0 +1,6582 @@ +/********************************************************************** +** +** Implementation of QPSPrinter class +** +** Created : 941003 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.QPL +** included in the packaging of this file. Licensees holding valid Qt +** Commercial 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 WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qplatformdefs.h" + +// POSIX Large File Support redefines open -> open64 +#if defined(open) +# undef open +#endif + +// POSIX Large File Support redefines truncate -> truncate64 +#if defined(truncate) +# undef truncate +#endif + +#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 "qfile.h" +#include "qbuffer.h" +#include "qintdict.h" +#include "qtextcodec.h" +#include "qsettings.h" +#include "qmap.h" +#include "qfontdatabase.h" +#include "qregexp.h" +#include "qbitmap.h" +#include + +#if defined(Q_OS_WIN32) +#include +#ifdef Q_PRINTER_USE_TYPE42 +#include +#endif +#else +#include +#include +#endif + +#ifdef Q_WS_X11 +#include "qt_x11_p.h" +#ifdef None +#undef None +#endif +#ifdef GrayScale +#undef GrayScale +#endif +#endif + +#if defined( Q_WS_X11 ) || defined (Q_WS_QWS) +#include "qfontdata_p.h" +#include "qfontengine_p.h" +#include "qtextlayout_p.h" +#include "qtextengine_p.h" +extern bool qt_has_xft; +#endif + +static bool qt_gen_epsf = FALSE; +static bool embedFonts = TRUE; + +Q_EXPORT void qt_generate_epsf( bool b ) +{ + qt_gen_epsf = b; +} + +static const char *const ps_header = +"/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\n" +"/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D\n" +"/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D\n" +"/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read\n" +"pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end\n" +"d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi\n" +"false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88\n" +"0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{\n" +"LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{\n" +"gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get\n" +"SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7\n" +"bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL\n" +"0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB\n" +"exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL\n" +"ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1\n" +"eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if\n" +"64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3\n" +"i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll\n" +"putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3\n" +"1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D\n" +"/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3\n" +"colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1\n" +"QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray\n" +"QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2\n" +"add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq\n" +"{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC\n" +"imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel\n" +"where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d\n" +"/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7\n" +"DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d\n" +"/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height\n" +"h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4\n" +"DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{\n" +"pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC\n" +"WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{\n" +"1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}\n" +"if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4\n" +"2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg\n" +"RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0\n" +"exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if\n" +"BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h\n" +"add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0\n" +"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\n" +"}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h\n" +"D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div\n" +"add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1\n" +"ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT\n" +"0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy\n" +"MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R\n" +"{/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{/h\n" +"ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry\n" +"D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y\n" +"w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul\n" +"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\n" +"x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0\n" +"-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h\n" +"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 scale\n" +"NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}\n" +"D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT\n" +"x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP\n" +"ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255\n" +"div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0\n" +"B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25\n" +"/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true\n" +"exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3\n" +"-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch\n" +"/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup\n" +"maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding\n" +"fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length\n" +"dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end\n" +"definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}\n" +"D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty\n" +"MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch\n" +"stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT\n" +"1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0\n" +"exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore\n" +"showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop\n" +"pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D\n" +"/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt\n" +"ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}\n" +"D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D\n"; + +// 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://partners.adobe.com:80/asn/developer/type/unicodegn.html +// +// IMPORTANT NOTE: +// 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 + // The names below are in the PU area of Unicode, but needed to get a correct mapping of the symbol font + { 0xF6D9, "copyrightserif" }, + { 0xF6DA, "registerserif" }, + { 0xF6DB, "trademarkserif" }, + { 0xF8E5, "radicalex" }, + { 0xF8E6, "arrowvertex" }, + { 0xF8E7, "arrowhorizex" }, + { 0xF8E8, "registersans" }, + { 0xF8E9, "copyrightsans" }, + { 0xF8EA, "trademarksans" }, + { 0xF8EB, "parenlefttp" }, + { 0xF8EC, "parenleftex" }, + { 0xF8ED, "parenleftbt" }, + { 0xF8EE, "bracketlefttp" }, + { 0xF8EF, "bracketleftex" }, + { 0xF8F0, "bracketleftbt" }, + { 0xF8F1, "bracelefttp" }, + { 0xF8F2, "braceleftmid" }, + { 0xF8F3, "braceleftbt" }, + { 0xF8F4, "braceex" }, + { 0xF8F5, "integralex" }, + { 0xF8F6, "parenrighttp" }, + { 0xF8F7, "parenrightex" }, + { 0xF8F8, "parenrightbt" }, + { 0xF8F9, "bracketrighttp" }, + { 0xF8FA, "bracketrightex" }, + { 0xF8FB, "bracketrightbt" }, + { 0xF8FC, "bracerighttp" }, + { 0xF8FD, "bracerightmid" }, + { 0xF8FE, "bracerightbt" }, + // End of extensions needed for symbols + { 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"; +} + +static QString toString( const float num ) +{ + return QString::number( num, 'f', 3 ); +} + +// ----------------------------- 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 &img, const QImage &mask ); + 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 headerFontNames; + QDict pageFontNames; + QDict 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; +#ifndef QT_NO_TEXTCODEC + QTextCodec * currentFontCodec; +#endif + 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, const QString &ps, const QFont &f, const QString &key, + QPSPrinterPrivate *d ); + virtual void download(QTextStream& s, bool global); + virtual void drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint); + virtual unsigned short mapUnicode( unsigned short unicode ); + void downloadMapping( QTextStream &s, bool global ); + QString glyphName( unsigned short glyphindex, bool *glyphSet = 0 ); + 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; } + + bool operator == ( const QPSPrinterFontPrivate &other ) { + return other.psname == psname; + } + inline void setSymbol() { symbol = TRUE; } + +protected: + QString psname; + QStringList replacementList; + + QMap subset; // unicode subset in the global font + QMap page_subset; // subset added in this page + int subsetCount; + int pageSubsetCount; + bool global_dict; + bool downloaded; + bool symbol; +}; + +// ------------------- end of class declarations --------------------------- + +// -------------------------------------------------------------- +// beginning of font related methods +// -------------------------------------------------------------- + + +static int getPsFontType( const QFontEngine *fe ) +{ + int weight = fe->fontDef.weight; + bool italic = fe->fontDef.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 QFontEngine *fe, QString &ps, const psfont *psf = 0 ) +{ + int type = getPsFontType( fe ); + + 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 QString makePSFontName( const QFontEngine *fe, int *listpos = 0, int *ftype = 0 ) +{ + QString ps; + int i; + + QString family = fe->fontDef.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] == '[') { + if ( ps[i-1] == ' ' ) + ps.truncate (i-1); + else + ps.truncate (i); + break; + } + 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( fe, ps, psf ); + + if ( listpos ) + *listpos = i; + if ( ftype ) + *ftype = type; + return ps; +} + +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 ) + " " + + toString( xscale / psf[type].xscale ) + " " + + toString( psf[type].slant ) + " ]"; + list.append( ps ); + ++replacements; + } +} + +static QStringList makePSFontNameList( const QFontEngine *fe, 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( fe, &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 " + + toString( psf[type].slant ) + " ]"; + } else { + ps = "[ /" + ps + " 1.0 0.0 ]"; + // only add default replacement fonts in case this font was unknown. + if ( fe->fontDef.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 inline 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; + symbol = 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()); +} + +static inline const char *toHex( uchar u ) +{ + static char hexVal[3]; + int i = 1; + while ( i >= 0 ) { + ushort hex = (u & 0x000f); + if ( hex < 0x0a ) + hexVal[i] = '0'+hex; + else + hexVal[i] = 'A'+(hex-0x0a); + u = u >> 4; + i--; + } + hexVal[2] = '\0'; + return hexVal; +} + +static inline const char *toHex( ushort u ) +{ + static char hexVal[5]; + int i = 3; + while ( i >= 0 ) { + ushort hex = (u & 0x000f); + if ( hex < 0x0a ) + hexVal[i] = '0'+hex; + else + hexVal[i] = 'A'+(hex-0x0a); + u = u >> 4; + i--; + } + hexVal[4] = '\0'; + return hexVal; +} + +static inline const char * toInt( int i ) +{ + static char intVal[20]; + intVal[19] = 0; + int pos = 19; + if ( i == 0 ) { + intVal[--pos] = '0'; + } else { + bool neg = FALSE; + if ( i < 0 ) { + neg = TRUE; + i = -i; + } + while ( i ) { + int dec = i%10; + intVal[--pos] = '0'+dec; + i /= 10; + } + if ( neg ) + intVal[--pos] = '-'; + } + return intVal+pos; +} + +void QPSPrinterFontPrivate::drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint) +{ + int len = engine->length( item ); + QScriptItem &si = engine->items[item]; + + int x = p.x() + si.x; + int y = p.y() + si.y; + if ( y != d->textY || d->textY == 0 ) + stream << y << " Y"; + d->textY = y; + + stream << "<"; + if ( si.analysis.bidiLevel % 2 ) { + for ( int i = len-1; i >=0; i-- ) + stream << toHex( mapUnicode(text.unicode()[i].unicode()) ); + } else { + for ( int i = 0; i < len; i++ ) + stream << toHex( mapUnicode(text.unicode()[i].unicode()) ); + } + stream << ">"; + + stream << si.width << " " << 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 << " AT\n"; + +} + + +QString QPSPrinterFontPrivate::defineFont( QTextStream &stream, const 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::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; +} + +// This map is used for symbol fonts to get the correct glyph names for the latin range +static const unsigned short symbol_map[0x100] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220b, + 0x0028, 0x0029, 0x2217, 0x002b, 0x002c, 0x2212, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + + 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393, + 0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f, + 0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9, + 0x039e, 0x03a8, 0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f, + 0xf8e5, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, + 0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf, + 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9, + 0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d, 0x223c, 0x007f, + + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x20ac, 0x03d2, 0x2023, 0x2264, 0x2044, 0x221e, 0x0192, 0x2263, + 0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193, + 0x00b0, 0x00b1, 0x2033, 0x2265, 0x00d7, 0x221d, 0x2202, 0x2022, + 0x00f7, 0x2260, 0x2261, 0x2248, 0x2026, 0xf8e6, 0xf8e7, 0x21b5, + + 0x2135, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229, + 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209, + 0x2220, 0x2207, 0xf6da, 0xf6d9, 0xf6db, 0x220f, 0x221a, 0x22c5, + 0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3, + 0x25ca, 0x2329, 0xf8e8, 0xf8e9, 0xf8ea, 0x2211, 0xf8eb, 0xf8ec, + 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0, 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, + 0x0000, 0x232a, 0x222b, 0x2320, 0xf8f5, 0x2321, 0xf8f6, 0xf8f7, + 0xf8f8, 0xf8f9, 0xf8fa, 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0x0000, +}; + +QString QPSPrinterFontPrivate::glyphName( unsigned short glyphindex, bool *glyphSet ) +{ + QString glyphname; + int l = 0; + unsigned short unicode = unicode_for_glyph( glyphindex ); + if (symbol && unicode < 0x100) { + // map from latin1 to symbol + unicode = symbol_map[unicode]; + } + if ( !unicode && glyphindex ) { + glyphname = "gl"; + glyphname += toHex( glyphindex ); + } else { + while( unicodetoglyph[l].u < unicode ) + l++; + if ( unicodetoglyph[l].u == unicode ) { + glyphname = unicodetoglyph[l].g; + if ( glyphSet ) { + int other = 0; + switch ( unicode ) { + // some glyph names are duplicate in postscript. Make sure we give the + // duplicate a different name to avoid infinite recursion + case 0x0394: + other = 0x2206; + break; + case 0x03a9: + other = 0x2126; + break; + case 0x0162: + other = 0x021a; + break; + case 0x2215: + other = 0x2044; + break; + case 0x00ad: + other = 0x002d; + break; + case 0x02c9: + other = 0x00af; + break; + case 0x03bc: + other = 0x00b5; + break; + case 0x2219: + other = 0x00b7; + break; + case 0x00a0: + other = 0x0020; + break; + case 0x0163: + other = 0x021b; + break; + default: + break; + } + if ( other ) { + int oglyph = glyph_for_unicode( other ); + if( oglyph && oglyph != glyphindex && glyphSet[oglyph] ) { + glyphname = "uni"; + glyphname += toHex( unicode ); + } + } + } + } else { + glyphname = "uni"; + glyphname += toHex( 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 ) +{ + uchar rangeOffset = 0; + uchar numRanges = (uchar)(subsetCount/256 + 1); + uchar range; + QMap *subsetDict = ⊂ + 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::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 = "%% Font Page "; + vector += toHex((uchar)(range + rangeOffset)); + vector += "\n/"; + vector += psname; + vector += "-ENC-"; + vector += toHex((uchar)(range + rangeOffset)); + vector += " [\n"; + + QString line; + for(int k=0; k<256; k++ ) { + int c = range*256 + k; + unsigned short glyph = inverse[c]; + glyphname = glyphName( glyph ); + if ( line.length() + glyphname.length() > 76 ) { + vector += line; + vector += "\n"; + line = ""; + } + line += "/" + glyphname; + } + vector += line; + vector += "] def\n"; + s << vector; + } + + delete [] inverse; + + // DEFINE BASE FONTS + + for (range=0; range < numRanges; range++) { + s << "/"; + s << psname; + s << "-Uni-"; + s << toHex((uchar)(range + rangeOffset)); + s << " "; + s << psname; + s << "-ENC-"; + s << toHex((uchar)(range + rangeOffset)); + if ( embedded() && embedFonts ) { + 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++) { + s << "/"; + s << psname; + s << "-Uni-"; + s << toHex( 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 QFontEngine *f, QByteArray& data); + virtual void download(QTextStream& s, bool global); + virtual void drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint); + // virtual ~QPSPrinterFontTTF(); + + virtual bool embedded() { return TRUE; } +private: + QByteArray data; + QMemArray uni2glyph; // to speed up lookups + QMemArray glyph2uni; // to speed up lookups + bool defective; // if we can't process this file + + BYTE* getTable(const char *); + 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, bool *glyphSet); + BYTE* charprocFindGlyphData(int charindex); + void charprocComposite(BYTE *glyph, QTextStream& s, bool *glyphSet); + 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 + +// #define DEBUG_TRUETYPE + +QPSPrinterFontTTF::QPSPrinterFontTTF(const QFontEngine *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; + qDebug("couldn't find name table" ); + 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); + + } + psname.replace(' ', '-'); + psname.replace("/", ""); + if (psname.isEmpty()) + psname = makePSFontName(f); + + //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"); + qWarning("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; + qDebug("no maxp table in font"); + return; + } + numGlyphs = getUSHORT( maxp + 4 ); +// qDebug("number of glyphs is %d", numGlyphs); + replacementList = makePSFontNameList( f, psname ); + uni2glyphSetup(); +} + + +void QPSPrinterFontTTF::drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint) +{ + // we draw glyphs here to get correct shaping of arabic and indic + QScriptItem &si = engine->items[item]; + engine->shape( item ); + int len = si.num_glyphs; + + int x = p.x() + si.x; + int y = p.y() + si.y; + if ( y != d->textY || d->textY == 0 ) + stream << y << " Y"; + d->textY = y; + + QCString xyarray; + int xo = 0; + int yo = 0; + + glyph_t *glyphs = engine->glyphs( &si ); + advance_t *advances = engine->advances( &si ); + qoffset_t *offsets = engine->offsets( &si ); +#ifdef Q_WS_X11 + int type = si.fontEngine->type(); + bool glyphIndices = (type == QFontEngine::Xft); + // This helps us get arabic for XLFD fonts working. In that case we have a Unicode + // cmap (== 0), and the glyphs array contains the shaped string. + bool useGlyphAsUnicode = (type == QFontEngine::XLFD && si.fontEngine->cmap() == 0); +#else // Q_WS_QWS + const bool glyphIndices = FALSE; + const bool useGlyphAsUnicode = TRUE; +#endif + stream << "<"; + if ( si.analysis.bidiLevel % 2 ) { + for ( int i = len-1; i >=0; i-- ) { + // map unicode is not really the correct name, as we map glyphs, but we also download glyphs, so this works + unsigned short glyph; + if (glyphIndices) + glyph = glyphs[i]; + else + glyph = glyph_for_unicode(useGlyphAsUnicode ? glyphs[i] : text.unicode()[i].unicode()); + stream << toHex(mapUnicode(glyph)); + if ( i != len-1 ) { + xyarray += toInt( xo + offsets[i].x + advances[i+1] ); + xyarray += " "; + xyarray += toInt( yo + offsets[i].y ); + xyarray += " "; + xo = -offsets[i].x; + yo = -offsets[i].y; + } + } + } else { + for ( int i = 0; i < len; i++ ) { + // map unicode is not really the correct name, as we map glyphs, but we also download glyphs, so this works + unsigned short glyph; + if (glyphIndices) + glyph = glyphs[i]; + else + glyph = glyph_for_unicode(useGlyphAsUnicode ? glyphs[i] : text.unicode()[i].unicode()); + stream << toHex(mapUnicode(glyph)); + if ( i ) { + xyarray += toInt( xo + offsets[i].x + advances[i-1] ); + xyarray += " "; + xyarray += toInt( yo + offsets[i].y ); + xyarray += " "; + xo = -offsets[i].x; + yo = -offsets[i].y; + } + } + } + stream << ">"; + + stream << "[" << xyarray << "0 0]"; + stream << si.width << " " << 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 << " XYT\n"; + +} + + +void QPSPrinterFontTTF::download(QTextStream& s,bool global) +{ + emitPSFontNameList( s, psname, replacementList); + if ( !embedFonts ) { + downloadMapping(s, global); + return; + } + + //qDebug("downloading ttf font %s", psname.latin1() ); + //qDebug("target type=%d", target_type); + global_dict = global; + QMap *subsetDict = ⊂ + if ( !global ) + subsetDict = &page_subset; + + downloaded = TRUE; + + 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::iterator it; + for( it = subsetDict->begin(); it != subsetDict->end(); ++it ) { + subsetGlyph( it.key(), 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, glyphset ); + s << "{"; + charproc(x,s, glyphset); + 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; +} + +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; + int map = -1; + bool symbol = TRUE; + for (i=0; i 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; inum_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; jnum_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; + 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; + } + } + if ( j == start ) { + xi[1] = cd->xcoor[end]; + yi[1] = cd->ycoor[end]; + } else { + xi[1] = cd->xcoor[j - 1]; + yi[1] = cd->ycoor[j - 1]; + } + if ( j == end ) { + xi[2] = cd->xcoor[start]; + yi[2] = cd->ycoor[start]; + } else { + xi[2] = cd->xcoor[j + 1]; + yi[2] = cd->ycoor[j + 1]; + } + return area( xi, yi, 3 ); +} + +/* +** 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; inum_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; inum_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\n",cd->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; + subsetGlyph( glyphIndex, glyphset ); + //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, bool *glyphSet) +{ + 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, glyphSet ); + 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, bool *glyphSet ) +{ + 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, glyphSet); + } + + stack_end(s); +} /* end of tt_type3_charproc() */ + + +// ================== PFA ==================== + +class QPSPrinterFontPFA + : public QPSPrinterFontPrivate { +public: + QPSPrinterFontPFA(const QFontEngine *f, QByteArray& data); + virtual void download(QTextStream& s, bool global); + virtual bool embedded() { return TRUE; } +private: + QByteArray data; +}; + +QPSPrinterFontPFA::QPSPrinterFontPFA(const QFontEngine *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) +{ + emitPSFontNameList( s, psname, replacementList); + + if ( !embedFonts ) { + downloadMapping(s, global); + return; + } + + //qDebug("downloading pfa font %s", psname.latin1() ); + char* p = data.data(); + + 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 QFontEngine *f, QByteArray& data); + virtual void download(QTextStream& s, bool global); + virtual bool embedded() { return TRUE; } +private: + QByteArray data; +}; + +QPSPrinterFontPFB::QPSPrinterFontPFB(const QFontEngine *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) +{ + emitPSFontNameList( s, psname, replacementList); + + if ( !embedFonts ) { + downloadMapping(s, global); + return; + } + + //qDebug("downloading pfb font %s", psname.latin1() ); + unsigned char* p = (unsigned char*) data.data(); + int pos; + int len; + int typ; + + int hexcol = 0; + int line_length = 64; + + 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++; + + //qDebug("font block type %d len %d",typ,len); + + int end = pos + len; + if (typ==1) { + while (pos < end) { + if (hexcol > 0) { + s << "\n"; + hexcol = 0; + } + //qWarning(QString::fromLatin1((char*)(p+pos),1)); + if (p[pos] == '\r' || p[pos] == '\n') { + s << "\n"; + while (pos < end && (p[pos] == '\r' || p[pos] == '\n')) + pos++; + } else { + s << QString::fromLatin1((char*)(p+pos),1); + pos++; + } + } + } + if (typ==2) { + static const char *hexchar = "0123456789abcdef"; + while (pos < end) { + /* trim hexadecimal lines to line_length columns */ + if (hexcol >= line_length) { + s << "\n"; + hexcol = 0; + } + s << QString::fromLatin1(hexchar+((p[pos] >> 4) & 0xf),1) + << QString::fromLatin1(hexchar+((p[pos] ) & 0xf),1); + pos++; + hexcol += 2; + } + } + } + s << "% End of font resource\n"; + downloadMapping( s, global ); +} + +// ================== AFontFileNotFound ============ + + + +class QPSPrinterFontNotFound + : public QPSPrinterFontPrivate { +public: + QPSPrinterFontNotFound(const QFontEngine* f); + virtual void download(QTextStream& s, bool global); +private: + QByteArray data; +}; + +QPSPrinterFontNotFound::QPSPrinterFontNotFound(const QFontEngine* f) +{ + psname = makePSFontName( f ); + replacementList = makePSFontNameList( f ); +} + +void QPSPrinterFontNotFound::download(QTextStream& s, bool) +{ + //qDebug("downloading not found font %s", psname.latin1() ); + emitPSFontNameList( s, psname, replacementList ); + s << "% No embeddable font for "; + s << psname; + s << " found\n"; + QPSPrinterFontPrivate::download(s, TRUE); +} + +#ifndef QT_NO_TEXTCODEC +// =================== A font file for asian ============ + +class QPSPrinterFontAsian + : public QPSPrinterFontPrivate { +public: + QPSPrinterFontAsian() + : QPSPrinterFontPrivate(), codec( 0 ) {} + void download(QTextStream& s, bool global); + QString defineFont( QTextStream &stream, const QString &ps, const QFont &f, const QString &key, + QPSPrinterPrivate *d ); + void drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint ); + + QString makePSFontName( const QFontEngine *f, int type ) const; + virtual QString extension() const = 0; + + QTextCodec *codec; +}; + +QString QPSPrinterFontAsian::makePSFontName( const QFontEngine *f, int type ) const +{ + QString ps; + int i; + + QString family = f->fontDef.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] == '[') { + if ( ps[i-1] == ' ' ) + ps.truncate (i-1); + else + ps.truncate (i); + break; + } + if ( i == 0 || ps[i-1] == ' ' ) { + ps[i] = ps[i].upper(); + if ( i ) + ps.remove( i-1, 1 ); + else + i++; + } else { + i++; + } + } + + 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; + } + + ps += extension(); + + return ps; +} + + +QString QPSPrinterFontAsian::defineFont( QTextStream &stream, const QString &ps, const QFont &f, + const QString &key, QPSPrinterPrivate *d) +{ + QString fontName; + QString fontName2; + + QString *tmp = d->headerFontNames.find( ps ); + + if ( d->buffer ) { + if ( tmp ) { + fontName = *tmp; + } else { + fontName.sprintf( "F%d", ++d->headerFontNumber ); + d->fontStream << "/" << fontName << " false " << ps << "List MF\n"; + d->headerFontNames.insert( ps, new QString( fontName ) ); + } + fontName2.sprintf( "F%d", ++d->headerFontNumber ); + d->fontStream << "/" << fontName2 << " " + << pointSize( f, d->scale ) << "/" << fontName << " DF\n"; + d->headerFontNames.insert( key, new QString( fontName2 ) ); + } else { + if ( tmp ) { + fontName = *tmp; + } else { + fontName.sprintf( "F%d", ++d->pageFontNumber ); + stream << "/" << fontName << " false " << ps << "List MF\n"; + d->pageFontNames.insert( ps, new QString( fontName ) ); + } + fontName2.sprintf( "F%d", ++d->pageFontNumber ); + stream << "/" << fontName2 << " " + << pointSize( f, d->scale ) << "/" << fontName << " DF\n"; + d->pageFontNames.insert( key, new QString( fontName2 ) ); + } + return fontName2; +} + + +void QPSPrinterFontAsian::download(QTextStream& s, bool) +{ + //qDebug("downloading asian font %s", psname.latin1() ); + s << "% Asian postscript font requested. Using " + << psname << endl; + emitPSFontNameList( s, psname, replacementList ); +} + +void QPSPrinterFontAsian::drawText( QTextStream &stream, const QPoint &p, QTextEngine *engine, int item, + const QString &text, QPSPrinterPrivate *d, QPainter *paint) +{ + int len = engine->length( item ); + QScriptItem &si = engine->items[item]; + + int x = p.x() + si.x; + int y = p.y() + si.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() ) + + " " + toString( d->fm.lineWidth() ) + " Tl"; + if ( paint->font().strikeOut() ) + mdf += " " + QString().setNum( y + d->fm.strikeOutPos() ) + + " " + toString( d->fm.lineWidth() ) + " Tl"; + QCString mb; + QCString out; + QString dummy( QChar(0x20) ); + + if ( si.analysis.bidiLevel % 2 ) { + for ( int i = len-1; i >= 0; i-- ) { + QChar ch = text.unicode()[i]; + if ( !ch.row() ) { + ; // ignore, we should never get here anyway + } else { + if ( codec ) { + dummy[0] = ch; + mb = codec->fromUnicode( dummy ); + } else + mb = " "; + + for ( unsigned int j = 0; j < mb.length (); j++ ) { + if ( mb.at(j) == '(' || mb.at(j) == ')' || mb.at(j) == '\\' ) + out += "\\"; + out += mb.at(j); + } + } + } + } else { + for ( int i = 0; i < len; i++ ) { + QChar ch = text.unicode()[i]; + if ( !ch.row() ) { + ; // ignore, we should never get here anyway + } else { + if ( codec ) { + dummy[0] = ch; + mb = codec->fromUnicode( dummy ); + } else + mb = " "; + + for ( unsigned int j = 0; j < mb.length (); j++ ) { + if ( mb.at(j) == '(' || mb.at(j) == ')' || mb.at(j) == '\\' ) + out += "\\"; + out += mb.at(j); + } + } + } + } + stream << "(" << out << ")" << si.width << " " << x << mdf << " AT\n"; +} + +// ----------- Japanese -------------- + +static const psfont Japanese1 [] = { + { "Ryumin-Light-H", 0, 100. }, + { "Ryumin-Light-H", 0.2, 100. }, + { "GothicBBB-Medium-H", 0, 100. }, + { "GothicBBB-Medium-H", 0.2, 100. } +}; + +static const psfont Japanese1a [] = { + { "GothicBBB-Medium-H", 0, 100. }, + { "GothicBBB-Medium-H", 0.2, 100. }, + { "Ryumin-Light-H", 0, 100. }, + { "Ryumin-Light-H", 0.2, 100. } +}; + +static const psfont Japanese2 [] = { + { "GothicBBB-Medium-H", 0, 100. }, + { "GothicBBB-Medium-H", 0.2, 100. }, + { "GothicBBB-Medium-H", 0, 100. }, + { "GothicBBB-Medium-H", 0.2, 100. } +}; + +static const psfont Japanese2a [] = { + { "Ryumin-Light-H", 0, 100. }, + { "Ryumin-Light-H", 0.2, 100. }, + { "Ryumin-Light-H", 0, 100. }, + { "Ryumin-Light-H", 0.2, 100. } +}; + + +// Wadalab fonts + +static const psfont WadaMin [] = { + { "WadaMin-Regular-H", 0, 100. }, + { "WadaMin-Regular-H", 0.2, 100. }, + { "WadaMin-Bold-H", 0, 100. }, + { "WadaMin-Bold-H", 0.2, 100. } +}; + +static const psfont WadaGo [] = { + { "WadaMaruGo-Regular-H", 0, 100. }, + { "WadaMaruGo-Regular-H", 0.2, 100. }, + { "WadaGo-Bold-H", 0, 100. }, + { "WadaGo-Bold-H", 0.2, 100. } +}; + +// Adobe Wadalab + +static const psfont WadaGoAdobe [] = { + { "WadaMaruGo-RegularH-Hojo-H", 0, 100. }, + { "WadaMaruGo-RegularH-Hojo-H", 0.2, 100. }, + { "WadaMaruGo-RegularH-Hojo-H", 0, 100. }, + { "WadaMaruGo-RegularH-Hojo-H", 0.2, 100. }, +}; +static const psfont WadaMinAdobe [] = { + { "WadaMin-RegularH-Hojo-H", 0, 100. }, + { "WadaMin-RegularH-Hojo-H", 0.2, 100. }, + { "WadaMin-RegularH-Hojo-H", 0, 100. }, + { "WadaMin-RegularH-Hojo-H", 0.2, 100. }, +}; + + +static const psfont * const Japanese1Replacements[] = { + Japanese1, Japanese1a, WadaMin, WadaGo, WadaMinAdobe, WadaGoAdobe, 0 +}; +static const psfont * const Japanese2Replacements[] = { + Japanese2, Japanese2a, WadaMin, WadaGo, WadaMinAdobe, WadaGoAdobe, 0 +}; + +class QPSPrinterFontJapanese + : public QPSPrinterFontAsian { +public: + QPSPrinterFontJapanese(const QFontEngine* f); + virtual QString extension() const; +}; + +QPSPrinterFontJapanese::QPSPrinterFontJapanese(const QFontEngine* f) +{ + codec = QTextCodec::codecForMib( 63 ); // jisx0208.1983-0 + + int type = getPsFontType( f ); + psname = makePSFontName( f, type ); + QString best = "[ /" + psname + " 1.0 0.0 ]"; + replacementList.append( best ); + + const psfont *const *replacements = ( psname.contains( "Helvetica" ) ? Japanese2Replacements : Japanese1Replacements ); + appendReplacements( replacementList, replacements, type ); +} + +QString QPSPrinterFontJapanese::extension() const +{ + return "-H"; +} + +// ----------- 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 +#if 0 // ### this is never used? +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. } +}; +#endif + +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 MunhwaGothic [] = { + { "MunhwaGothic-Regular-KSC-EUC-H", 0, 100. }, + { "MunhwaGothic-Regular-KSC-EUC-H", 0.2, 100. }, + { "MunhwaGothic-Bold-KSC-EUC-H", 0, 100. }, + { "MunhwaGothic-Bold-KSC-EUC-H", 0.2, 100. } +}; + +static const psfont MunhwaGungSeo [] = { + { "MunhwaGungSeo-Light-KSC-EUC-H", 0, 100. }, + { "MunhwaGungSeo-Light-KSC-EUC-H", 0.2, 100. }, + { "MunhwaGungSeo-Bold-KSC-EUC-H", 0, 100. }, + { "MunhwaGungSeo-Bold-KSC-EUC-H", 0.2, 100. } +}; + +static const psfont MunhwaGungSeoHeulim [] = { + { "MunhwaGungSeoHeulim-Light-KSC-EUC-H", 0, 100. }, + { "MunhwaGungSeoHeulim-Light-KSC-EUC-H", 0.2, 100. }, + { "MunhwaGungSeoHeulim-Bold-KSC-EUC-H", 0, 100. }, + { "MunhwaGungSeoHeulim-Bold-KSC-EUC-H", 0.2, 100. } +}; + +static const psfont MunhwaHoonMin [] = { + { "MunhwaHoonMin-Regular-KSC-EUC-H", 0, 100. }, + { "MunhwaHoonMin-Regular-KSC-EUC-H", 0.2, 100. }, + { "MunhwaHoonMin-Regular-KSC-EUC-H", 0, 100. }, + { "MunhwaHoonMin-Regular-KSC-EUC-H", 0.2, 100. } +}; + +static const psfont BaekmukGulim [] = { + { "Baekmuk-Gulim-KSC-EUC-H", 0, 100. }, + { "Baekmuk-Gulim-KSC-EUC-H", 0.2, 100. }, + { "Baekmuk-Gulim-KSC-EUC-H", 0, 100. }, + { "Baekmuk-Gulim-KSC-EUC-H", 0.2, 100. } +}; + +static const psfont * const KoreanReplacements[] = { + BaekmukGulim, SMGothic, Munhwa, MunhwaGothic, MKai, MunhwaGungSeo, + MunhwaGungSeoHeulim, MunhwaHoonMin, Helvetica, 0 +}; + +class QPSPrinterFontKorean + : public QPSPrinterFontAsian { +public: + QPSPrinterFontKorean(const QFontEngine* f); + QString extension() const; +}; + +QPSPrinterFontKorean::QPSPrinterFontKorean(const QFontEngine* f) +{ + codec = QTextCodec::codecForMib( 38 ); // eucKR + int type = getPsFontType( f ); + psname = makePSFontName( f, type ); + QString best = "[ /" + psname + " 1.0 0.0 ]"; + replacementList.append( best ); + appendReplacements( replacementList, KoreanReplacements, type ); +} + +QString QPSPrinterFontKorean::extension() const +{ + return "-KSC-EUC-H"; +} +// ----------- traditional chinese ------------ + +// Arphic Public License Big5 TrueType fonts (on Debian and CLE and others) +static const psfont ShanHeiSun [] = { + { "ShanHeiSun-Light-ETen-B5-H", 0, 100. }, + { "ShanHeiSun-Light-ETen-B5-H", 0.2, 100. }, + { "ShanHeiSun-Light-ETen-B5-H", 0, 100. }, + { "ShanHeiSun-Light-ETen-B5-H", 0.2, 100. }, +}; +static const psfont ZenKai [] = { + { "ZenKai-Medium-ETen-B5-H", 0, 100. }, + { "ZenKai-Medium-Italic-ETen-B5-H", 0.2, 100. }, + { "ZenKai-Medium-Bold-ETen-B5-H", 0, 100. }, + { "ZenKai-Medium-BoldItalic-ETen-B5-H", 0.2, 100. }, +}; + +// Fonts on Turbolinux +static const psfont SongB5 [] = { + { "B5-MSung-Light-ETen-B5-H", 0, 100. }, + { "B5-MSung-Italic-ETen-B5-H", 0, 100. }, + { "B5-MSung-Bold-ETen-B5-H", 0, 100. }, + { "B5-MSung-BoldItalic-ETen-B5-H", 0, 100. }, +}; +static const psfont KaiB5 [] = { + { "B5-MKai-Medium-ETen-B5-H", 0, 100. }, + { "B5-MKai-Italic-ETen-B5-H", 0, 100. }, + { "B5-MKai-Bold-ETen-B5-H", 0, 100. }, + { "B5-MKai-BoldItalic-ETen-B5-H", 0, 100. }, +}; +static const psfont HeiB5 [] = { + { "B5-MHei-Medium-ETen-B5-H", 0, 100. }, + { "B5-MHei-Italic-ETen-B5-H", 0, 100. }, + { "B5-MHei-Bold-ETen-B5-H", 0, 100. }, + { "B5-MHei-BoldItalic-ETen-B5-H", 0, 100. }, +}; +static const psfont FangSongB5 [] = { + { "B5-CFangSong-Light-ETen-B5-H", 0, 100. }, + { "B5-CFangSong-Italic-ETen-B5-H", 0, 100. }, + { "B5-CFangSong-Bold-ETen-B5-H", 0, 100. }, + { "B5-CFangSong-BoldItalic-ETen-B5-H", 0, 100. }, +}; + +// Arphic fonts on Thiz Linux +static const psfont LinGothic [] = { + { "LinGothic-Light-ETen-B5-H", 0, 100. }, + { "LinGothic-Light-Italic-ETen-B5-H", 0.2, 100. }, + { "LinGothic-Light-Bold-ETen-B5-H", 0, 100. }, + { "LinGothic-Light-BoldItalic-ETen-B5-H", 0.2, 100. }, +}; +static const psfont YenRound [] = { + { "YenRound-Light-ETen-B5-H", 0, 100. }, + { "YenRound-Light-Italic-ETen-B5-H", 0.2, 100. }, + { "YenRound-Light-Bold-ETen-B5-H", 0, 100. }, + { "YenRound-Light-BoldItalic-ETen-B5-H", 0.2, 100. }, +}; + +// Dr. Wang Hann-Tzong's GPL'ed Big5 TrueType fonts +#if 0 // ### this is never used? +static const psfont HtWFangSong [] = { + { "HtW-FSong-Light-ETen-B5-H", 0, 100. }, + { "HtW-FSong-Light-Italic-ETen-B5-H", 0.2, 100. }, + { "HtW-FSong-Light-Bold-ETen-B5-H", 0, 100. }, + { "HtW-FSong-Light-BoldItalic-ETen-B5-H", 0.2, 100. }, +}; +#endif + +static const psfont MingB5 [] = { + { "Ming-Light-ETen-B5-H", 0, 100. }, + { "Ming-Light-Italic-ETen-B5-H", 0.2, 100. }, + { "Ming-Light-Bold-ETen-B5-H", 0, 100. }, + { "Ming-Light-BoldItalic-ETen-B5-H", 0.2, 100. }, +}; + +// Microsoft's Ming/Sung font? +static const psfont MSung [] = { + { "MSung-Light-ETenms-B5-H", 0, 100. }, + { "MSung-Light-ETenms-B5-H", 0.2, 100. }, + { "MSung-Light-ETenms-B5-H", 0, 100. }, + { "MSung-Light-ETenms-B5-H", 0.2, 100. }, +}; +// "Standard Sung/Ming" font by Taiwan Ministry of Education +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 MOEKai [] = { + { "MOEKai-Regular-B5-H", 0, 100. }, + { "MOEKai-Regular-B5-H", 0.2, 100. }, + { "MOEKai-Regular-B5-H", 0, 100. }, + { "MOEKai-Regular-B5-H", 0.2, 100. }, +}; + +static const psfont * const TraditionalReplacements[] = { + MOESung, SongB5, ShanHeiSun, MingB5, MSung, FangSongB5, KaiB5, ZenKai, HeiB5, + LinGothic, YenRound, MOEKai, Helvetica, 0 + }; + +#if 0 // ### these are never used? +static const psfont * const SongB5Replacements[] = { + SongB5, ShanHeiSun, MingB5, MSung, MOESung, Helvetica, 0 + }; + +static const psfont * const FangSongB5Replacements[] = { + FangSongB5, HtWFangSong, Courier, 0 + }; +static const psfont * const KaiB5Replacements[] = { + KaiB5, ZenKai, Times, 0 + }; +static const psfont * const HeiB5Replacements[] = { + HeiB5, LinGothic, YenRound, LucidaSans, 0 + }; +static const psfont * const YuanB5Replacements[] = { + YenRound, LinGothic, HeiB5, LucidaSans, 0 + }; +#endif + + +class QPSPrinterFontTraditionalChinese + : public QPSPrinterFontAsian { +public: + QPSPrinterFontTraditionalChinese(const QFontEngine* f); + QString extension() const; +}; + +QPSPrinterFontTraditionalChinese::QPSPrinterFontTraditionalChinese(const QFontEngine* f) +{ + codec = QTextCodec::codecForMib( 2026 ); // Big5-0 + int type = getPsFontType( f ); + psname = makePSFontName( f, type ); + QString best = "[ /" + psname + " 1.0 0.0 ]"; + replacementList.append( best ); + appendReplacements( replacementList, TraditionalReplacements, type ); +} + +QString QPSPrinterFontTraditionalChinese::extension() const +{ + return "-ETen-B5-H"; +} + +// ----------- simplified chinese ------------ + +#if 0 +// GB18030 fonts on XteamLinux (?) +static const psfont SimplifiedGBK2K [] = { + { "MSung-Light-GBK2K-H", 0, 100. }, + { "MSung-Light-GBK2K-H", 0.2, 100. }, + { "MKai-Medium-GBK2K-H", 0, 100. }, + { "MKai-Medium-GBK2K-H", 0.2, 100. }, +}; +#endif + +// GB18030 fonts on Turbolinux +static const psfont SongGBK2K [] = { + { "MSung-Light-GBK2K-H", 0, 100. }, + { "MSung-Italic-GBK2K-H", 0, 100. }, + { "MSung-Bold-GBK2K-H", 0, 100. }, + { "MSung-BoldItalic-GBK2K-H", 0, 100. }, +}; +static const psfont KaiGBK2K [] = { + { "MKai-Medium-GBK2K-H", 0, 100. }, + { "MKai-Italic-GBK2K-H", 0, 100. }, + { "MKai-Bold-GBK2K-H", 0, 100. }, + { "MKai-BoldItalic-GBK2K-H", 0, 100. }, +}; +static const psfont HeiGBK2K [] = { + { "MHei-Medium-GBK2K-H", 0, 100. }, + { "MHei-Italic-GBK2K-H", 0, 100. }, + { "MHei-Bold-GBK2K-H", 0, 100. }, + { "MHei-BoldItalic-GBK2K-H", 0, 100. }, +}; +static const psfont FangSongGBK2K [] = { + { "CFangSong-Light-GBK2K-H", 0, 100. }, + { "CFangSong-Italic-GBK2K-H", 0, 100. }, + { "CFangSong-Bold-GBK2K-H", 0, 100. }, + { "CFangSong-BoldItalic-GBK2K-H", 0, 100. }, +}; + +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. }, +}; + +// Arphic Public License GB2312 TrueType fonts (on Debian and CLE and others) +static const psfont BousungEG [] = { + { "BousungEG-Light-GB-GB-EUC-H", 0, 100. }, + { "BousungEG-Light-GB-GB-EUC-H", 0.2, 100. }, + { "BousungEG-Light-GB-Bold-GB-EUC-H", 0, 100. }, + { "BousungEG-Light-GB-Bold-GB-EUC-H", 0.2, 100. }, +}; +static const psfont GBZenKai [] = { + { "GBZenKai-Medium-GB-GB-EUC-H", 0, 100. }, + { "GBZenKai-Medium-GB-GB-EUC-H", 0.2, 100. }, + { "GBZenKai-Medium-GB-Bold-GB-EUC-H", 0, 100. }, + { "GBZenKai-Medium-GB-Bold-GB-EUC-H", 0.2, 100. }, +}; + +static const psfont * const SimplifiedReplacements[] = { + SongGBK2K, FangSongGBK2K, KaiGBK2K, HeiGBK2K, + Simplified, MSungGBK, FangSong, BousungEG, GBZenKai, Helvetica, 0 + }; +#if 0 +static const psfont * const SongGBK2KReplacements[] = { + SongGBK2K, MSungGBK, BousungEG, Helvetica, 0 + }; +#endif +static const psfont * const FangSongGBK2KReplacements[] = { + FangSongGBK2K, FangSong, Courier, 0 + }; +static const psfont * const KaiGBK2KReplacements[] = { + KaiGBK2K, GBZenKai, Times, 0 + }; +static const psfont * const HeiGBK2KReplacements[] = { + HeiGBK2K, LucidaSans, 0 + }; + +class QPSPrinterFontSimplifiedChinese + : public QPSPrinterFontAsian { +public: + QPSPrinterFontSimplifiedChinese(const QFontEngine* f); + QString extension() const; +}; + +QPSPrinterFontSimplifiedChinese::QPSPrinterFontSimplifiedChinese(const QFontEngine* f) +{ + codec = QTextCodec::codecForMib( 114 ); // GB18030 + int type = getPsFontType( f ); + QString family = f->fontDef.family.lower(); + if( family.contains("kai",FALSE) ) { + psname = KaiGBK2K[type].psname; + appendReplacements( replacementList, KaiGBK2KReplacements, type ); + } else if( family.contains("fangsong",FALSE) ) { + psname = FangSongGBK2K[type].psname; + appendReplacements( replacementList, FangSongGBK2KReplacements, type ); + } else if( family.contains("hei",FALSE) ) { + psname = HeiGBK2K[type].psname; + appendReplacements( replacementList, HeiGBK2KReplacements, type ); + } else { + psname = SongGBK2K[type].psname; + appendReplacements( replacementList, SimplifiedReplacements, type ); + } + //qDebug("simplified chinese: fontname is %s, psname=%s", f.family().latin1(), psname.latin1() ); +} + +QString QPSPrinterFontSimplifiedChinese::extension() const +{ + return "-GBK2K-H"; +} + +#endif + + +// ================== QPSPrinterFont ==================== + +class QPSPrinterFont { +public: + QPSPrinterFont(const QFont& f, int script, QPSPrinterPrivate *priv); + ~QPSPrinterFont(); + QString postScriptFontName() { return p->postScriptFontName(); } + QString defineFont( QTextStream &stream, const 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; + + enum { NONE, PFB, PFA, TTF } type = NONE; + + QFontEngine *engine = f.d->engineForScript( (QFont::Script) script ); + // ### implement similar code for QWS and WIN + xfontname = makePSFontName( engine ); + +#if defined( Q_WS_X11 ) + bool xlfd = FALSE; + //qDebug("engine = %p name=%s, script=%d", engine, engine ? engine->name() : "(null)", script); + +#ifndef QT_NO_XFTFREETYPE + if ( qt_has_xft && engine && engine->type() == QFontEngine::Xft ) { + XftPattern *pattern = static_cast( engine )->pattern(); + char *filename = 0; + XftPatternGetString (pattern, XFT_FILE, 0, &filename); + //qDebug("filename for font is '%s'", filename); + if ( filename ) { + fontfilename = QString::fromLocal8Bit( filename ); + xfontname = fontfilename; + } + } else +#endif + { + QString rawName; + if ( engine && engine != (QFontEngine *)-1 ) + rawName = engine->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); + if ( xfontname.endsWith( "*" ) ) + xfontname.truncate( xfontname.length() - 1 ); + xlfd = TRUE; + } + } +#endif // Q_WS_X11 +#ifndef QT_NO_TEXTCODEC + // map some scripts to something more useful + if ( script == QFont::Han ) { + QTextCodec *lc = QTextCodec::codecForLocale(); + switch( lc->mibEnum() ) { + case 36: // KS C 5601 + case 38: // EUC KR + script = QFont::Hangul; + break; + + case 57: // gb2312.1980-0 + case 113: // GBK + case -113: // gbk-0 + case 114: // GB18030 + case -114: // gb18030-0 + case 2025: // GB2312 + case 2026: // Big5 + case -2026: // Big5-HKSCS + case 2101: // big5-0, big5.eten-0 + case -2101: // big5hkscs-0, hkscs-1 + break; + + case 16: // JIS7 + case 17: // SJIS + case 18: // EUC JP + case 63: // JIS X 0208 + default: + script = QFont::Hiragana; + break; + } + } else if ( script == QFont::Katakana ) + script = QFont::Hiragana; + else if ( script == QFont::Bopomofo ) + script = QFont::Han; +#endif + + QString searchname = xfontname; +#if defined(Q_WS_X11) + // we need an extension here due to the fact that we use different + // fonts for different scripts + if ( xlfd && script >= QFont::Han && script <= QFont::Bopomofo ) + xfontname += "/" + toString( script ); +#endif + + //qDebug("looking for font %s in dict", xfontname.latin1() ); + p = priv->fonts.find(xfontname); + if ( p ) + return; + +#if defined(Q_WS_X11) + if ( xlfd ) { + + 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(searchname.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, file=%s, p=%p", f.family().latin1(), xfontname.latin1(), fontfilename.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(); + } + } + + if (!data.isNull() && data.size() > 0) { + unsigned char* d = (unsigned char *)data.data(); + if (d[0] == 0x80 && d[1] == 0x01 && d[6] == '%' && d[7] == '!') + 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; + + //qDebug("font is of type %d", type ); + switch (type) { + case TTF : + p = new QPSPrinterFontTTF(engine, data); + break; + case PFB: + p = new QPSPrinterFontPFB(engine, data); + break; + case PFA: + p = new QPSPrinterFontPFA(engine, data); + break; + case NONE: + default: + +#ifndef QT_NO_TEXTCODEC + + if ( script == QFont::Hiragana ) + p = new QPSPrinterFontJapanese( engine ); + else if ( script == QFont::Hangul ) + p = new QPSPrinterFontKorean( engine ); + else if ( script == QFont::Han ) { + QTextCodec *lc = QTextCodec::codecForLocale(); + switch( lc->mibEnum() ) { + case 2025: // GB2312 + case 57: // gb2312.1980-0 + case 113: // GBK + case -113: // gbk-0 + case 114: // GB18030 + case -114: // gb18030-0 + p = new QPSPrinterFontSimplifiedChinese( engine ); + break; + case 2026: // Big5 + case -2026: // big5-0, big5.eten-0 + case 2101: // Big5-HKSCS + case -2101: // big5hkscs-0, hkscs-1 + p = new QPSPrinterFontTraditionalChinese( engine ); + break; + default: + p = new QPSPrinterFontJapanese( engine ); + } + } else +#endif + //qDebug("didnt find font for %s", xfontname.latin1()); + p = new QPSPrinterFontNotFound( engine ); + break; + } + + if (p->postScriptFontName() == "Symbol") + p->setSymbol(); + + // this is needed to make sure we don't get the same postscriptname twice + QDictIterator it( priv->fonts ); + for( it.toFirst(); it.current(); ++it ) { + if ( *(*it) == *p ) { +// qWarning("Post script driver: font already in dict"); + delete p; + p = *it; + return; + } + } + + //qDebug("inserting font %s in dict psname=%s", xfontname.latin1(), p->postScriptFontName().latin1() ); + 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 ), bkMode( Qt::TransparentMode ), dirtyBkMode( FALSE ), +#ifndef QT_NO_TEXTCODEC + currentFontCodec( 0 ), +#endif + fm( QFont() ), textY( 0 ) +{ + printer = prt; + headerFontNames.setAutoDelete( TRUE ); + pageFontNames.setAutoDelete( TRUE ); + fonts.setAutoDelete( TRUE ); + currentFontFile = 0; + scale = 1.; + scriptUsed = -1; + +#ifdef Q_WS_X11 + // append qsettings fontpath + QSettings settings; + embedFonts = settings.readBoolEntry( "/qt/embedFonts", TRUE ); + + int npaths; + char** font_path; + font_path = XGetFontPath( qt_xdisplay(), &npaths); + bool xfsconfig_read = FALSE; + for (int i=0; i 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(); + bool end = FALSE; + while( f.status()==IO_Ok && !end ) { + if ( fs[int(fs.length())-1] == ',' ) + fs = fs.left(fs.length()-1); + else + end = TRUE; + if (fs[0] != '#' && !fs.contains(":unscaled")) + fontpath += fs; + 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 + fontpath += font_path[i]; + } + } + XFreeFontPath(font_path); + + // append qsettings fontpath + QStringList fp = settings.readListEntry( "/qt/fontPath", ':' ); + if ( !fp.isEmpty() ) + fontpath += fp; +#else + embedFonts = FALSE; +#endif +} + +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 ); + } + + QPSPrinterFont ff( f, script, this ); + QString ps = ff.postScriptFontName(); + + QString s = ps; + s.append( ' ' ); + s.prepend( ' ' ); + + QString key = ff.xfontname; + + if ( f.pointSize() != -1 ) + key += " " + toString( f.pointSize() ); + else + key += " px" + toString( 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; + +#ifndef QT_NO_TEXTCODEC + 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 + currentFontCodec = codec; +#endif + currentFont = fontName; + 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+256; // magic extended quote +static const int quoteReach = 10; // ... 1-1024 pixels back +static const int tableSize = 1024; // 2 ** quoteReach; +static const int numAttempts = 128; + +static const int hashSize = 71; + +static const int None = INT_MAX; + +/* puts the lowest numBits of data into the out array starting at postion (byte/bit). + Adjusts byte and bit to point ot the next position. + + Need to make sure the out array is long enough before calling the method. +*/ +static void emitBits( char *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++; + } + } +} + +//#define DEBUG_COMPRESS +#ifdef DEBUG_COMPRESS +#include +#endif + +static QByteArray compress( const QImage & image, bool gray ) { +#ifdef DEBUG_COMPRESS + QTime t; + t.start(); + int sizeUncompressed[11]; + for( int i = 0; i < 11; i++ ) + sizeUncompressed[i] = 0; + int sizeCompressed[11]; + for( int i = 0; i < 11; i++ ) + sizeCompressed[i] = 0; +#endif + + int width = image.width(); + int height = image.height(); + int depth = image.depth(); + int size = width*height; + + int pastPixel[tableSize]; + int mostRecentPixel[hashSize]; + if ( depth == 1 ) + size = (width+7)/8*height; + else if ( !gray ) + size = size*3; + + unsigned char *pixel = new unsigned char[size+1]; + int i = 0; + if ( depth == 1 ) { + QImage::Endian bitOrder = image.bitOrder(); + memset( pixel, 0xff, size ); + for( int y=0; y < height; y++ ) { + uchar * s = image.scanLine( y ); + for( int x=0; x < width; x++ ) { + // need to copy bit for bit... + bool b = ( bitOrder == QImage::LittleEndian ) ? + (*(s + (x >> 3)) >> (x & 7)) & 1 : + (*(s + (x >> 3)) << (x & 7)) & 0x80 ; + if ( b ) + pixel[i >> 3] ^= (0x80 >> ( i & 7 )); + i++; + } + // we need to align to 8 bit here + i = (i+7) & 0xffffff8; + } + } else if ( depth == 8 ) { + for( int y=0; y < height; y++ ) { + uchar * s = image.scanLine( y ); + for( int x=0; x < width; x++ ) { + QRgb rgb = image.color( s[x] ); + if ( gray ) { + pixel[i] = (unsigned char) qGray( rgb ); + i++; + } else { + pixel[i] = (unsigned char) qRed( rgb ); + pixel[i+1] = (unsigned char) qGreen( rgb ); + pixel[i+2] = (unsigned char) qBlue( rgb ); + i += 3; + } + } + } + } else { + bool alpha = image.hasAlphaBuffer(); + for( int y=0; y < height; y++ ) { + QRgb * s = (QRgb*)(image.scanLine( y )); + for( int x=0; x < width; x++ ) { + QRgb rgb = (*s++); + if ( alpha && qAlpha( rgb ) < 0x40 ) // 25% alpha, convert to white - + rgb = qRgb( 0xff, 0xff, 0xff ); + if ( gray ) { + pixel[i] = (unsigned char) qGray( rgb ); + i++; + } else { + pixel[i] = (unsigned char) qRed( rgb ); + pixel[i+1] = (unsigned char) qGreen( rgb ); + pixel[i+2] = (unsigned char) qBlue( rgb ); + i += 3; + } + } + } + } + + 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. + + The format of the compressed stream is as follows: + // 2 bits step size for search and backreference ( 1 or 3 ) + 1 bit compressed or uncompressed block follows + + uncompressed block: + 3 bits size of block in bytes + size*8 bits data + + compressed block: + 3 bits compression header + 0-2 size of block is 1-3 bytes + 3-7 size of block is bigger, 4-8 additional bits specifying size follow + 0/4-8 additional size fields + 10 location of backreference + */ + + for( i=0; i < hashSize; i++ ) + mostRecentPixel[i] = None; + int index = 0; + int emittedUntil = 0; + char *out = (char *)malloc( 256 * sizeof( char ) ); + int outLen = 256; + 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++ > numAttempts ) { + 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 > numAttempts ) { + 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; + } + } + /* backreferences to 1 byte of data are actually more costly than + emitting the data directly, 2 bytes don't save much. */ + if ( bestCandidate != None && bestLength < 3 ) + bestCandidate = 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 ) { +#ifdef DEBUG_COMPRESS + int x = 0; + int bl = emittedUntil - index; + while ( (bl /= 2) ) + x++; + if ( x > 10 ) x = 10; + sizeUncompressed[x]++; +#endif + int l = QMIN( 8, index - emittedUntil ); + if ( outOffset + l + 2 >= outLen ) { + outLen *= 2; + out = (char *) realloc( out, outLen ); + } + emitBits( out, outOffset, outBit, + 1, 0 ); + emitBits( out, outOffset, outBit, + quoteSize, l-1 ); + while( l-- ) { + emitBits( out, outOffset, outBit, + 8, pixel[emittedUntil] ); + emittedUntil++; + } + } + } + /* if we have some quoted data to output, do it. */ + if ( bestCandidate != None ) { +#ifdef DEBUG_COMPRESS + int x = 0; + int bl = bestLength; + while ( (bl /= 2) ) + x++; + if ( x > 10 ) x = 10; + sizeCompressed[x]++; +#endif + if ( outOffset + 4 >= outLen ) { + outLen *= 2; + out = (char *) realloc( out, outLen ); + } + emitBits( out, outOffset, outBit, + 1, 1 ); + int l = bestLength - 3; + const struct off_len { + int off; + int bits; + } ol_table [] = { + /* Warning: if you change the table here, change /uc in the PS code! */ + { 3, 0/*dummy*/ }, + { 16, 4 }, + { 32, 5 }, + { 64, 6 }, + { 128, 7 }, + { /*256*/ 0xfffffff, 8 }, + }; + + if ( l < ol_table[0].off ) { + emitBits( out, outOffset, outBit, + quoteSize, l ); + } else { + const off_len *ol = ol_table; + l -= ol->off; + ol++; + while ( l >= ol->off ) { + l -= ol->off; + ol++; + } + emitBits( out, outOffset, outBit, + quoteSize, ol->bits-1 ); + emitBits( out, outOffset, outBit, + ol->bits, l ); + } + 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++; + i = 0; + /* we have to make sure the data 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++; + } + QByteArray outarr; + outarr.duplicate( out, outOffset ); + free( out ); + delete [] pixel; + +#ifdef DEBUG_COMPRESS + qDebug( "------------- image compression statistics ----------------" ); + qDebug(" compression time %d", t.elapsed() ); + qDebug( "Size dist of uncompressed blocks:" ); + qDebug( "\t%d\t%d\t%d\t%d\t%d\t%d\n", sizeUncompressed[0], sizeUncompressed[1], + sizeUncompressed[2], sizeUncompressed[3], sizeUncompressed[4], sizeUncompressed[5]); + qDebug( "\t%d\t%d\t%d\t%d\t%d\n", sizeUncompressed[6], sizeUncompressed[7], + sizeUncompressed[8], sizeUncompressed[9], sizeUncompressed[10] ); + qDebug( "Size dist of compressed blocks:" ); + qDebug( "\t%d\t%d\t%d\t%d\t%d\t%d\n", sizeCompressed[0], sizeCompressed[1], + sizeCompressed[2], sizeCompressed[3], sizeCompressed[4], sizeCompressed[5]); + qDebug( "\t%d\t%d\t%d\t%d\t%d\n", sizeCompressed[6], sizeCompressed[7], + sizeCompressed[8], sizeCompressed[9], sizeCompressed[10] ); + qDebug( "===> total compression ratio %d/%d = %f", outOffset, size, (float)outOffset/(float)size ); + qDebug( "-----------------------------------------------------------" ); +#endif + + return outarr; +} + +#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, const QImage &mask ) +{ + if ( !w || !h || img.isNull() ) return; + + int width = img.width(); + int height = img.height(); + float scaleX = (float)width/w; + float scaleY = (float)height/h; + + bool gray = (printer->colorMode() == QPrinter::GrayScale) || + img.allGray(); + int splitSize = 21830 * (gray ? 3 : 1 ); + if ( width * height > splitSize ) { // 65535/3, tolerance for broken printers + int images, subheight; + images = ( width * height + splitSize - 1 ) / splitSize; + subheight = ( height + images-1 ) / images; + while ( subheight * width > splitSize ) { + 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 ) ), + mask.isNull() ? mask : mask.copy( 0, suby, width, QMIN( subheight, height-suby ) )); + suby += subheight; + } + } else { + QByteArray out; + int size = 0; + const char *bits; + + if ( !mask.isNull() ) { + out = ::compress( mask, TRUE ); + size = (width+7)/8*height; + pageStream << "/mask " << size << " string uc\n"; + ps_r7( pageStream, out, out.size() ); + pageStream << "d\n"; + } + if ( img.depth() == 1 ) { + size = (width+7)/8*height; + bits = "1 "; + } else if ( gray ) { + size = width*height; + bits = "8 "; + } else { + size = width*height*3; + bits = "24 "; + } + + out = ::compress( img, gray ); + pageStream << "/sl " << size << " string uc\n"; + ps_r7( pageStream, out, out.size() ); + pageStream << "d\n" + << width << ' ' << height << "[" << scaleX << " 0 0 " << scaleY << " 0 0]sl " + << bits << (!mask.isNull() ? "mask " : "false ") + << x << ' ' << y << " di\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()); + uint mtop, mleft, mbottom, mright; + printer->margins( &mtop, &mleft, &mbottom, &mright ); + int width = m.width(); + int height = m.height(); + bool fullPage = printer->fullPage(); + if ( finished && pageCount == 1 && printer->numCopies() == 1 && + ( ( printer->fullPage() && qt_gen_epsf ) || + ( printer->outputToFile() && printer->outputFileName().endsWith( ".eps" ) ) ) + ) { + if ( !boundingBox.isValid() ) + boundingBox.setRect( 0, 0, width, height ); + if ( printer->orientation() == QPrinter::Landscape ) { + if ( !fullPage ) + boundingBox.moveBy( -mleft, -mtop ); + outStream << " EPSF-3.0\n%%BoundingBox: " + << (int)(m.height() - boundingBox.bottom())*scale << " " // llx + << (int)(m.width() - boundingBox.right())*scale - 1 << " " // lly + << (int)(m.height() - boundingBox.top())*scale + 1 << " " // urx + << (int)(m.width() - boundingBox.left())*scale; // ury + } else { + if ( !fullPage ) + boundingBox.moveBy( mleft, -mtop ); + outStream << " EPSF-3.0\n%%BoundingBox: " + << (int)(boundingBox.left())*scale << " " + << (int)(m.height() - boundingBox.bottom())*scale - 1 << " " + << (int)(boundingBox.right())*scale + 1 << " " + << (int)(m.height() - boundingBox.top())*scale; + } + } else { + int w = width + (fullPage ? 0 : mleft + mright); + int h = height + (fullPage ? 0 : mtop + mbottom); + w = (int)(w*scale); + h = (int)(h*scale); + // 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"; + + outStream << "%%BeginProlog\n"; + const char * const prologLicense = "% Prolog copyright 1994-2006 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 << 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" ), toString( 10./scale ) ); + lineStyles.replace( QRegExp( "m" ), toString( 5./scale ) ); + lineStyles.replace( QRegExp( "s" ), toString( 3./scale ) ); + + outStream << lineStyles; + + outStream << "/pageinit {\n"; + if ( !printer->fullPage() ) { + if ( printer->orientation() == QPrinter::Portrait ) + outStream << mleft*scale << " " + << mbottom*scale << " translate\n"; + else + outStream << mtop*scale << " " + << mleft*scale << " translate\n"; + } + if ( printer->orientation() == QPrinter::Portrait ) { + outStream << "% " << m.widthMM() << "*" << m.heightMM() + << "mm (portrait)\n0 " << height*scale + << " 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 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; +} + + +/* 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 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 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 ) ) ) +#ifdef Q_WS_QWS + (last || buffer->size() > 2000000) // embedded is usually limited in memory +#else + (last || buffer->size() > 50000000) +#endif + ) { +// qDebug("emiting header at page %d", pageCount ); + emitHeader( last ); + } + outStream << "%%Page: " + << pageCount << ' ' << pageCount << endl + << "%%BeginPageSetup\n" + << "QI\n"; + if (!dirtyNewPage) { + if ( pageFonts ) { + //qDebug("page fonts for page %d", pageCount); + // we have already downloaded the header. Maybe we have page fonts here + QDictIterator 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; +} + + + +static void ignoreSigPipe(bool b) +{ + static struct sigaction *users_sigpipe_handler = 0; + + if (b) { + if (users_sigpipe_handler != 0) + return; // already ignoring sigpipe + + users_sigpipe_handler = new struct sigaction; + struct sigaction tmp_sigpipe_handler; + tmp_sigpipe_handler.sa_handler = SIG_IGN; + sigemptyset(&tmp_sigpipe_handler.sa_mask); + tmp_sigpipe_handler.sa_flags = 0; + + if (sigaction(SIGPIPE, &tmp_sigpipe_handler, users_sigpipe_handler) == -1) { + delete users_sigpipe_handler; + users_sigpipe_handler = 0; + } + } + else { + if (users_sigpipe_handler == 0) + return; // not ignoring sigpipe + + if (sigaction(SIGPIPE, users_sigpipe_handler, 0) == -1) + qWarning("QPSPrinter: could not restore SIGPIPE handler"); + + delete users_sigpipe_handler; + users_sigpipe_handler = 0; + } +} + +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); + + // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE + // if lp/lpr dies + ignoreSigPipe(TRUE); + d->flushPage( TRUE ); + d->outStream << "%%Trailer\n"; + if ( pageCountAtEnd ) + d->outStream << "%%Pages: " << d->pageCount - 1 << "\n" << + wrapDSC( "%%DocumentFonts: " + d->fontsUsed ); + d->outStream << "%%EOF\n"; + ignoreSigPipe(FALSE); + + 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: + // we use drawTextItem instead + return TRUE; + case PdcDrawText2Formatted: + return TRUE; + case PdcDrawTextItem: { + const QTextItem *ti = p[1].textItem; + QScriptItem &si = ti->engine->items[ti->item]; + int len = ti->engine->length( ti->item ); + if ( si.isSpace || si.isObject ) + return FALSE; + + if ( d->currentSet != d->currentUsed || d->scriptUsed != si.analysis.script || !d->currentFontFile ) { + d->currentUsed = d->currentSet; + d->setFont( d->currentSet, si.analysis.script ); + } + if( d->currentFontFile ) // better not crash in case somethig goes wrong. + d->currentFontFile->drawText( d->pageStream, *p[0].point, ti->engine, ti->item, + ti->engine->string.mid( si.position, len ), d, paint); + return FALSE; + } + case PdcDrawPixmap: { + if ( p[1].pixmap->isNull() ) + break; + QRect r = *p[0].rect; + QImage img; + img = *(p[1].pixmap); + QImage mask; + if ( p[1].pixmap->mask() ) + mask = *(p[1].pixmap->mask()); + d->drawImage(paint, r.x(), r.y(), r.width(), r.height(), img, mask); + break; + } + case PdcDrawImage: { + if ( p[1].image->isNull() ) + break; + QRect r = *(p[0].rect); + QImage img = *(p[1].image); + QImage mask; +#ifndef QT_NO_IMAGE_DITHER_TO_1 + if ( img.hasAlphaBuffer() ) + mask = img.createAlphaMask(); +#endif + d->drawImage(paint, r.x(), r.y(), r.width(), r.height(), img, mask); + 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: + // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE + // if lp/lpr dies + ignoreSigPipe(TRUE); + d->flushPage(); + ignoreSigPipe(FALSE); + + d->dirtyNewPage = TRUE; + break; + case AbortPrinting: + break; + default: + break; + } + return TRUE; +} + +#endif // QT_NO_PRINTER -- cgit v1.2.1