Author: jghali
Date: Wed Feb 10 21:54:56 2021
New Revision: 24464

URL: http://scribus.net/websvn/listing.php?repname=Scribus&sc=1&rev=24464
Log:
PDF: subset CID-keyed OpenType fonts as OpenType with the help of Harfbuzz when 
possible

Modified:
    trunk/Scribus/scribus/fonts/cff.cpp
    trunk/Scribus/scribus/fonts/cff.h
    trunk/Scribus/scribus/fonts/sfnt.cpp
    trunk/Scribus/scribus/fonts/sfnt.h
    trunk/Scribus/scribus/pdflib_core.cpp
    trunk/Scribus/scribus/pdflib_core.h

Modified: trunk/Scribus/scribus/fonts/cff.cpp
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/fonts/cff.cpp
==============================================================================
--- trunk/Scribus/scribus/fonts/cff.cpp (original)
+++ trunk/Scribus/scribus/fonts/cff.cpp Wed Feb 10 21:54:56 2021
@@ -1,1745 +1,1748 @@
-//
-//  cff.cpp
-//  Scribus
-//
-//  Created by Andreas Vox on 04.05.15.
-//
-//
-
-#include "cff.h"
-
-#include <cassert>
-#include <cmath>
-#include <QDebug>
-
-namespace cff {
-       
-       static const char* cffDictKeys[] = {
-               "version",
-               "Notice",
-               "FullName",
-               "FamilyName",
-               "Weight",
-               "FontBBox",
-               "BlueValues",
-               "OtherBlues",
-               "FamilyBlues",
-               "FamilyOtherBlues",
-               "StdHW",
-               "StdVW",
-               "",
-               "UniqueID",
-               "XUID",
-               "charset",
-               "Encoding",
-               "CharStrings",
-               "Private",
-               "Subrs",
-               "defaultWidthX",
-               "nominalWidthX"
-       };
-       
-       static const char* cffDictKeys0c[] = {
-               "Copyright",
-               "IsFixedPitch",
-               "ItalicAngle",
-               "UnderlinePosition",
-               "UnderlineThickness",
-               "PaintType",
-               "CharstringType",
-               "FontMatrix",
-               "StrokeWidth",
-               "BlueScale",
-               "BlueShift",
-               "BlueFuzz",
-               "StemSnapH",
-               "StemSnapV",
-               "ForceBold",
-               "-Reserved-",
-               "-Reserved-",
-               "LanguageGroup",
-               "ExpansionFactor",
-               "InitialRandomSeed",
-               "SyntheticBase",
-               "PostScript",
-               "BaseFontName",
-               "BaseFontBlend",
-               "-Reserved-",
-               "-Reserved-",
-               "-Reserved-",
-               "-Reserved-",
-               "-Reserved-",
-               "-Reserved-",
-               "ROS",
-               "CIDFontVersion",
-               "CIDFontRevision",
-               "CIDFontType",
-               "CIDCount",
-               "UIDBase",
-               "FDArray",
-               "FDSelect",
-               "FontName"
-       };
-       
-       static const char* cff_operator(cff::operator_type id)
-       {
-               if ((id & 0x0c00) == 0x0c00)
-               {
-                       int idx = id & 0xff;
-                       if (idx >= 0 && idx <= 38)
-                               return cffDictKeys0c[idx];
-                       return "";
-               }
-               if ( /* id >= 0 && */  id <= 21)
-                       return cffDictKeys[id];
-               return "";
-       }
-       
-       static const char* stdStrings[] = {
-               /* 0 */
-               ".notdef",
-               "space",
-               "exclam",
-               "quotedbl",
-               "numbersign",
-               "dollar",
-               "percent",
-               "ampersand",
-               "quoteright",
-               "parenleft",
-               /* 10 */
-               "parenright",
-               "asterisk",
-               "plus",
-               "comma",
-               "hyphen",
-               "period",
-               "slash",
-               "zero",
-               "one",
-               "two",
-               "three",
-               "four",
-               "five",
-               "six",
-               "seven",
-               "eight",
-               "nine",
-               "colon",
-               "semicolon",
-               "less",
-               /* 30 */
-               "equal",
-               "greater",
-               "question",
-               "at",
-               "A",
-               "B",
-               "C",
-               "D",
-               "E",
-               "F",
-               "G",
-               "H",
-               "I",
-               "J",
-               "K",
-               "L",
-               "M",
-               "N",
-               "O",
-               "P",
-               /* 50 */
-               "Q",
-               "R",
-               "S",
-               "T",
-               "U",
-               "V",
-               "W",
-               "X",
-               "Y",
-               "Z",
-               "bracketleft",
-               "backslash",
-               "bracketrigh",
-               "asciicircum",
-               "underscore",
-               "quoteleft",
-               "a",
-               "b",
-               "c",
-               "d",
-               /* 70 */
-               "e",
-               "f",
-               "g",
-               "h",
-               "i",
-               "j",
-               "k",
-               "l",
-               "m",
-               "n",
-               "o",
-               "p",
-               "q",
-               "r",
-               "s",
-               "t",
-               "u",
-               "v",
-               "w",
-               "x",
-               /* 90 */
-               "y",
-               "z",
-               "braceleft",
-               "bar",
-               "braceright",
-               "asciitilde",
-               "exclamdown",
-               "cent",
-               "sterling",
-               "fraction",
-               /* 100  */
-               "yen",
-               "florin",
-               "section",
-               "currency",
-               "quotesingle",
-               "quotedblleft",
-               "guillemotleft",
-               "guilsinglleft",
-               "guilsinglright",
-               "fi",
-               "fl",
-               "endash",
-               "dagger",
-               "daggerdbl",
-               "periodcentered",
-               "paragraph",
-               "bullet",
-               "quotesinglbase",
-               "quotedblbase",
-               "quotedblright",
-               /* 120 */
-               "guillemotright",
-               "ellipsis",
-               "perthousand",
-               "questiondown",
-               "grave",
-               "acute",
-               "circumflex",
-               "tilde",
-               "macron",
-               "breve",
-               "dotaccent",
-               "dieresis",
-               "ring",
-               "cedilla",
-               "hungarumlaut",
-               "ogonek",
-               "caron",
-               "emdash",
-               "AE",
-               "ordfeminine",
-               /* 140 */
-               "Lslash",
-               "Oslash",
-               "OE",
-               "ordmasculine",
-               "ae",
-               "dotlessi",
-               "lslash",
-               "oslash",
-               "oe",
-               "germandbls",
-               "onesuperior",
-               "logicalnot",
-               "mu",
-               "trademark",
-               "Eth",
-               "onehalf",
-               "plusminus",
-               "Thorn",
-               "onequarter",
-               "divide",
-               /* 160 */
-               "brokenbar",
-               "degree",
-               "thorn",
-               "threequarters",
-               "twosuperior",
-               "registered",
-               "minus",
-               "eth",
-               "multiply",
-               "threesuperior",
-               "copyright",
-               "Aacute",
-               "Acircumflex",
-               "Adieresis",
-               "Agrave",
-               "Aring",
-               "Atilde",
-               "Ccedilla",
-               "Eacute",
-               "Ecircumflex",
-               /* 180 */
-               "Edieresis",
-               "Egrave",
-               "Iacute",
-               "Icircumflex",
-               "Idieresis",
-               "Igrave",
-               "Ntilde",
-               "Oacute",
-               "Ocircumflex",
-               "Odieresis",
-               "Ograve",
-               "Otilde",
-               "Scaron",
-               "Uacute",
-               "Ucircumflex",
-               "Udieresis",
-               "Ugrave",
-               "Yacute",
-               "Ydieresis",
-               "Zcaron",
-               /* 200 */
-               "aacute",
-               "acircumflex",
-               "adieresis",
-               "agrave",
-               "aring",
-               "atilde",
-               "ccedilla",
-               "eacute",
-               "ecircumflex",
-               "edieresis",
-               "egrave",
-               "iacute",
-               "icircumflex",
-               "idieresis",
-               "igrave",
-               "ntilde",
-               "oacute",
-               "ocircumflex",
-               "odieresis",
-               "ograve",
-               /* 220 */
-               "otilde",
-               "scaron",
-               "uacute",
-               "ucircumflex",
-               "udieresis",
-               "ugrave",
-               "yacute",
-               "ydieresis",
-               "zcaron",
-               "exclamsmall",
-               "Hungarumlautsmall",
-               "dollaroldstyle",
-               "dollarsuperior",
-               "ampersandsmall",
-               "Acutesmall",
-               "parenleftsuperior",
-               "parenrightsuperior",
-               "twodotenleader",
-               "onedotenleader",
-               "zerooldstyle",
-               /* 240 */
-               "oneoldstyle",
-               "twooldstyle",
-               "threeoldstyle",
-               "fouroldstyle",
-               "fiveoldstyle",
-               "sixoldstyle",
-               "sevenoldstyle",
-               "2eightoldstyle",
-               "nineoldstyle",
-               "commasuperior",
-               "threequartersemdash",
-               "periodsuperior",
-               "questionsmall",
-               "asuperior",
-               "bsuperior",
-               "centsuperior",
-               "dsuperior",
-               "esuperior",
-               "isuperior",
-               "lsuperior",
-               /* 260 */
-               "msuperior",
-               "nsuperior",
-               "osuperior",
-               "rsuperior",
-               "ssuperior",
-               "tsuperior",
-               "ff",
-               "ffi",
-               "ffl",
-               "parenleftinferior",
-               "parenrightinferior",
-               "Circumflexsmall",
-               "hyphensuperior",
-               "Gravesmall",
-               "Asmall",
-               "Bsmall",
-               "Csmall",
-               "Dsmall",
-               "Esmall",
-               "Fsmall",
-               /* 280 */
-               "Gsmall",
-               "Hsmall",
-               "Ismall",
-               "Jsmall",
-               "Ksmall",
-               "Lsmall",
-               "Msmall",
-               "Nsmall",
-               "Osmall",
-               "Psmall",
-               "Qsmall",
-               "Rsmall",
-               "Ssmall",
-               "Tsmall",
-               "Usmall",
-               "Vsmall",
-               "Wsmall",
-               "Xsmall",
-               "Ysmall",
-               "Zsmall",
-               /* 300 */
-               "colonmonetary",
-               "onefitted",
-               "rupiah",
-               "Tildesmall",
-               "exclamdownsmall",
-               "centoldstyle",
-               "Lslashsmall",
-               "Scaronsmall",
-               "Zcaronsmall",
-               "Dieresissmall",
-               "Brevesmall",
-               "Caronsmall",
-               "Dotaccentsmall",
-               "Macronsmall",
-               "iguredash",
-               "hypheninferior",
-               "goneksmall",
-               "Ringsmall",
-               "Cedillasmall",
-               "questiondownsmall",
-               /* 320 */
-               "oneeighth",
-               "hreeeighths",
-               "fiveeighths",
-               "seveneighths",
-               "onethird",
-               "twothirds",
-               "zerosuperior",
-               "foursuperior",
-               "fivesuperior",
-               "sixsuperior",
-               "sevensuperior",
-               "eightsuperior",
-               "ninesuperior",
-               "zeroinferior",
-               "oneinferior",
-               "twoinferior",
-               "threeinferior",
-               "fourinferior",
-               "fiveinferior",
-               "sixinferior",
-               /* 340 */
-               "seveninferior",
-               "eightinferior",
-               "nineinferior",
-               "centinferior",
-               "dollarinferior",
-               "periodinferior",
-               "commainferior",
-               "Agravesmall",
-               "Aacutesmall",
-               "Acircumflexsmall",
-               "Atildesmall",
-               "Adieresissmall",
-               "Aringsmall",
-               "AEsmall",
-               "Ccedillasmall",
-               "Egravesmall",
-               "Eacutesmall",
-               "Ecircumflexsmall",
-               "Edieresissmall",
-               "Igravesmall",
-               /* 360 */
-               "Iacutesmall",
-               "Icircumflexsmall",
-               "Idieresissmall",
-               "Ethsmall",
-               "Ntildesmall",
-               "Ogravesmall",
-               "Oacutesmall",
-               "Ocircumflexsmall",
-               "Otildesmall",
-               "Odieresissmall",
-               "OEsmall",
-               "Oslashsmall",
-               "Ugravesmall",
-               "Uacutesmall",
-               "Ucircumflexsmall",
-               "Udieresissmall",
-               "Yacutesmall",
-               "Thornsmall",
-               "Ydieresissmall",
-               "001.000",
-               /* 380 */
-               "001.001",
-               "001.002",
-               "001.003",
-               "Black",
-               "Bold",
-               "Book",
-               "Light",
-               "Medium",
-               "Regular",
-               "Roman",
-               /* 390 */
-               "Semibold"
-       };
-       
-       bool CFF_Number::isCardinal() const
-       {
-               switch (type)
-               {
-                       case cff_varnt_Error:
-                       case cff_varnt_Real:
-                               return false;
-                       default:
-                               return true;
-               }
-       }
-       
-       double CFF_Number::toDouble() const
-       {
-               switch (type)
-               {
-                       case cff_varnt_Real:
-                               return card * std::pow(10.0, exponent);
-                       default:
-                               return toCardinal();
-               }
-
-       }
-       
-       int CFF_Number::toCardinal() const
-       {
-               return card;
-       }
-
-       
-       CFF::CFF() : m_bytes(), m_offsetSize(4)
-       {
-               for (int i = 0; i <= sid_last_std; ++i)
-               {
-                       m_strings.append(stdStrings[i]);
-                       m_sids[stdStrings[i]] = i;
-               }
-       }
-       
-       
-       CFF::CFF(const QByteArray& cff) : m_bytes(cff)
-       {
-               // read header
-               m_offsetSize = cff[cff_offSize];
-               uint pos = cff[cff_hdrSize];
-               qDebug() << "cff header" << m_offsetSize << "starts" << pos;
-               // read names
-               m_names = readIndex(pos);
-               // read top dicts
-               QList<QByteArray> topDicts = readIndex(pos);
-               for (int i = 0; i < m_names.length(); ++i)
-               {
-                       QByteArray fontName = m_names[i];
-                       qDebug() << i << fontName;
-                       if (fontName.length() > 0 && fontName[0] != char(0))
-                       {
-                               m_fontTopDicts[fontName] = getDict(topDicts[i]);
-                               uint privLength = 
m_fontTopDicts[fontName][cff_dict_Private].array[0].toCardinal();
-                               uint privOffset = 
m_fontTopDicts[fontName][cff_dict_Private].array[1].toCardinal();
-                               getDict(readSegment(privOffset, privLength));
-                       }
-               }
-               // read strings
-               for (int i = 0; i <= sid_last_std; ++i)
-               {
-                       m_strings.append(stdStrings[i]);
-               }
-               m_strings.append(readIndex(pos));
-               for (int i = 0; i < m_strings.length(); ++i)
-               {
-//                     if ( i > sid_last_std)
-//                             qDebug() << i << strings[i];
-                       m_sids[m_strings[i]] = i;
-               }
-               // read global subroutines
-               m_globalSubr = readIndex(pos);
-       }
-       
-       
-       QByteArray CFF::readSegment(uint pos, uint size) const
-       {
-               return QByteArray::fromRawData(m_bytes.data() + pos, size);
-       }
-       
-       
-       uint CFF::readCard(uint pos) const
-       {
-               return static_cast<uchar>(m_bytes[pos]) << 8 | 
static_cast<uchar>(m_bytes[pos+1]);
-       }
-       
-       
-       
-       QMap<operator_type,CFF_Variant> CFF::getDict(const QByteArray& dict) 
const
-       {
-               QMap<uint,CFF_Variant> result;
-               QList<CFF_Number> stack;
-               
-               uint pos = 0;
-               while (pos < (uint) dict.length())
-               {
-                       CFF_Number num = parseDictElement(dict, pos);
-                       if (num.type == cff_varnt_Operator)
-                       {
-                               if (stack.length() == 1)
-                               {
-                                       result[num.card] = 
CFF_Variant(stack[0]);
-                               }
-                               else if (stack.length() > 0)
-                               {
-                                       result[num.card] = CFF_Variant(stack);
-                               }
-                               else
-                               {
-                                       /* error */
-                               }
-                               stack = QList<CFF_Number>();
-                       }
-                       else
-                       {
-                               stack.append(num);
-                       }
-               }
-               if (stack.length() > 0)
-               {
-                       /* error */
-               }
-               // TODO: adapt variant type according to operator
-               return result;
-       }
-       
-       
-       CFF_Number CFF::parseDictElement(const QByteArray& dict, uint& pos) 
const
-       {
-               uint code = dict[pos];
-               switch (code)
-               {
-                       case cff_dict_TwoBytes:
-                               code = (code << 8) | dict[++pos];
-                               break;
-                       case cff_dict_Card16:
-                       case cff_dict_Card32:
-                               return parseCard(dict, pos);
-                       case cff_dict_Real:
-                               return parseReal(dict, pos);
-
-                       default:
-                               if (code >= cff_dict_minOperand)
-                               {
-                                       return parseCard(dict, pos);
-                               }
-                               break;
-               }
-               ++pos;
-               CFF_Number num;
-               num.type = cff_varnt_Operator;
-               num.card = code;
-               qDebug() << "parsed operator" << cff_operator(code) << "(" << 
code << ")";
-               return num;
-       }
-       
-       
-       CFF_Number CFF::parseCard(const QByteArray& dict, uint& pos) const
-       {
-               CFF_Number result;
-               result.type = cff_varnt_Card;
-               result.exponent = 0;
-               uint start=pos;
-               uchar b0 = dict[pos++];
-               uchar b1,b2,b3,b4;
-               if (b0 == cff_dict_Card16)
-               {
-                       b1 = dict[pos++];
-                       b2 = dict[pos++];
-                       result.card = static_cast<qint16>((b1 << 8) | b2);
-               }
-               else if (b0 == cff_dict_Card32)
-               {
-                       b1 = dict[pos++];
-                       b2 = dict[pos++];
-                       b3 = dict[pos++];
-                       b4 = dict[pos++];
-                       result.card = static_cast<qint32>((b1 << 24) | (b2 << 
16) | (b3 << 8) | b4);
-               }
-               else if (b0 < cff_dict_minOperand)
-               {
-                       /* error */
-               }
-               else if (b0 <= cff_dict_maxSmallCard)
-               {
-                       result.card = b0 + cff_dict_biasSmallCard;
-               }
-               else if (b0 <= cff_dict_maxPosCard)
-               {
-                       b1 = dict[pos++];
-                       result.card = (b0 - cff_dict_minPosCard) * 256 + b1 + 
cff_dict_biasPosCard;
-               }
-               else if (b0 <= cff_dict_maxNegCard)
-               {
-                       b1 = dict[pos++];
-                       result.card = (cff_dict_minNegCard - b0) * 256 - b1 + 
cff_dict_biasNegCard;
-               }
-               else
-               {
-                       /* error */
-               }
-               qDebug() << "parsed" << 
QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to card " << 
result.card;
-               return result;
-       }
-       
-       
-       CFF_Number CFF::parseReal(const QByteArray& dict, uint& pos) const
-       {
-               assert( dict[pos] == cff_dict_Real );
-               uint start = pos++;
-
-               CFF_Number result;
-               result.type = cff_varnt_Real;
-               result.card = 0;
-               result.exponent = 0;
-               bool upperNibble = false;
-               bool haveSeenMinus = false;
-               bool haveSeenE = false;;
-               bool haveSeenEminus = false;
-               int decimalPointAt = -1;
-               uchar nibble, twoNibbles;
-               do
-               {
-                       // get nibble
-                       if (upperNibble)
-                       {
-                               nibble = twoNibbles & 0x0f;
-                               upperNibble = false;
-                       }
-                       else
-                       {
-                               twoNibbles = dict[pos++];
-                               nibble = twoNibbles >> 4;
-                               upperNibble = true;
-                       }
-                       
-                       // decode
-                       switch (nibble)
-                       {
-                               case cff_nibble_Minus:
-                                       haveSeenMinus = !haveSeenMinus;
-                                       break;
-                               case cff_nibble_Point:
-                                       decimalPointAt = 0;
-                                       break;
-                               case cff_nibble_PosExp:
-                                       haveSeenE = true;
-                                       break;
-                               case cff_nibble_NegExp:
-                                       haveSeenEminus = true;
-                                       break;
-                               case cff_nibble_End:
-                                       break;
-                               default:
-                                       if (nibble > cff_nibble_maxDigit)
-                                       {
-                                               /* error */
-                                       }
-                                       else if (haveSeenE || haveSeenEminus)
-                                       {
-                                               result.exponent *= 10;
-                                               result.exponent += nibble;
-                                       }
-                                       else
-                                       {
-                                               result.card *= 10;
-                                               result.card += nibble;
-                                               if (decimalPointAt >= 0)
-                                               {
-                                                       ++decimalPointAt;
-                                               }
-                                       }
-                                       break;
-                       }
-               } while (nibble != cff_nibble_End);
-                       
-               // finish
-               if (haveSeenMinus)
-                       result.card = -result.card;
-               if (haveSeenEminus)
-                       result.exponent = -result.exponent;
-               if (decimalPointAt > 0)
-                       result.exponent -= decimalPointAt;
-               qDebug() << "parsed" << 
QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to real " << 
result.card << "E" << result.exponent;
-
-               return result;
-       }
-       
-       
-       QList<QByteArray> CFF::readIndex(uint& pos) const
-       {
-               qDebug() << "read INDEX @" << pos;
-               QList<QByteArray> result;
-               uint N = readCard(pos);
-               pos += 2;
-               if (N == 0)
-                       return result;
-
-               uint offSize = m_bytes[pos++];
-               uint dataStart = pos + offSize * (N+1) - 1;
-               qDebug() << "size" << N << "offsetsize" << offSize << 
"dataStart" << dataStart;
-               uint start = 0;
-               uint end = 0;
-               for (uint c = 0; c < offSize; ++c)
-               {
-                       start = start << 8 | (uchar) m_bytes[pos++];
-               }
-               start += dataStart;
-               result.reserve(N);
-               for (uint i = 0; i < N; ++i)
-               {
-                       end = 0;
-                       for (uint c = 0; c < offSize; ++c)
-                       {
-                               end = end << 8 | (uchar) m_bytes[pos++];
-                       }
-                       end += dataStart;
-                       result.append(readSegment(start, end-start));
-                       start = end;
-               }
-               pos = end;
-               qDebug() << "INDEX ends @" << end;
-               return result;
-       }
-       
-       
-       QList<uint> CFF::readEncoding(uint& pos) const
-       {
-               QList<uint> result;
-               result.reserve(256);
-               for(int i = 0; i < 256; ++i)
-                       result.append(0);
-               
-               uchar format = m_bytes[pos++];
-               uchar N = m_bytes[pos++];
-               int gid;
-               uchar code;
-               switch (format)
-               {
-                       case 0x0:
-                       case 0x80:
-                               for (gid = 1; gid <= N; ++gid)
-                               {
-                                       code = m_bytes[pos++];
-                                       if (result[code] == 0)
-                                       {
-                                               result[code] = gid;
-                                       }
-                               }
-                               break;
-                       case 0x1:
-                       case 0x81:
-                               gid = 1;
-                               for (int i = 0; i < N; ++i)
-                               {
-                                       uchar first = m_bytes[pos++];
-                                       uchar nLeft = m_bytes[pos++];
-                                       int lastCode = (int) first + (int) 
nLeft;
-                                       for (int j = first; j <= lastCode; ++j)
-                                       {
-                                               if (result[j] == 0)
-                                               {
-                                                       result[j] = gid;
-                                               }
-                                               ++gid;
-                                       }
-                               }
-                               break;
-               }
-               if (format >= 0x80)
-               {
-                       uchar nSupplements = m_bytes[pos++];
-                       for (int i = 0; i < nSupplements; ++i)
-                       {
-                               code = m_bytes[pos++];
-                               gid = readCard(pos);
-                               pos += 2;
-                               result[code] = gid;
-                       }
-               }
-               return result;
-       }
-       
-       
-       QList<sid_type> CFF::readCharset(uint nGlyphs, uint& pos) const
-       {
-               QList<sid_type> result;
-               result.append(0); // sid for .notdef
-               
-               uchar format = m_bytes[pos++];
-               
-               sid_type first;
-               uchar nLeft1;
-               uint nLeft2;
-               
-               switch (format)
-               {
-                       case 0:
-                               for (uint gid = 1; gid < nGlyphs; ++gid)
-                               {
-                                       result.append(readCard(pos));
-                                       pos += 2;
-                               }
-                               break;
-                       case 1:
-                               while ((uint) result.length() < nGlyphs)
-                               {
-                                       first = readCard(pos);
-                                       pos += 2;
-                                       nLeft1 = m_bytes[pos++];
-                                       for (sid_type sid = first; sid <= first 
+ nLeft1; ++sid)
-                                       {
-                                               result.append(sid);
-                                       }
-                               }
-                               break;
-                       case 2:
-                               while ((uint) result.length() < nGlyphs)
-                               {
-                                       first = readCard(pos);
-                                       pos += 2;
-                                       nLeft2 = readCard(pos);
-                                       pos += 2;
-                                       for (sid_type sid = first; sid <= first 
+ nLeft2; ++sid)
-                                       {
-                                               result.append(sid);
-                                       }
-                               }
-                               break;
-               }
-               return result;
-       }
-       
-       
-       QByteArray CFF::dump(const CFF_Variant& var) const
-       {
-               QByteArray result;
-               switch (var.type)
-               {
-                       case cff_varnt_Error:
-                               result += "ERROR";
-                               break;
-                       case cff_varnt_Card:
-                       case cff_varnt_Bool:
-                       case cff_varnt_SID:
-                               result += 
QByteArray::number(var.array[0].toCardinal());
-                               break;
-                       case cff_varnt_Real:
-                               result += 
QByteArray::number(var.array[0].toDouble());
-                               break;
-                       case cff_varnt_Operator:
-                               result += string(var.array[0].toCardinal());
-                               result += " (";
-                               result += 
QByteArray::number(var.array[0].toCardinal());
-                               result +=")";
-                               break;
-                       case cff_varnt_Delta:
-                               result += "Delta";
-                               /* fall through */
-                       case cff_varnt_Array:
-                               result += "[";
-                               for (int i = 0; i < var.array.length(); ++i)
-                               {
-                                       result += 
QByteArray::number(var.array[i].toDouble());
-                                       result += ", ";
-                               }
-                               result.chop(2);
-                               result += "]";
-                               break;
-               }
-               return result;
-       }
-       
-       
-       void CFF::dump()
-       {
-               qDebug() << "CFF" << m_fontTopDicts.count() << "fonts, size =" 
<< m_bytes.size() << "offset size=" << m_offsetSize;
-               QMap<QByteArray,QMap<operator_type,CFF_Variant> >::Iterator it;
-               for (it = m_fontTopDicts.begin(); it != m_fontTopDicts.end(); 
++it)
-               {
-                       qDebug() << "Font" << it.key() << ":";
-                       QMap<operator_type, CFF_Variant>::Iterator it2;
-                       for (it2= it.value().begin(); it2 != it.value().end(); 
++it2)
-                       {
-                               qDebug() << "\t" << cff_operator(it2.key()) << 
"=" << dump(it2.value());
-                       }
-               }
-       }
-       
-       
-       static QByteArray num(uint n)
-       {
-               return QByteArray::number(n);
-       }
-       
-       
-       static void write(QDataStream& out, const QByteArray& data)
-       {
-               out.writeRawData(data.data(), data.length());
-       }
-       
-       static void dumpData(const QList<QByteArray>& index, QDataStream& out, 
const QByteArray& indent)
-       {
-               for (int i = 0; i < index.length(); ++i)
-               {
-                       write(out, indent);
-                       write(out, "<data idx='");
-                       write(out, num(i));
-                       write(out, "' length='");
-                       write(out, num(index[i].length()));
-                       write(out, "' >");
-                       write(out, index[i].toHex());
-                       write(out, "</data>\n");
-               }
-       }
-
-       static void dumpStrings(const QList<QByteArray>& index, QDataStream& 
out, const QByteArray& indent)
-       {
-               for (int i = 0; i < index.length(); ++i)
-               {
-                       write(out, indent);
-                       write(out, "<string idx='");
-                       write(out, num(i));
-                       write(out, "' length='");
-                       write(out, num(index[i].length()));
-                       write(out, "' >");
-                       write(out, index[i]);
-                       write(out, "</string>\n");
-               }
-       }
-
-       static void dumpDict(const CFF& cff, const 
QMap<operator_type,CFF_Variant>& dict, QDataStream& out, const QByteArray& 
indent)
-       {
-               QMap<operator_type,CFF_Variant>::ConstIterator it;
-               for (it = dict.cbegin(); it != dict.cend(); ++it)
-               {
-                       write(out, indent);
-                       write(out, "<keyvalue key='");
-                       write(out, cff_operator(it.key()));
-                       write(out, "' value='");
-                       write(out, cff.dump(it.value()));
-                       write(out, "' />\n");
-               }
-       }
-       
-       void CFF::dump(QDataStream& out) const
-       {
-               write(out, "<CFF version='1.0' offsetSize='" + 
num(m_offsetSize) + "' >\n");
-               for (int f = 0; f < m_names.length(); ++f)
-               {
-                       QByteArray font = m_names[f];
-                       QMap<operator_type, CFF_Variant> topDict = 
m_fontTopDicts[font];
-                       write(out, "  <Font name='" + font + "' >\n");
-                       write(out, "    <TopDict>");
-                       dumpDict(*this, topDict, out, "      ");
-                       write(out, "    </TopDict>\n");
-                       uint pos;
-                       if (topDict.contains(cff_dict_Encoding))
-                       {
-                               uint encOffset = 
topDict[cff_dict_Encoding].array[0].toCardinal();
-                               if (encOffset > 4)
-                               {
-                                       write(out, "    <Encoding>\n    ");
-                                       pos = encOffset;
-                                       QList<uint> enc = readEncoding(pos);
-                                       write(out, readSegment(encOffset, pos - 
encOffset).toHex());
-                                       write(out, "\n    </Encoding>\n");
-                               }
-                               else
-                                       write(out, "    <Encoding predefined='" 
+ num(encOffset) + "' />\n");
-                       }
-                       pos = 
topDict[cff_dict_CharStrings].array[0].toCardinal();
-                       QList<QByteArray> charStrings = readIndex(pos);
-                       uint nglyphs = charStrings.length();
-                       if (topDict.contains(cff_dict_charset))
-                       {
-                               uint charsetOffset = 
topDict[cff_dict_charset].array[0].toCardinal();
-                               if (charsetOffset > 4)
-                               {
-                                       pos = charsetOffset;
-                                       QList<sid_type> charset = 
readCharset(nglyphs, pos);
-                                       write(out, "    <charset>\n");
-                                       for (int i=0; i < charset.length(); ++i)
-                                       {
-                                               write(out, "      <char gid='" 
+ num(i) + "' sid='" + num(charset[i]) + "' >");
-                                               write(out, string(charset[i]) + 
"</char>\n");
-                                       }
-                                       write(out, "    </charset>\n");
-                               }
-                       }
-                       write(out, "    <CharStrings>\n");
-                       dumpData(charStrings, out, "      ");
-                       write(out, "    </CharStrings>\n");
-                       uint privateLength = 
topDict[cff_dict_Private].array[0].toCardinal();
-                       uint privateOffset = 
topDict[cff_dict_Private].array[1].toCardinal();
-                       write(out, "    <PrivateDict offset='" + 
num(privateOffset) + "' length='" + num(privateLength) + ">\n");
-                       QMap<operator_type,CFF_Variant> privateDict = 
getDict(readSegment(privateOffset, privateLength));
-                       dumpDict(*this, privateDict, out, "      ");
-                       write(out, "    </PrivateDict>\n");
-                       if (privateDict.contains(cff_dict_Subrs))
-                       {
-                               uint subrsOffset = 
privateDict[cff_dict_Subrs].array[0].toCardinal();
-                               pos = privateOffset + subrsOffset;
-                               QList<QByteArray> localSubrs = readIndex(pos);
-                               write(out, "    <LocalSubrs offset='" + 
num(subrsOffset) + "' length='" + num(pos-subrsOffset) + "' >\n");
-                               dumpData(localSubrs, out, "      ");
-                               write(out, "    </LocalSubrs>\n");
-                       }
-                       write(out, "  </Font>\n");
-               }
-               write(out, "  <Strings>\n");
-               dumpStrings(m_strings, out, "    ");
-               write(out, "  </Strings>\n");
-               write(out, "  <GlobalSubrs>\n");
-               dumpData(m_globalSubr, out, "    ");
-               write(out, "  </GlobalSubrs>\n");
-               write(out, "</CFF>\n");
-       }
-    
-       /// encodes 'value' as exactly 'nBytes' bytes in Big Endian
-       static QByteArray encodeBE(int nbytes, uint value)
-       {
-               QByteArray result;
-               while (nbytes-- > 0)
-               {
-                       result.prepend(static_cast<uchar>(value & 0xFF));
-                       value >>= 8;
-               }
-               return result;
-       }
-       
-       
-       /// calculates the required offset size to represent 'dataLength'
-       static int requiredOffsetSize(int dataLength)
-       {
-               if (dataLength < 255)
-                       return 1;
-               if (dataLength < 65535)
-                       return 2;
-               if (dataLength < 0xFFFFFF)
-                       return 3;
-               return 4;
-       }
-       
-       
-       static void writeCard(QByteArray& bytes, int val)
-       {
-               int biasedVal = val - cff_dict_biasSmallCard;
-               if (biasedVal >= cff_dict_minOperand && biasedVal <= 
cff_dict_maxSmallCard)
-               {
-                       bytes.append((char) biasedVal);
-                       return;
-               }
-               if (val > 0)
-               {
-                       biasedVal = val - cff_dict_biasPosCard;
-                       if (biasedVal >= 0 &&  biasedVal <= 1023)
-                       {
-                               biasedVal += (cff_dict_minPosCard << 8);
-                               bytes.append(encodeBE(2, biasedVal));
-                               qDebug() << "writeCard" << val << "as" << 
encodeBE(2, biasedVal).toHex();
-                               return;
-                       };
-               }
-               else
-               {
-                       biasedVal = -val + cff_dict_biasNegCard;
-                       if (biasedVal >= 0 &&  biasedVal <= 1023)
-                       {
-                               biasedVal += (cff_dict_minNegCard << 8);
-                               bytes.append(encodeBE(2, biasedVal));
-                               qDebug() << "writeCard" << val << "as" << 
encodeBE(2, biasedVal).toHex();
-                               return;
-                       };
-               }
-               if (val >= -32768 && val <= 32767)
-               {
-                       bytes.append((char) cff_dict_Card16);
-                       bytes.append(encodeBE(2, val));
-               }
-               else
-               {
-                       bytes.append((char) cff_dict_Card32);
-                       bytes.append(encodeBE(4, val));
-               }
-       }
-       
-       
-       /// creates one byte per digit (not ASCII!)
-       static QByteArray bsdNibbles(long long val)
-       {
-               QByteArray result;
-               if (val < 0)
-                       val = -val;
-               
-               do {
-                       result.prepend((char) (val % 10));
-                       val /= 10;
-               }
-               while (val != 0);
-               
-               return result;
-       }
-       
-       
-       static void writeReal(QByteArray& bytes, CFF_Number num)
-       {
-               QByteArray bsd;
-               if (num.card < 0)
-               {
-                       bsd.append((char) cff_nibble_Minus);
-                       bsd.append(bsdNibbles(-num.card));
-               }
-               else
-               {
-                       bsd.append(bsdNibbles(num.card));
-               }
-               if (num.exponent < 0)
-               {
-                       bsd.append((char) cff_nibble_NegExp);
-                       bsd.append(bsdNibbles(-num.exponent));
-               }
-               else if (num.exponent > 0)
-               {
-                       bsd.append((char) cff_nibble_PosExp);
-                       bsd.append(bsdNibbles(num.exponent));
-               }
-               bsd.append((char) cff_nibble_End);
-               if (bsd.length() % 2 == 1)
-                       bsd.append((char) cff_nibble_End);
-               
-               uint start = bytes.length();
-               bytes.append((char) cff_dict_Real);
-               for (int i= 0; i < bsd.length(); i += 2)
-               {
-                       bytes.append((char) ((bsd[i] << 4) | bsd[i+1]));
-               }
-               qDebug() << "writeReal" << num.card << "E" << num.exponent << 
"as" << bytes.mid(start).toHex();
-       }
-       
-       
-       uint CFF::writeSegment(const QByteArray& data)
-       {
-               uint result = m_bytes.length();
-               m_bytes.append(data);
-               return result;
-       }
-       
-       
-       sid_type CFF::createSid(const QByteArray& str)
-       {
-               sid_type result;
-               if (!m_sids.contains(str))
-               {
-                       result = m_strings.length();
-                       m_strings.append(str);
-                       m_sids[str] = result;
-                       qDebug() << "new SID" << result << "for" << str;
-               }
-               else
-               {
-                       result = m_sids[str];
-               }
-               return result;
-       }
-       
-       
-       uint CFF::writeTopDict(const QByteArray& name,
-                                                  const QMap<operator_type, 
CFF_Variant>& dict,
-                                                  const QList<QByteArray>& 
oldStrings,
-                                                  QHash<operator_type, uint>& 
patchAddresses)
-       {
-               m_offsetSize = 4;
-               m_names.append(name);
-               m_fontTopDicts[name] = dict;
-               m_bytes.append((char) 1);
-               m_bytes.append((char) 0);                     // format 1.0
-               m_bytes.append((char) 4);                     // header length 4
-               m_bytes.append((char) 4);                     // offsetSize 4
-               
-               // write Name index
-               m_bytes.append(encodeBE(2,1));                  // count
-               assert (name.length() < 255);
-               m_bytes.append(encodeBE(1, 1));                 // offSize
-               m_bytes.append(encodeBE(1, 1));                 // offset 1
-               m_bytes.append(encodeBE(1, 1 + name.length())); // offset 2
-               m_bytes.append(name);
-               
-               
-               // write TopDict index
-               QByteArray topDict = makeDict(dict, oldStrings, patchAddresses);
-               int offSize = requiredOffsetSize(topDict.length());
-               m_bytes.append(encodeBE(2, 1));                          // 
count
-               m_bytes.append(encodeBE(1, offSize));                    // 
offSize
-               m_bytes.append(encodeBE(offSize, 1));                    // 
offset 1
-               m_bytes.append(encodeBE(offSize, 1 + topDict.length())); // 
offset 2
-               uint start = m_bytes.size();
-               m_bytes.append(topDict);
-               return start;
-       }
-       
-       
-       QByteArray CFF::makeDict(QMap<operator_type, CFF_Variant> dict,
-                                                        const 
QList<QByteArray>& oldStrings,
-                                QHash<operator_type, uint>& patchAddresses)
-       {
-               QByteArray result;
-               if (dict.contains(cff_dict_ROS))
-               {
-                       // pull to front
-                       QList<CFF_Number> ros = dict[cff_dict_ROS].array;
-                       sid_type sid = ros[0].toCardinal();
-                       sid = createSid(oldStrings[sid]);
-                       writeCard(result, sid);
-                       sid = ros[1].toCardinal();
-                       sid = createSid(oldStrings[sid]);
-                       writeCard(result, sid);
-                       writeCard(result, ros[2].toCardinal());
-                       result.append(encodeBE(2, cff_dict_ROS));
-
-               }
-               if (dict.contains(cff_dict_SyntheticBase))
-               {
-                       // pull to front
-                       writeCard(result, 
dict[cff_dict_ROS].array[0].toCardinal());
-                       result.append(encodeBE(2, cff_dict_SyntheticBase));
-               }
-               QMap<operator_type, CFF_Variant>::Iterator it;
-               for (it = dict.begin(); it != dict.end(); ++it)
-               {
-                       QList<CFF_Number> arr = it.value().array;
-                       switch (it.key())
-                       {
-                               case cff_dict_ROS:
-                               case cff_dict_SyntheticBase:
-                                       /* already done */
-                                       break;
-                               case cff_dict_charset:
-                               case cff_dict_Encoding:
-                               case cff_dict_CharStrings:
-                               case cff_dict_FDArray:
-                               case cff_dict_FDSelect:
-                               case cff_dict_Subrs:
-                                       /* remember offset */
-                                       patchAddresses[it.key()] = 
result.length();
-                                       /* write 32 bit offset */
-                                       result.append((char) cff_dict_Card32);
-                                       result.append(encodeBE(4, 
arr[0].toCardinal()));
-                                       break;
-                               case cff_dict_Private:
-                                       /* remember offset */
-                                       patchAddresses[it.key()] = 
result.length();
-                                       /* write 32 bit length */
-                                       result.append((char) cff_dict_Card32);
-                                       result.append(encodeBE(4, 
arr[0].toCardinal()));
-                                       /* write 32 bit offset */
-                                       result.append((char) cff_dict_Card32);
-                                       result.append(encodeBE(4, 
arr[1].toCardinal()));
-                                       break;
-                               case cff_dict_version:
-                               case cff_dict_Notice:
-                               case cff_dict_Copyright:
-                               case cff_dict_FullName:
-                               case cff_dict_FamilyName:
-                               case cff_dict_Weight:
-                               case cff_dict_PostScript:
-                               case cff_dict_BaseFontName:
-                               case cff_dict_FontName:
-                               {
-                                       /* write SID */
-                                       sid_type sid = arr[0].toCardinal();
-                                       sid = createSid(oldStrings[sid]);
-                                       qDebug() << "writeDict SID" << 
arr[0].toCardinal() << "-->" << sid;
-                                       writeCard(result, sid);
-                               }
-                                       break;
-                               default:
-                                       /* write numbers */
-                                       for (int i = 0; i < arr.length(); ++i)
-                                       {
-                                               if (arr[i].type == 
cff_varnt_Real)
-                                                       writeReal(result, 
arr[i]);
-                                               else
-                                                       writeCard(result, 
arr[i].toCardinal());
-                                       }
-                                       break;
-                       }
-                       // write operator
-                       if (it.key() >= 0x0c00)
-                       {
-                               result.append(encodeBE(2, it.key()));
-                       }
-                       else
-                       {
-                               result.append((char) it.key());
-                       }
-               }
-               return result;
-       }
-       
-       
-       void CFF::patch(QHash<operator_type, uint> patchPositions,
-                       uint patchOffset,
-                       operator_type op,
-                       uint offset,
-                       uint length)
-       {
-               if (patchPositions.contains(op))
-               {
-                       uint pos = patchOffset + patchPositions[op];
-                       uchar c;
-                       switch (op)
-                       {
-                               case cff_dict_charset:
-                               case cff_dict_Encoding:
-                               case cff_dict_CharStrings:
-                               case cff_dict_Subrs:
-                               case cff_dict_FDArray:
-                               case cff_dict_FDSelect:
-                                       assert (m_bytes[pos] == (char) 
cff_dict_Card32);
-                                       ++pos;
-                                       m_bytes.replace(pos, 4, encodeBE(4, 
offset));
-                                       qDebug() << "patch" << cff_operator(op) 
<< "offset @" << pos << offset;
-                                       break;
-                               case cff_dict_Private:
-                                       c = m_bytes[pos];
-                                       if (c == cff_dict_Card16)
-                                       {
-                                               if (length > 0)
-                                               {
-                                                       m_bytes.replace(pos+1, 
2, encodeBE(2, length));
-                                                       qDebug() << "patch priv 
short length @" << (pos+1) << length;
-                                               }
-                                               pos += 3;
-                                       }
-                                       else if (c == cff_dict_Card32)
-                                       {
-                                               if (length > 0)
-                                               {
-                                                       m_bytes.replace(pos+1, 
4, encodeBE(4, length));
-                                                       qDebug() << "patch priv 
length @" << (pos+1) << length;
-                                               }
-                                               pos += 5;
-                                       }
-                                       else if (c >= cff_dict_minOperand && c 
<= cff_dict_maxSmallCard)
-                                       {
-                                               pos += 1;
-                                       }
-                                       else if (c >= cff_dict_minPosCard && c 
<= cff_dict_maxNegCard)
-                                       {
-                                               pos += 2;
-                                       }
-                                       else
-                                       {
-                                               /* error */
-                                       }
-                                       assert (m_bytes[pos] == (char) 
cff_dict_Card32);
-                                       ++pos;
-                                       m_bytes.replace(pos, 4, encodeBE(4, 
offset));
-                                       qDebug() << "patch priv offset @" << 
pos << offset;
-                                       break;
-                               default:
-                                       /* error */
-                                       break;
-                       }
-               }
-       }
-       
-       
-       QByteArray CFF::makeIndex(QList<QByteArray> data) const
-       {
-               QByteArray result;
-               uint size = 0;
-               for (int i = 0; i < data.length(); ++i)
-               {
-                       size += data[i].size();
-               }
-               int offSize = requiredOffsetSize(size);
-               result.append(encodeBE(2, data.length())); // count
-               if (data.length() == 0)
-                       return result;
-               
-               result.append(encodeBE(1, offSize));       // offSize
-               uint offset = 1;
-               result.append(encodeBE(offSize, offset));  // offset 1
-               for (int i=0; i < data.length(); ++i)
-               {
-                       offset += data[i].length();
-                       result.append(encodeBE(offSize, offset));
-               }
-               for (int i=0; i < data.length(); ++i)
-               {
-                       result.append(data[i]);
-               }
-               return result;
-       }
-
-       
-       QByteArray CFF::makeCharset(QList<sid_type> sids) const
-       {
-               QByteArray result;
-               // we won't bother with ranges for now
-               result.append('\0'); // format 0
-               for (int i = 1; i < sids.length(); ++i)
-               {
-                       result.append(encodeBE(2, sids[i]));
-               }
-               return result;
-       }
-       
-       
-       QByteArray CFF::makeEncoding(QList<uint> encoding) const
-       {
-               QByteArray result;
-#ifdef UNTESTET_CFF_MAKEENCODING
-               QMap<uint, uchar> codes;
-               QMap<uchar, uint> supplements;
-               uint maxGid = 0;
-               for (uchar c = 0; c < encoding.length(); ++c)
-               {
-                       uint gid = encoding[c];
-                       if (gid > maxGid && gid < 256)
-                               maxGid = gid;
-                       
-                       if (gid != 0)
-                       {
-                               if (gid >= 256 || codes.contains(gid))
-                                       supplements[c] = gid;
-                               else
-                                       codes[gid] = c;
-                       }
-               }
-               result.append('\0');
-               result.append((uchar) (maxGid+1));
-               for (uint gid = 0; gid <= maxGid; ++gid);
-               {
-                       if (codes.contains(gid))
-                               result.append(codes[gid]);
-                       else
-                               result.append('\0'); // this is probably not 
correct
-               }
-               QMap<uchar, uint>::Iterator it;
-               result.append(supplements.count());
-               for (it = supplements.begin(); it != supplements.end(); ++it)
-               {
-                       result.append(it.key());
-                       result.append(encodeBE(2, it.value()));
-               }
-#else
-               result.append((char) 0x80);                        // format 0 
with supplements
-               result.append((char) 0);                                  // no 
encoded glyphs except supplements
-               result.append((char) encoding.length());  // all supplements
-               for (int c = 0; c < encoding.length(); ++c)
-               {
-                       result.append((uchar)c);
-                       result.append(encodeBE(2, encoding[c]));
-               }
-#endif
-               return result;
-       }
-       
-       
-       CFF CFF::extractSubset(uint faceIndex,
-                              QList<uint> cids) const
-       {
-               uint pos;
-               
-               // get top dict
-               QByteArray fontName = m_names[faceIndex];
-               QMap<operator_type, CFF_Variant> topDict = 
m_fontTopDicts[fontName];
-               
-               // get charstrings
-               QList<QByteArray> charStrings;
-               pos = topDict[cff_dict_CharStrings].array[0].toCardinal();
-               charStrings = readIndex(pos);
-               
-               // get encoding (optional, maybe predefined 0-1)
-               QList<uint> encoding;
-               if (topDict.contains(cff_dict_Encoding))
-               {
-                       uint offset = 
topDict[cff_dict_Encoding].array[0].toCardinal();
-                       if (offset > 1)
-                       {
-                               pos = offset;
-                               encoding = readEncoding(pos);
-//                             encodingBytes = readSegment(offset, pos-offset);
-                       }
-                       else
-                               encoding.append(offset);
-               }
-               
-               // get charmap (optional, maybe predefined 0-2)
-               QList<sid_type> charset;
-               if (topDict.contains(cff_dict_charset))
-               {
-                       uint offset = 
topDict[cff_dict_charset].array[0].toCardinal();
-                       if (offset > 2)
-                       {
-                               pos = offset;
-                               charset = readCharset(charStrings.count(), pos);
-//                             charsetBytes = readSegment(offset, pos-offset);
-                       }
-                       else
-                               charset.append(offset);
-               }
-               
-               // get private dict
-               QList<CFF_Number> lengthOffset = 
topDict[cff_dict_Private].array;
-               QMap<operator_type, CFF_Variant> privateDict = 
getDict(readSegment(lengthOffset[1].toCardinal(), 
lengthOffset[0].toCardinal()));
-               
-               // get local subr (optional)
-               QList<QByteArray> localSubrs;
-               if (privateDict.contains(cff_dict_Subrs))
-               {
-                       pos = lengthOffset[1].toCardinal() + 
privateDict[cff_dict_Subrs].array[0].toCardinal();
-                       localSubrs = readIndex(pos);
-               }
-               
-               // now create new font
-               CFF result;
-               result.m_globalSubr = m_globalSubr;  // no changes
-               
-               // subset
-               if (cids.length() > 0)
-               {
-                       // normalize .notdef
-                       cids.removeAll(0);
-                       cids.prepend(0);
-                       
-                       // forget encoding
-                       topDict.remove(cff_dict_Encoding);
-                       encoding = QList<uint>();
-               
-                       // new charset
-                       QList<sid_type> newCharset;
-                       // new charStrings
-                       QList<QByteArray> newCharStrings;
-                       int cidsLength = cids.length();
-                       newCharset.reserve(cidsLength);
-                       newCharStrings.reserve(cidsLength);
-                       for (int i = 0; i < cidsLength; ++i)
-                       {
-                               sid_type gid = cids[i];
-                               sid_type sid = charset[gid];
-                               if (sid < m_strings.length())
-                               {
-                                       sid = result.createSid(m_strings[sid]);
-                               }
-                               newCharset.append(sid);
-                               newCharStrings.append(charStrings[gid]);
-                       }
-                       
-                       charset = newCharset;
-                       charStrings = newCharStrings;
-               }
-               else if (charset.length() > 1)
-               {
-                       // copy over needed strings
-                       for (int i = 0; i < charset.length(); ++i)
-                       {
-                               sid_type cid = charset[i];
-                               if (cid < m_strings.length())
-                               {
-                                       cid = result.createSid(m_strings[cid]);
-                               }
-                               charset[i] = cid;
-                       }
-               }
-               
-               // create new private dict
-               QHash<operator_type, uint> privatePatches;
-               QByteArray privateBytes = result.makeDict(privateDict, 
m_strings, privatePatches);
-               
-               // write new header, name and topdict, remember offset 
positions for patching
-               QHash<operator_type, uint> patchPositions;
-               uint topDictOffset = result.writeTopDict(fontName, topDict, 
m_strings, patchPositions);
-               
-               // write strings
-               // makeDict() needs to be called before this in order to create 
SIDs for used strings
-               result.writeSegment(makeIndex(result.m_strings.mid(sid_last_std 
+ 1)));
-               
-               // write global subr (required but maybe empty)
-               result.writeSegment(makeIndex(m_globalSubr));
-
-               // write encoding
-               uint encodingOffset = encoding.size() > 1? 
result.writeSegment(makeEncoding(encoding)) : encoding.size() == 1? encoding[0] 
: 0;
-               
-               // write charset
-               uint charsetOffset = charset.size() > 1? 
result.writeSegment(makeCharset(charset)) : charset.size() == 1? charset[0] : 0;
-               
-               // write charstrings
-               uint charStringsOffset = 
result.writeSegment(makeIndex(charStrings));
-               
-               // write private dict
-               uint privateOffset = result.writeSegment(privateBytes);
-               
-               // write local subr
-               if (!localSubrs.empty())
-               {
-                       uint localSubrOffset = 
result.writeSegment(makeIndex(localSubrs));
-
-                       result.patch(privatePatches, privateOffset, 
cff_dict_Subrs, localSubrOffset - privateOffset);
-               }
-               
-               // patch topdict offset positions for charset, encoding, 
charstrings, private
-               result.patch(patchPositions, topDictOffset, cff_dict_charset, 
charsetOffset);
-               result.patch(patchPositions, topDictOffset, cff_dict_Encoding, 
encodingOffset);
-               result.patch(patchPositions, topDictOffset, 
cff_dict_CharStrings, charStringsOffset);
-               result.patch(patchPositions, topDictOffset, cff_dict_Private, 
privateOffset, privateBytes.length());
-               
-               // no FDArray and FDSelect yet
-               
-               return result;
-       }
-       
-       
-       
-       QByteArray extractFace(const QByteArray& cff, int faceIndex)
-       {
-               return CFF(cff).extractSubset(faceIndex, QList<uint>()).data();
-       }
-       
-       QByteArray subsetFace(const QByteArray& cff, QList<uint> cids)
-       {
-               return CFF(cff).extractSubset(0, cids).data();
-       }
-       
-} // namespace
+//
+//  cff.cpp
+//  Scribus
+//
+//  Created by Andreas Vox on 04.05.15.
+//
+//
+
+#include "cff.h"
+
+#include <cassert>
+#include <cmath>
+#include <QDebug>
+
+namespace cff {
+       
+       static const char* cffDictKeys[] = {
+               "version",
+               "Notice",
+               "FullName",
+               "FamilyName",
+               "Weight",
+               "FontBBox",
+               "BlueValues",
+               "OtherBlues",
+               "FamilyBlues",
+               "FamilyOtherBlues",
+               "StdHW",
+               "StdVW",
+               "",
+               "UniqueID",
+               "XUID",
+               "charset",
+               "Encoding",
+               "CharStrings",
+               "Private",
+               "Subrs",
+               "defaultWidthX",
+               "nominalWidthX"
+       };
+       
+       static const char* cffDictKeys0c[] = {
+               "Copyright",
+               "IsFixedPitch",
+               "ItalicAngle",
+               "UnderlinePosition",
+               "UnderlineThickness",
+               "PaintType",
+               "CharstringType",
+               "FontMatrix",
+               "StrokeWidth",
+               "BlueScale",
+               "BlueShift",
+               "BlueFuzz",
+               "StemSnapH",
+               "StemSnapV",
+               "ForceBold",
+               "-Reserved-",
+               "-Reserved-",
+               "LanguageGroup",
+               "ExpansionFactor",
+               "InitialRandomSeed",
+               "SyntheticBase",
+               "PostScript",
+               "BaseFontName",
+               "BaseFontBlend",
+               "-Reserved-",
+               "-Reserved-",
+               "-Reserved-",
+               "-Reserved-",
+               "-Reserved-",
+               "-Reserved-",
+               "ROS",
+               "CIDFontVersion",
+               "CIDFontRevision",
+               "CIDFontType",
+               "CIDCount",
+               "UIDBase",
+               "FDArray",
+               "FDSelect",
+               "FontName"
+       };
+       
+       static const char* cff_operator(cff::operator_type id)
+       {
+               if ((id & 0x0c00) == 0x0c00)
+               {
+                       int idx = id & 0xff;
+                       if (idx >= 0 && idx <= 38)
+                               return cffDictKeys0c[idx];
+                       return "";
+               }
+               if ( /* id >= 0 && */  id <= 21)
+                       return cffDictKeys[id];
+               return "";
+       }
+       
+       static const char* stdStrings[] = {
+               /* 0 */
+               ".notdef",
+               "space",
+               "exclam",
+               "quotedbl",
+               "numbersign",
+               "dollar",
+               "percent",
+               "ampersand",
+               "quoteright",
+               "parenleft",
+               /* 10 */
+               "parenright",
+               "asterisk",
+               "plus",
+               "comma",
+               "hyphen",
+               "period",
+               "slash",
+               "zero",
+               "one",
+               "two",
+               "three",
+               "four",
+               "five",
+               "six",
+               "seven",
+               "eight",
+               "nine",
+               "colon",
+               "semicolon",
+               "less",
+               /* 30 */
+               "equal",
+               "greater",
+               "question",
+               "at",
+               "A",
+               "B",
+               "C",
+               "D",
+               "E",
+               "F",
+               "G",
+               "H",
+               "I",
+               "J",
+               "K",
+               "L",
+               "M",
+               "N",
+               "O",
+               "P",
+               /* 50 */
+               "Q",
+               "R",
+               "S",
+               "T",
+               "U",
+               "V",
+               "W",
+               "X",
+               "Y",
+               "Z",
+               "bracketleft",
+               "backslash",
+               "bracketrigh",
+               "asciicircum",
+               "underscore",
+               "quoteleft",
+               "a",
+               "b",
+               "c",
+               "d",
+               /* 70 */
+               "e",
+               "f",
+               "g",
+               "h",
+               "i",
+               "j",
+               "k",
+               "l",
+               "m",
+               "n",
+               "o",
+               "p",
+               "q",
+               "r",
+               "s",
+               "t",
+               "u",
+               "v",
+               "w",
+               "x",
+               /* 90 */
+               "y",
+               "z",
+               "braceleft",
+               "bar",
+               "braceright",
+               "asciitilde",
+               "exclamdown",
+               "cent",
+               "sterling",
+               "fraction",
+               /* 100  */
+               "yen",
+               "florin",
+               "section",
+               "currency",
+               "quotesingle",
+               "quotedblleft",
+               "guillemotleft",
+               "guilsinglleft",
+               "guilsinglright",
+               "fi",
+               "fl",
+               "endash",
+               "dagger",
+               "daggerdbl",
+               "periodcentered",
+               "paragraph",
+               "bullet",
+               "quotesinglbase",
+               "quotedblbase",
+               "quotedblright",
+               /* 120 */
+               "guillemotright",
+               "ellipsis",
+               "perthousand",
+               "questiondown",
+               "grave",
+               "acute",
+               "circumflex",
+               "tilde",
+               "macron",
+               "breve",
+               "dotaccent",
+               "dieresis",
+               "ring",
+               "cedilla",
+               "hungarumlaut",
+               "ogonek",
+               "caron",
+               "emdash",
+               "AE",
+               "ordfeminine",
+               /* 140 */
+               "Lslash",
+               "Oslash",
+               "OE",
+               "ordmasculine",
+               "ae",
+               "dotlessi",
+               "lslash",
+               "oslash",
+               "oe",
+               "germandbls",
+               "onesuperior",
+               "logicalnot",
+               "mu",
+               "trademark",
+               "Eth",
+               "onehalf",
+               "plusminus",
+               "Thorn",
+               "onequarter",
+               "divide",
+               /* 160 */
+               "brokenbar",
+               "degree",
+               "thorn",
+               "threequarters",
+               "twosuperior",
+               "registered",
+               "minus",
+               "eth",
+               "multiply",
+               "threesuperior",
+               "copyright",
+               "Aacute",
+               "Acircumflex",
+               "Adieresis",
+               "Agrave",
+               "Aring",
+               "Atilde",
+               "Ccedilla",
+               "Eacute",
+               "Ecircumflex",
+               /* 180 */
+               "Edieresis",
+               "Egrave",
+               "Iacute",
+               "Icircumflex",
+               "Idieresis",
+               "Igrave",
+               "Ntilde",
+               "Oacute",
+               "Ocircumflex",
+               "Odieresis",
+               "Ograve",
+               "Otilde",
+               "Scaron",
+               "Uacute",
+               "Ucircumflex",
+               "Udieresis",
+               "Ugrave",
+               "Yacute",
+               "Ydieresis",
+               "Zcaron",
+               /* 200 */
+               "aacute",
+               "acircumflex",
+               "adieresis",
+               "agrave",
+               "aring",
+               "atilde",
+               "ccedilla",
+               "eacute",
+               "ecircumflex",
+               "edieresis",
+               "egrave",
+               "iacute",
+               "icircumflex",
+               "idieresis",
+               "igrave",
+               "ntilde",
+               "oacute",
+               "ocircumflex",
+               "odieresis",
+               "ograve",
+               /* 220 */
+               "otilde",
+               "scaron",
+               "uacute",
+               "ucircumflex",
+               "udieresis",
+               "ugrave",
+               "yacute",
+               "ydieresis",
+               "zcaron",
+               "exclamsmall",
+               "Hungarumlautsmall",
+               "dollaroldstyle",
+               "dollarsuperior",
+               "ampersandsmall",
+               "Acutesmall",
+               "parenleftsuperior",
+               "parenrightsuperior",
+               "twodotenleader",
+               "onedotenleader",
+               "zerooldstyle",
+               /* 240 */
+               "oneoldstyle",
+               "twooldstyle",
+               "threeoldstyle",
+               "fouroldstyle",
+               "fiveoldstyle",
+               "sixoldstyle",
+               "sevenoldstyle",
+               "2eightoldstyle",
+               "nineoldstyle",
+               "commasuperior",
+               "threequartersemdash",
+               "periodsuperior",
+               "questionsmall",
+               "asuperior",
+               "bsuperior",
+               "centsuperior",
+               "dsuperior",
+               "esuperior",
+               "isuperior",
+               "lsuperior",
+               /* 260 */
+               "msuperior",
+               "nsuperior",
+               "osuperior",
+               "rsuperior",
+               "ssuperior",
+               "tsuperior",
+               "ff",
+               "ffi",
+               "ffl",
+               "parenleftinferior",
+               "parenrightinferior",
+               "Circumflexsmall",
+               "hyphensuperior",
+               "Gravesmall",
+               "Asmall",
+               "Bsmall",
+               "Csmall",
+               "Dsmall",
+               "Esmall",
+               "Fsmall",
+               /* 280 */
+               "Gsmall",
+               "Hsmall",
+               "Ismall",
+               "Jsmall",
+               "Ksmall",
+               "Lsmall",
+               "Msmall",
+               "Nsmall",
+               "Osmall",
+               "Psmall",
+               "Qsmall",
+               "Rsmall",
+               "Ssmall",
+               "Tsmall",
+               "Usmall",
+               "Vsmall",
+               "Wsmall",
+               "Xsmall",
+               "Ysmall",
+               "Zsmall",
+               /* 300 */
+               "colonmonetary",
+               "onefitted",
+               "rupiah",
+               "Tildesmall",
+               "exclamdownsmall",
+               "centoldstyle",
+               "Lslashsmall",
+               "Scaronsmall",
+               "Zcaronsmall",
+               "Dieresissmall",
+               "Brevesmall",
+               "Caronsmall",
+               "Dotaccentsmall",
+               "Macronsmall",
+               "iguredash",
+               "hypheninferior",
+               "goneksmall",
+               "Ringsmall",
+               "Cedillasmall",
+               "questiondownsmall",
+               /* 320 */
+               "oneeighth",
+               "hreeeighths",
+               "fiveeighths",
+               "seveneighths",
+               "onethird",
+               "twothirds",
+               "zerosuperior",
+               "foursuperior",
+               "fivesuperior",
+               "sixsuperior",
+               "sevensuperior",
+               "eightsuperior",
+               "ninesuperior",
+               "zeroinferior",
+               "oneinferior",
+               "twoinferior",
+               "threeinferior",
+               "fourinferior",
+               "fiveinferior",
+               "sixinferior",
+               /* 340 */
+               "seveninferior",
+               "eightinferior",
+               "nineinferior",
+               "centinferior",
+               "dollarinferior",
+               "periodinferior",
+               "commainferior",
+               "Agravesmall",
+               "Aacutesmall",
+               "Acircumflexsmall",
+               "Atildesmall",
+               "Adieresissmall",
+               "Aringsmall",
+               "AEsmall",
+               "Ccedillasmall",
+               "Egravesmall",
+               "Eacutesmall",
+               "Ecircumflexsmall",
+               "Edieresissmall",
+               "Igravesmall",
+               /* 360 */
+               "Iacutesmall",
+               "Icircumflexsmall",
+               "Idieresissmall",
+               "Ethsmall",
+               "Ntildesmall",
+               "Ogravesmall",
+               "Oacutesmall",
+               "Ocircumflexsmall",
+               "Otildesmall",
+               "Odieresissmall",
+               "OEsmall",
+               "Oslashsmall",
+               "Ugravesmall",
+               "Uacutesmall",
+               "Ucircumflexsmall",
+               "Udieresissmall",
+               "Yacutesmall",
+               "Thornsmall",
+               "Ydieresissmall",
+               "001.000",
+               /* 380 */
+               "001.001",
+               "001.002",
+               "001.003",
+               "Black",
+               "Bold",
+               "Book",
+               "Light",
+               "Medium",
+               "Regular",
+               "Roman",
+               /* 390 */
+               "Semibold"
+       };
+       
+       bool CFF_Number::isCardinal() const
+       {
+               switch (type)
+               {
+                       case cff_varnt_Error:
+                       case cff_varnt_Real:
+                               return false;
+                       default:
+                               return true;
+               }
+       }
+       
+       double CFF_Number::toDouble() const
+       {
+               switch (type)
+               {
+                       case cff_varnt_Real:
+                               return card * std::pow(10.0, exponent);
+                       default:
+                               return toCardinal();
+               }
+
+       }
+       
+       int CFF_Number::toCardinal() const
+       {
+               return card;
+       }
+
+       
+       CFF::CFF() : m_bytes(), m_offsetSize(4)
+       {
+               for (int i = 0; i <= sid_last_std; ++i)
+               {
+                       m_strings.append(stdStrings[i]);
+                       m_sids[stdStrings[i]] = i;
+               }
+       }
+       
+       CFF::CFF(const QByteArray& cff) : m_bytes(cff)
+       {
+               // read header
+               m_offsetSize = cff[cff_offSize];
+               uint pos = cff[cff_hdrSize];
+               qDebug() << "cff header" << m_offsetSize << "starts" << pos;
+               // read names
+               m_names = readIndex(pos);
+               // read top dicts
+               QList<QByteArray> topDicts = readIndex(pos);
+               for (int i = 0; i < m_names.length(); ++i)
+               {
+                       QByteArray fontName = m_names[i];
+                       qDebug() << i << fontName;
+                       if (fontName.length() > 0 && fontName[0] != char(0))
+                       {
+                               m_fontTopDicts[fontName] = getDict(topDicts[i]);
+                               uint privLength = 
m_fontTopDicts[fontName][cff_dict_Private].array[0].toCardinal();
+                               uint privOffset = 
m_fontTopDicts[fontName][cff_dict_Private].array[1].toCardinal();
+                               getDict(readSegment(privOffset, privLength));
+                       }
+               }
+               // read strings
+               for (int i = 0; i <= sid_last_std; ++i)
+               {
+                       m_strings.append(stdStrings[i]);
+               }
+               m_strings.append(readIndex(pos));
+               for (int i = 0; i < m_strings.length(); ++i)
+               {
+//                     if ( i > sid_last_std)
+//                             qDebug() << i << strings[i];
+                       m_sids[m_strings[i]] = i;
+               }
+               // read global subroutines
+               m_globalSubr = readIndex(pos);
+       }
+       
+       
+       QByteArray CFF::readSegment(uint pos, uint size) const
+       {
+               return QByteArray::fromRawData(m_bytes.data() + pos, size);
+       }
+       
+       
+       uint CFF::readCard(uint pos) const
+       {
+               return static_cast<uchar>(m_bytes[pos]) << 8 | 
static_cast<uchar>(m_bytes[pos+1]);
+       }
+       
+       
+       
+       QMap<operator_type,CFF_Variant> CFF::getDict(const QByteArray& dict) 
const
+       {
+               QMap<uint,CFF_Variant> result;
+               QList<CFF_Number> stack;
+               
+               uint pos = 0;
+               while (pos < (uint) dict.length())
+               {
+                       CFF_Number num = parseDictElement(dict, pos);
+                       if (num.type == cff_varnt_Operator)
+                       {
+                               if (stack.length() == 1)
+                               {
+                                       result[num.card] = 
CFF_Variant(stack[0]);
+                               }
+                               else if (stack.length() > 0)
+                               {
+                                       result[num.card] = CFF_Variant(stack);
+                               }
+                               else
+                               {
+                                       /* error */
+                               }
+                               stack = QList<CFF_Number>();
+                       }
+                       else
+                       {
+                               stack.append(num);
+                       }
+               }
+               if (stack.length() > 0)
+               {
+                       /* error */
+               }
+               // TODO: adapt variant type according to operator
+               return result;
+       }
+       
+       
+       CFF_Number CFF::parseDictElement(const QByteArray& dict, uint& pos) 
const
+       {
+               uint code = dict[pos];
+               switch (code)
+               {
+                       case cff_dict_TwoBytes:
+                               code = (code << 8) | dict[++pos];
+                               break;
+                       case cff_dict_Card16:
+                       case cff_dict_Card32:
+                               return parseCard(dict, pos);
+                       case cff_dict_Real:
+                               return parseReal(dict, pos);
+
+                       default:
+                               if (code >= cff_dict_minOperand)
+                               {
+                                       return parseCard(dict, pos);
+                               }
+                               break;
+               }
+               ++pos;
+               CFF_Number num;
+               num.type = cff_varnt_Operator;
+               num.card = code;
+               qDebug() << "parsed operator" << cff_operator(code) << "(" << 
code << ")";
+               return num;
+       }
+       
+       
+       CFF_Number CFF::parseCard(const QByteArray& dict, uint& pos) const
+       {
+               CFF_Number result;
+               result.type = cff_varnt_Card;
+               result.exponent = 0;
+               uint start=pos;
+               uchar b0 = dict[pos++];
+               uchar b1,b2,b3,b4;
+               if (b0 == cff_dict_Card16)
+               {
+                       b1 = dict[pos++];
+                       b2 = dict[pos++];
+                       result.card = static_cast<qint16>((b1 << 8) | b2);
+               }
+               else if (b0 == cff_dict_Card32)
+               {
+                       b1 = dict[pos++];
+                       b2 = dict[pos++];
+                       b3 = dict[pos++];
+                       b4 = dict[pos++];
+                       result.card = static_cast<qint32>((b1 << 24) | (b2 << 
16) | (b3 << 8) | b4);
+               }
+               else if (b0 < cff_dict_minOperand)
+               {
+                       /* error */
+               }
+               else if (b0 <= cff_dict_maxSmallCard)
+               {
+                       result.card = b0 + cff_dict_biasSmallCard;
+               }
+               else if (b0 <= cff_dict_maxPosCard)
+               {
+                       b1 = dict[pos++];
+                       result.card = (b0 - cff_dict_minPosCard) * 256 + b1 + 
cff_dict_biasPosCard;
+               }
+               else if (b0 <= cff_dict_maxNegCard)
+               {
+                       b1 = dict[pos++];
+                       result.card = (cff_dict_minNegCard - b0) * 256 - b1 + 
cff_dict_biasNegCard;
+               }
+               else
+               {
+                       /* error */
+               }
+               qDebug() << "parsed" << 
QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to card " << 
result.card;
+               return result;
+       }
+       
+       
+       CFF_Number CFF::parseReal(const QByteArray& dict, uint& pos) const
+       {
+               assert( dict[pos] == cff_dict_Real );
+               uint start = pos++;
+
+               CFF_Number result;
+               result.type = cff_varnt_Real;
+               result.card = 0;
+               result.exponent = 0;
+               bool upperNibble = false;
+               bool haveSeenMinus = false;
+               bool haveSeenE = false;;
+               bool haveSeenEminus = false;
+               int decimalPointAt = -1;
+               uchar nibble, twoNibbles;
+               do
+               {
+                       // get nibble
+                       if (upperNibble)
+                       {
+                               nibble = twoNibbles & 0x0f;
+                               upperNibble = false;
+                       }
+                       else
+                       {
+                               twoNibbles = dict[pos++];
+                               nibble = twoNibbles >> 4;
+                               upperNibble = true;
+                       }
+                       
+                       // decode
+                       switch (nibble)
+                       {
+                               case cff_nibble_Minus:
+                                       haveSeenMinus = !haveSeenMinus;
+                                       break;
+                               case cff_nibble_Point:
+                                       decimalPointAt = 0;
+                                       break;
+                               case cff_nibble_PosExp:
+                                       haveSeenE = true;
+                                       break;
+                               case cff_nibble_NegExp:
+                                       haveSeenEminus = true;
+                                       break;
+                               case cff_nibble_End:
+                                       break;
+                               default:
+                                       if (nibble > cff_nibble_maxDigit)
+                                       {
+                                               /* error */
+                                       }
+                                       else if (haveSeenE || haveSeenEminus)
+                                       {
+                                               result.exponent *= 10;
+                                               result.exponent += nibble;
+                                       }
+                                       else
+                                       {
+                                               result.card *= 10;
+                                               result.card += nibble;
+                                               if (decimalPointAt >= 0)
+                                               {
+                                                       ++decimalPointAt;
+                                               }
+                                       }
+                                       break;
+                       }
+               } while (nibble != cff_nibble_End);
+                       
+               // finish
+               if (haveSeenMinus)
+                       result.card = -result.card;
+               if (haveSeenEminus)
+                       result.exponent = -result.exponent;
+               if (decimalPointAt > 0)
+                       result.exponent -= decimalPointAt;
+               qDebug() << "parsed" << 
QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to real " << 
result.card << "E" << result.exponent;
+
+               return result;
+       }
+       
+       
+       QList<QByteArray> CFF::readIndex(uint& pos) const
+       {
+               qDebug() << "read INDEX @" << pos;
+               QList<QByteArray> result;
+               uint N = readCard(pos);
+               pos += 2;
+               if (N == 0)
+                       return result;
+
+               uint offSize = m_bytes[pos++];
+               uint dataStart = pos + offSize * (N+1) - 1;
+               qDebug() << "size" << N << "offsetsize" << offSize << 
"dataStart" << dataStart;
+               uint start = 0;
+               uint end = 0;
+               for (uint c = 0; c < offSize; ++c)
+               {
+                       start = start << 8 | (uchar) m_bytes[pos++];
+               }
+               start += dataStart;
+               result.reserve(N);
+               for (uint i = 0; i < N; ++i)
+               {
+                       end = 0;
+                       for (uint c = 0; c < offSize; ++c)
+                       {
+                               end = end << 8 | (uchar) m_bytes[pos++];
+                       }
+                       end += dataStart;
+                       result.append(readSegment(start, end-start));
+                       start = end;
+               }
+               pos = end;
+               qDebug() << "INDEX ends @" << end;
+               return result;
+       }
+       
+       
+       QList<uint> CFF::readEncoding(uint& pos) const
+       {
+               QList<uint> result;
+               result.reserve(256);
+               for(int i = 0; i < 256; ++i)
+                       result.append(0);
+               
+               uchar format = m_bytes[pos++];
+               uchar N = m_bytes[pos++];
+               int gid;
+               uchar code;
+               switch (format)
+               {
+                       case 0x0:
+                       case 0x80:
+                               for (gid = 1; gid <= N; ++gid)
+                               {
+                                       code = m_bytes[pos++];
+                                       if (result[code] == 0)
+                                       {
+                                               result[code] = gid;
+                                       }
+                               }
+                               break;
+                       case 0x1:
+                       case 0x81:
+                               gid = 1;
+                               for (int i = 0; i < N; ++i)
+                               {
+                                       uchar first = m_bytes[pos++];
+                                       uchar nLeft = m_bytes[pos++];
+                                       int lastCode = (int) first + (int) 
nLeft;
+                                       for (int j = first; j <= lastCode; ++j)
+                                       {
+                                               if (result[j] == 0)
+                                               {
+                                                       result[j] = gid;
+                                               }
+                                               ++gid;
+                                       }
+                               }
+                               break;
+               }
+               if (format >= 0x80)
+               {
+                       uchar nSupplements = m_bytes[pos++];
+                       for (int i = 0; i < nSupplements; ++i)
+                       {
+                               code = m_bytes[pos++];
+                               gid = readCard(pos);
+                               pos += 2;
+                               result[code] = gid;
+                       }
+               }
+               return result;
+       }
+       
+       
+       QList<sid_type> CFF::readCharset(uint nGlyphs, uint& pos) const
+       {
+               QList<sid_type> result;
+               result.append(0); // sid for .notdef
+               
+               uchar format = m_bytes[pos++];
+               
+               sid_type first;
+               uchar nLeft1;
+               uint nLeft2;
+               
+               switch (format)
+               {
+                       case 0:
+                               for (uint gid = 1; gid < nGlyphs; ++gid)
+                               {
+                                       result.append(readCard(pos));
+                                       pos += 2;
+                               }
+                               break;
+                       case 1:
+                               while ((uint) result.length() < nGlyphs)
+                               {
+                                       first = readCard(pos);
+                                       pos += 2;
+                                       nLeft1 = m_bytes[pos++];
+                                       for (sid_type sid = first; sid <= first 
+ nLeft1; ++sid)
+                                       {
+                                               result.append(sid);
+                                       }
+                               }
+                               break;
+                       case 2:
+                               while ((uint) result.length() < nGlyphs)
+                               {
+                                       first = readCard(pos);
+                                       pos += 2;
+                                       nLeft2 = readCard(pos);
+                                       pos += 2;
+                                       for (sid_type sid = first; sid <= first 
+ nLeft2; ++sid)
+                                       {
+                                               result.append(sid);
+                                       }
+                               }
+                               break;
+               }
+               return result;
+       }
+       
+       
+       QByteArray CFF::dump(const CFF_Variant& var) const
+       {
+               QByteArray result;
+               switch (var.type)
+               {
+                       case cff_varnt_Error:
+                               result += "ERROR";
+                               break;
+                       case cff_varnt_Card:
+                       case cff_varnt_Bool:
+                       case cff_varnt_SID:
+                               result += 
QByteArray::number(var.array[0].toCardinal());
+                               break;
+                       case cff_varnt_Real:
+                               result += 
QByteArray::number(var.array[0].toDouble());
+                               break;
+                       case cff_varnt_Operator:
+                               result += string(var.array[0].toCardinal());
+                               result += " (";
+                               result += 
QByteArray::number(var.array[0].toCardinal());
+                               result +=")";
+                               break;
+                       case cff_varnt_Delta:
+                               result += "Delta";
+                               /* fall through */
+                       case cff_varnt_Array:
+                               result += "[";
+                               for (int i = 0; i < var.array.length(); ++i)
+                               {
+                                       result += 
QByteArray::number(var.array[i].toDouble());
+                                       result += ", ";
+                               }
+                               result.chop(2);
+                               result += "]";
+                               break;
+               }
+               return result;
+       }
+       
+       
+       void CFF::dump()
+       {
+               qDebug() << "CFF" << m_fontTopDicts.count() << "fonts, size =" 
<< m_bytes.size() << "offset size=" << m_offsetSize;
+               QMap<QByteArray,QMap<operator_type,CFF_Variant> >::Iterator it;
+               for (it = m_fontTopDicts.begin(); it != m_fontTopDicts.end(); 
++it)
+               {
+                       qDebug() << "Font" << it.key() << ":";
+                       QMap<operator_type, CFF_Variant>::Iterator it2;
+                       for (it2= it.value().begin(); it2 != it.value().end(); 
++it2)
+                       {
+                               qDebug() << "\t" << cff_operator(it2.key()) << 
"=" << dump(it2.value());
+                       }
+               }
+       }
+       
+       
+       static QByteArray num(uint n)
+       {
+               return QByteArray::number(n);
+       }
+       
+       
+       static void write(QDataStream& out, const QByteArray& data)
+       {
+               out.writeRawData(data.data(), data.length());
+       }
+       
+       static void dumpData(const QList<QByteArray>& index, QDataStream& out, 
const QByteArray& indent)
+       {
+               for (int i = 0; i < index.length(); ++i)
+               {
+                       write(out, indent);
+                       write(out, "<data idx='");
+                       write(out, num(i));
+                       write(out, "' length='");
+                       write(out, num(index[i].length()));
+                       write(out, "' >");
+                       write(out, index[i].toHex());
+                       write(out, "</data>\n");
+               }
+       }
+
+       static void dumpStrings(const QList<QByteArray>& index, QDataStream& 
out, const QByteArray& indent)
+       {
+               for (int i = 0; i < index.length(); ++i)
+               {
+                       write(out, indent);
+                       write(out, "<string idx='");
+                       write(out, num(i));
+                       write(out, "' length='");
+                       write(out, num(index[i].length()));
+                       write(out, "' >");
+                       write(out, index[i]);
+                       write(out, "</string>\n");
+               }
+       }
+
+       static void dumpDict(const CFF& cff, const 
QMap<operator_type,CFF_Variant>& dict, QDataStream& out, const QByteArray& 
indent)
+       {
+               QMap<operator_type,CFF_Variant>::ConstIterator it;
+               for (it = dict.cbegin(); it != dict.cend(); ++it)
+               {
+                       write(out, indent);
+                       write(out, "<keyvalue key='");
+                       write(out, cff_operator(it.key()));
+                       write(out, "' value='");
+                       write(out, cff.dump(it.value()));
+                       write(out, "' />\n");
+               }
+       }
+       
+       void CFF::dump(QDataStream& out) const
+       {
+               write(out, "<CFF version='1.0' offsetSize='" + 
num(m_offsetSize) + "' >\n");
+               for (int f = 0; f < m_names.length(); ++f)
+               {
+                       QByteArray font = m_names[f];
+                       QMap<operator_type, CFF_Variant> topDict = 
m_fontTopDicts[font];
+                       write(out, "  <Font name='" + font + "' >\n");
+                       write(out, "    <TopDict>");
+                       dumpDict(*this, topDict, out, "      ");
+                       write(out, "    </TopDict>\n");
+                       uint pos;
+                       if (topDict.contains(cff_dict_Encoding))
+                       {
+                               uint encOffset = 
topDict[cff_dict_Encoding].array[0].toCardinal();
+                               if (encOffset > 4)
+                               {
+                                       write(out, "    <Encoding>\n    ");
+                                       pos = encOffset;
+                                       QList<uint> enc = readEncoding(pos);
+                                       write(out, readSegment(encOffset, pos - 
encOffset).toHex());
+                                       write(out, "\n    </Encoding>\n");
+                               }
+                               else
+                                       write(out, "    <Encoding predefined='" 
+ num(encOffset) + "' />\n");
+                       }
+                       pos = 
topDict[cff_dict_CharStrings].array[0].toCardinal();
+                       QList<QByteArray> charStrings = readIndex(pos);
+                       uint nglyphs = charStrings.length();
+                       if (topDict.contains(cff_dict_charset))
+                       {
+                               uint charsetOffset = 
topDict[cff_dict_charset].array[0].toCardinal();
+                               if (charsetOffset > 4)
+                               {
+                                       pos = charsetOffset;
+                                       QList<sid_type> charset = 
readCharset(nglyphs, pos);
+                                       write(out, "    <charset>\n");
+                                       for (int i=0; i < charset.length(); ++i)
+                                       {
+                                               write(out, "      <char gid='" 
+ num(i) + "' sid='" + num(charset[i]) + "' >");
+                                               write(out, string(charset[i]) + 
"</char>\n");
+                                       }
+                                       write(out, "    </charset>\n");
+                               }
+                       }
+                       write(out, "    <CharStrings>\n");
+                       dumpData(charStrings, out, "      ");
+                       write(out, "    </CharStrings>\n");
+                       uint privateLength = 
topDict[cff_dict_Private].array[0].toCardinal();
+                       uint privateOffset = 
topDict[cff_dict_Private].array[1].toCardinal();
+                       write(out, "    <PrivateDict offset='" + 
num(privateOffset) + "' length='" + num(privateLength) + ">\n");
+                       QMap<operator_type,CFF_Variant> privateDict = 
getDict(readSegment(privateOffset, privateLength));
+                       dumpDict(*this, privateDict, out, "      ");
+                       write(out, "    </PrivateDict>\n");
+                       if (privateDict.contains(cff_dict_Subrs))
+                       {
+                               uint subrsOffset = 
privateDict[cff_dict_Subrs].array[0].toCardinal();
+                               pos = privateOffset + subrsOffset;
+                               QList<QByteArray> localSubrs = readIndex(pos);
+                               write(out, "    <LocalSubrs offset='" + 
num(subrsOffset) + "' length='" + num(pos-subrsOffset) + "' >\n");
+                               dumpData(localSubrs, out, "      ");
+                               write(out, "    </LocalSubrs>\n");
+                       }
+                       write(out, "  </Font>\n");
+               }
+               write(out, "  <Strings>\n");
+               dumpStrings(m_strings, out, "    ");
+               write(out, "  </Strings>\n");
+               write(out, "  <GlobalSubrs>\n");
+               dumpData(m_globalSubr, out, "    ");
+               write(out, "  </GlobalSubrs>\n");
+               write(out, "</CFF>\n");
+       }
+    
+       /// encodes 'value' as exactly 'nBytes' bytes in Big Endian
+       static QByteArray encodeBE(int nbytes, uint value)
+       {
+               QByteArray result;
+               while (nbytes-- > 0)
+               {
+                       result.prepend(static_cast<uchar>(value & 0xFF));
+                       value >>= 8;
+               }
+               return result;
+       }
+       
+       
+       /// calculates the required offset size to represent 'dataLength'
+       static int requiredOffsetSize(int dataLength)
+       {
+               if (dataLength < 255)
+                       return 1;
+               if (dataLength < 65535)
+                       return 2;
+               if (dataLength < 0xFFFFFF)
+                       return 3;
+               return 4;
+       }
+       
+       
+       static void writeCard(QByteArray& bytes, int val)
+       {
+               int biasedVal = val - cff_dict_biasSmallCard;
+               if (biasedVal >= cff_dict_minOperand && biasedVal <= 
cff_dict_maxSmallCard)
+               {
+                       bytes.append((char) biasedVal);
+                       return;
+               }
+               if (val > 0)
+               {
+                       biasedVal = val - cff_dict_biasPosCard;
+                       if (biasedVal >= 0 &&  biasedVal <= 1023)
+                       {
+                               biasedVal += (cff_dict_minPosCard << 8);
+                               bytes.append(encodeBE(2, biasedVal));
+                               qDebug() << "writeCard" << val << "as" << 
encodeBE(2, biasedVal).toHex();
+                               return;
+                       };
+               }
+               else
+               {
+                       biasedVal = -val + cff_dict_biasNegCard;
+                       if (biasedVal >= 0 &&  biasedVal <= 1023)
+                       {
+                               biasedVal += (cff_dict_minNegCard << 8);
+                               bytes.append(encodeBE(2, biasedVal));
+                               qDebug() << "writeCard" << val << "as" << 
encodeBE(2, biasedVal).toHex();
+                               return;
+                       };
+               }
+               if (val >= -32768 && val <= 32767)
+               {
+                       bytes.append((char) cff_dict_Card16);
+                       bytes.append(encodeBE(2, val));
+               }
+               else
+               {
+                       bytes.append((char) cff_dict_Card32);
+                       bytes.append(encodeBE(4, val));
+               }
+       }
+       
+       
+       /// creates one byte per digit (not ASCII!)
+       static QByteArray bsdNibbles(long long val)
+       {
+               QByteArray result;
+               if (val < 0)
+                       val = -val;
+               
+               do {
+                       result.prepend((char) (val % 10));
+                       val /= 10;
+               }
+               while (val != 0);
+               
+               return result;
+       }
+       
+       
+       static void writeReal(QByteArray& bytes, CFF_Number num)
+       {
+               QByteArray bsd;
+               if (num.card < 0)
+               {
+                       bsd.append((char) cff_nibble_Minus);
+                       bsd.append(bsdNibbles(-num.card));
+               }
+               else
+               {
+                       bsd.append(bsdNibbles(num.card));
+               }
+               if (num.exponent < 0)
+               {
+                       bsd.append((char) cff_nibble_NegExp);
+                       bsd.append(bsdNibbles(-num.exponent));
+               }
+               else if (num.exponent > 0)
+               {
+                       bsd.append((char) cff_nibble_PosExp);
+                       bsd.append(bsdNibbles(num.exponent));
+               }
+               bsd.append((char) cff_nibble_End);
+               if (bsd.length() % 2 == 1)
+                       bsd.append((char) cff_nibble_End);
+               
+               uint start = bytes.length();
+               bytes.append((char) cff_dict_Real);
+               for (int i= 0; i < bsd.length(); i += 2)
+               {
+                       bytes.append((char) ((bsd[i] << 4) | bsd[i+1]));
+               }
+               qDebug() << "writeReal" << num.card << "E" << num.exponent << 
"as" << bytes.mid(start).toHex();
+       }
+       
+       
+       uint CFF::writeSegment(const QByteArray& data)
+       {
+               uint result = m_bytes.length();
+               m_bytes.append(data);
+               return result;
+       }
+       
+       
+       sid_type CFF::createSid(const QByteArray& str)
+       {
+               sid_type result;
+               if (!m_sids.contains(str))
+               {
+                       result = m_strings.length();
+                       m_strings.append(str);
+                       m_sids[str] = result;
+                       qDebug() << "new SID" << result << "for" << str;
+               }
+               else
+               {
+                       result = m_sids[str];
+               }
+               return result;
+       }
+       
+       
+       uint CFF::writeTopDict(const QByteArray& name,
+                                                  const QMap<operator_type, 
CFF_Variant>& dict,
+                                                  const QList<QByteArray>& 
oldStrings,
+                                                  QHash<operator_type, uint>& 
patchAddresses)
+       {
+               m_offsetSize = 4;
+               m_names.append(name);
+               m_fontTopDicts[name] = dict;
+               m_bytes.append((char) 1);
+               m_bytes.append((char) 0);                     // format 1.0
+               m_bytes.append((char) 4);                     // header length 4
+               m_bytes.append((char) 4);                     // offsetSize 4
+               
+               // write Name index
+               m_bytes.append(encodeBE(2,1));                  // count
+               assert (name.length() < 255);
+               m_bytes.append(encodeBE(1, 1));                 // offSize
+               m_bytes.append(encodeBE(1, 1));                 // offset 1
+               m_bytes.append(encodeBE(1, 1 + name.length())); // offset 2
+               m_bytes.append(name);
+               
+               
+               // write TopDict index
+               QByteArray topDict = makeDict(dict, oldStrings, patchAddresses);
+               int offSize = requiredOffsetSize(topDict.length());
+               m_bytes.append(encodeBE(2, 1));                          // 
count
+               m_bytes.append(encodeBE(1, offSize));                    // 
offSize
+               m_bytes.append(encodeBE(offSize, 1));                    // 
offset 1
+               m_bytes.append(encodeBE(offSize, 1 + topDict.length())); // 
offset 2
+               uint start = m_bytes.size();
+               m_bytes.append(topDict);
+               return start;
+       }
+       
+       
+       QByteArray CFF::makeDict(QMap<operator_type, CFF_Variant> dict,
+                                                        const 
QList<QByteArray>& oldStrings,
+                                QHash<operator_type, uint>& patchAddresses)
+       {
+               QByteArray result;
+               if (dict.contains(cff_dict_ROS))
+               {
+                       // pull to front
+                       QList<CFF_Number> ros = dict[cff_dict_ROS].array;
+                       sid_type sid = ros[0].toCardinal();
+                       sid = createSid(oldStrings[sid]);
+                       writeCard(result, sid);
+                       sid = ros[1].toCardinal();
+                       sid = createSid(oldStrings[sid]);
+                       writeCard(result, sid);
+                       writeCard(result, ros[2].toCardinal());
+                       result.append(encodeBE(2, cff_dict_ROS));
+
+               }
+               if (dict.contains(cff_dict_SyntheticBase))
+               {
+                       // pull to front
+                       writeCard(result, 
dict[cff_dict_ROS].array[0].toCardinal());
+                       result.append(encodeBE(2, cff_dict_SyntheticBase));
+               }
+               QMap<operator_type, CFF_Variant>::Iterator it;
+               for (it = dict.begin(); it != dict.end(); ++it)
+               {
+                       QList<CFF_Number> arr = it.value().array;
+                       switch (it.key())
+                       {
+                               case cff_dict_ROS:
+                               case cff_dict_SyntheticBase:
+                                       /* already done */
+                                       break;
+                               case cff_dict_charset:
+                               case cff_dict_Encoding:
+                               case cff_dict_CharStrings:
+                               case cff_dict_FDArray:
+                               case cff_dict_FDSelect:
+                               case cff_dict_Subrs:
+                                       /* remember offset */
+                                       patchAddresses[it.key()] = 
result.length();
+                                       /* write 32 bit offset */
+                                       result.append((char) cff_dict_Card32);
+                                       result.append(encodeBE(4, 
arr[0].toCardinal()));
+                                       break;
+                               case cff_dict_Private:
+                                       /* remember offset */
+                                       patchAddresses[it.key()] = 
result.length();
+                                       /* write 32 bit length */
+                                       result.append((char) cff_dict_Card32);
+                                       result.append(encodeBE(4, 
arr[0].toCardinal()));
+                                       /* write 32 bit offset */
+                                       result.append((char) cff_dict_Card32);
+                                       result.append(encodeBE(4, 
arr[1].toCardinal()));
+                                       break;
+                               case cff_dict_version:
+                               case cff_dict_Notice:
+                               case cff_dict_Copyright:
+                               case cff_dict_FullName:
+                               case cff_dict_FamilyName:
+                               case cff_dict_Weight:
+                               case cff_dict_PostScript:
+                               case cff_dict_BaseFontName:
+                               case cff_dict_FontName:
+                               {
+                                       /* write SID */
+                                       sid_type sid = arr[0].toCardinal();
+                                       sid = createSid(oldStrings[sid]);
+                                       qDebug() << "writeDict SID" << 
arr[0].toCardinal() << "-->" << sid;
+                                       writeCard(result, sid);
+                               }
+                                       break;
+                               default:
+                                       /* write numbers */
+                                       for (int i = 0; i < arr.length(); ++i)
+                                       {
+                                               if (arr[i].type == 
cff_varnt_Real)
+                                                       writeReal(result, 
arr[i]);
+                                               else
+                                                       writeCard(result, 
arr[i].toCardinal());
+                                       }
+                                       break;
+                       }
+                       // write operator
+                       if (it.key() >= 0x0c00)
+                       {
+                               result.append(encodeBE(2, it.key()));
+                       }
+                       else
+                       {
+                               result.append((char) it.key());
+                       }
+               }
+               return result;
+       }
+       
+       
+       void CFF::patch(QHash<operator_type, uint> patchPositions,
+                       uint patchOffset,
+                       operator_type op,
+                       uint offset,
+                       uint length)
+       {
+               if (patchPositions.contains(op))
+               {
+                       uint pos = patchOffset + patchPositions[op];
+                       uchar c;
+                       switch (op)
+                       {
+                               case cff_dict_charset:
+                               case cff_dict_Encoding:
+                               case cff_dict_CharStrings:
+                               case cff_dict_Subrs:
+                               case cff_dict_FDArray:
+                               case cff_dict_FDSelect:
+                                       assert (m_bytes[pos] == (char) 
cff_dict_Card32);
+                                       ++pos;
+                                       m_bytes.replace(pos, 4, encodeBE(4, 
offset));
+                                       qDebug() << "patch" << cff_operator(op) 
<< "offset @" << pos << offset;
+                                       break;
+                               case cff_dict_Private:
+                                       c = m_bytes[pos];
+                                       if (c == cff_dict_Card16)
+                                       {
+                                               if (length > 0)
+                                               {
+                                                       m_bytes.replace(pos+1, 
2, encodeBE(2, length));
+                                                       qDebug() << "patch priv 
short length @" << (pos+1) << length;
+                                               }
+                                               pos += 3;
+                                       }
+                                       else if (c == cff_dict_Card32)
+                                       {
+                                               if (length > 0)
+                                               {
+                                                       m_bytes.replace(pos+1, 
4, encodeBE(4, length));
+                                                       qDebug() << "patch priv 
length @" << (pos+1) << length;
+                                               }
+                                               pos += 5;
+                                       }
+                                       else if (c >= cff_dict_minOperand && c 
<= cff_dict_maxSmallCard)
+                                       {
+                                               pos += 1;
+                                       }
+                                       else if (c >= cff_dict_minPosCard && c 
<= cff_dict_maxNegCard)
+                                       {
+                                               pos += 2;
+                                       }
+                                       else
+                                       {
+                                               /* error */
+                                       }
+                                       assert (m_bytes[pos] == (char) 
cff_dict_Card32);
+                                       ++pos;
+                                       m_bytes.replace(pos, 4, encodeBE(4, 
offset));
+                                       qDebug() << "patch priv offset @" << 
pos << offset;
+                                       break;
+                               default:
+                                       /* error */
+                                       break;
+                       }
+               }
+       }
+       
+       
+       QByteArray CFF::makeIndex(QList<QByteArray> data) const
+       {
+               QByteArray result;
+               uint size = 0;
+               for (int i = 0; i < data.length(); ++i)
+               {
+                       size += data[i].size();
+               }
+               int offSize = requiredOffsetSize(size);
+               result.append(encodeBE(2, data.length())); // count
+               if (data.length() == 0)
+                       return result;
+               
+               result.append(encodeBE(1, offSize));       // offSize
+               uint offset = 1;
+               result.append(encodeBE(offSize, offset));  // offset 1
+               for (int i=0; i < data.length(); ++i)
+               {
+                       offset += data[i].length();
+                       result.append(encodeBE(offSize, offset));
+               }
+               for (int i=0; i < data.length(); ++i)
+               {
+                       result.append(data[i]);
+               }
+               return result;
+       }
+
+       
+       QByteArray CFF::makeCharset(QList<sid_type> sids) const
+       {
+               QByteArray result;
+               // we won't bother with ranges for now
+               result.append('\0'); // format 0
+               for (int i = 1; i < sids.length(); ++i)
+               {
+                       result.append(encodeBE(2, sids[i]));
+               }
+               return result;
+       }
+       
+       
+       QByteArray CFF::makeEncoding(QList<uint> encoding) const
+       {
+               QByteArray result;
+#ifdef UNTESTET_CFF_MAKEENCODING
+               QMap<uint, uchar> codes;
+               QMap<uchar, uint> supplements;
+               uint maxGid = 0;
+               for (uchar c = 0; c < encoding.length(); ++c)
+               {
+                       uint gid = encoding[c];
+                       if (gid > maxGid && gid < 256)
+                               maxGid = gid;
+                       
+                       if (gid != 0)
+                       {
+                               if (gid >= 256 || codes.contains(gid))
+                                       supplements[c] = gid;
+                               else
+                                       codes[gid] = c;
+                       }
+               }
+               result.append('\0');
+               result.append((uchar) (maxGid+1));
+               for (uint gid = 0; gid <= maxGid; ++gid);
+               {
+                       if (codes.contains(gid))
+                               result.append(codes[gid]);
+                       else
+                               result.append('\0'); // this is probably not 
correct
+               }
+               QMap<uchar, uint>::Iterator it;
+               result.append(supplements.count());
+               for (it = supplements.begin(); it != supplements.end(); ++it)
+               {
+                       result.append(it.key());
+                       result.append(encodeBE(2, it.value()));
+               }
+#else
+               result.append((char) 0x80);                        // format 0 
with supplements
+               result.append((char) 0);                                  // no 
encoded glyphs except supplements
+               result.append((char) encoding.length());  // all supplements
+               for (int c = 0; c < encoding.length(); ++c)
+               {
+                       result.append((uchar)c);
+                       result.append(encodeBE(2, encoding[c]));
+               }
+#endif
+               return result;
+       }
+       
+       
+       CFF CFF::extractSubset(uint faceIndex, QList<uint> cids) const
+       {
+               uint pos;
+               
+               // get top dict
+               QByteArray fontName = m_names[faceIndex];
+               QMap<operator_type, CFF_Variant> topDict = 
m_fontTopDicts[fontName];
+               
+               // get charstrings
+               QList<QByteArray> charStrings;
+               pos = topDict[cff_dict_CharStrings].array[0].toCardinal();
+               charStrings = readIndex(pos);
+               
+               // get encoding (optional, maybe predefined 0-1)
+               QList<uint> encoding;
+               if (topDict.contains(cff_dict_Encoding))
+               {
+                       uint offset = 
topDict[cff_dict_Encoding].array[0].toCardinal();
+                       if (offset > 1)
+                       {
+                               pos = offset;
+                               encoding = readEncoding(pos);
+//                             encodingBytes = readSegment(offset, pos-offset);
+                       }
+                       else
+                               encoding.append(offset);
+               }
+               
+               // get charmap (optional, maybe predefined 0-2)
+               QList<sid_type> charset;
+               if (topDict.contains(cff_dict_charset))
+               {
+                       uint offset = 
topDict[cff_dict_charset].array[0].toCardinal();
+                       if (offset > 2)
+                       {
+                               pos = offset;
+                               charset = readCharset(charStrings.count(), pos);
+//                             charsetBytes = readSegment(offset, pos-offset);
+                       }
+                       else
+                               charset.append(offset);
+               }
+               
+               // get private dict
+               QList<CFF_Number> lengthOffset = 
topDict[cff_dict_Private].array;
+               QMap<operator_type, CFF_Variant> privateDict = 
getDict(readSegment(lengthOffset[1].toCardinal(), 
lengthOffset[0].toCardinal()));
+               
+               // get local subr (optional)
+               QList<QByteArray> localSubrs;
+               if (privateDict.contains(cff_dict_Subrs))
+               {
+                       pos = lengthOffset[1].toCardinal() + 
privateDict[cff_dict_Subrs].array[0].toCardinal();
+                       localSubrs = readIndex(pos);
+               }
+               
+               // now create new font
+               CFF result;
+               result.m_globalSubr = m_globalSubr;  // no changes
+               
+               // subset
+               if (cids.length() > 0)
+               {
+                       // normalize .notdef
+                       cids.removeAll(0);
+                       cids.prepend(0);
+                       
+                       // forget encoding
+                       topDict.remove(cff_dict_Encoding);
+                       encoding = QList<uint>();
+               
+                       // new charset
+                       QList<sid_type> newCharset;
+                       // new charStrings
+                       QList<QByteArray> newCharStrings;
+                       int cidsLength = cids.length();
+                       newCharset.reserve(cidsLength);
+                       newCharStrings.reserve(cidsLength);
+                       for (int i = 0; i < cidsLength; ++i)
+                       {
+                               sid_type gid = cids[i];
+                               sid_type sid = charset[gid];
+                               if (sid < m_strings.length())
+                               {
+                                       sid = result.createSid(m_strings[sid]);
+                               }
+                               newCharset.append(sid);
+                               newCharStrings.append(charStrings[gid]);
+                       }
+                       
+                       charset = newCharset;
+                       charStrings = newCharStrings;
+               }
+               else if (charset.length() > 1)
+               {
+                       // copy over needed strings
+                       for (int i = 0; i < charset.length(); ++i)
+                       {
+                               sid_type cid = charset[i];
+                               if (cid < m_strings.length())
+                               {
+                                       cid = result.createSid(m_strings[cid]);
+                               }
+                               charset[i] = cid;
+                       }
+               }
+               
+               // create new private dict
+               QHash<operator_type, uint> privatePatches;
+               QByteArray privateBytes = result.makeDict(privateDict, 
m_strings, privatePatches);
+               
+               // write new header, name and topdict, remember offset 
positions for patching
+               QHash<operator_type, uint> patchPositions;
+               uint topDictOffset = result.writeTopDict(fontName, topDict, 
m_strings, patchPositions);
+               
+               // write strings
+               // makeDict() needs to be called before this in order to create 
SIDs for used strings
+               result.writeSegment(makeIndex(result.m_strings.mid(sid_last_std 
+ 1)));
+               
+               // write global subr (required but maybe empty)
+               result.writeSegment(makeIndex(m_globalSubr));
+
+               // write encoding
+               uint encodingOffset = encoding.size() > 1 ? 
result.writeSegment(makeEncoding(encoding)) : (encoding.size() == 1 ? 
encoding[0] : 0);
+               
+               // write charset
+               uint charsetOffset = charset.size() > 1 ? 
result.writeSegment(makeCharset(charset)) : (charset.size() == 1 ? charset[0] : 
0);
+               
+               // write charstrings
+               uint charStringsOffset = 
result.writeSegment(makeIndex(charStrings));
+               
+               // write private dict
+               uint privateOffset = result.writeSegment(privateBytes);
+               
+               // write local subr
+               if (!localSubrs.empty())
+               {
+                       uint localSubrOffset = 
result.writeSegment(makeIndex(localSubrs));
+
+                       result.patch(privatePatches, privateOffset, 
cff_dict_Subrs, localSubrOffset - privateOffset);
+               }
+               
+               // patch topdict offset positions for charset, encoding, 
charstrings, private
+               result.patch(patchPositions, topDictOffset, cff_dict_charset, 
charsetOffset);
+               result.patch(patchPositions, topDictOffset, cff_dict_Encoding, 
encodingOffset);
+               result.patch(patchPositions, topDictOffset, 
cff_dict_CharStrings, charStringsOffset);
+               result.patch(patchPositions, topDictOffset, cff_dict_Private, 
privateOffset, privateBytes.length());
+               
+               // no FDArray and FDSelect yet
+               
+               return result;
+       }
+       
+       QByteArray extractFace(const QByteArray& cff, int faceIndex)
+       {
+               return CFF(cff).extractSubset(faceIndex, QList<uint>()).data();
+       }
+       
+       QByteArray subsetFace(const QByteArray& cff, QList<uint> cids, 
QMap<uint, uint>& glyphMap)
+       {
+               glyphMap.clear();
+               QByteArray result = CFF(cff).extractSubset(0, cids).data();
+               if (!result.isEmpty())
+               {
+                       for (int i = 0; i < cids.length(); ++i)
+                               glyphMap[cids[i]] = i;
+               }
+               return result;
+       }
+       
+} // namespace

Modified: trunk/Scribus/scribus/fonts/cff.h
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/fonts/cff.h
==============================================================================
--- trunk/Scribus/scribus/fonts/cff.h   (original)
+++ trunk/Scribus/scribus/fonts/cff.h   Wed Feb 10 21:54:56 2021
@@ -242,7 +242,7 @@
        };
        
        QByteArray extractFace(const QByteArray& cff, int faceIndex);
-       QByteArray subsetFace(const QByteArray& cff, QList<uint> cids);
+       QByteArray subsetFace(const QByteArray& cff, QList<uint> cids, 
QMap<uint, uint>& glyphMap);
 }
 
 

Modified: trunk/Scribus/scribus/fonts/sfnt.cpp
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/fonts/sfnt.cpp
==============================================================================
--- trunk/Scribus/scribus/fonts/sfnt.cpp        (original)
+++ trunk/Scribus/scribus/fonts/sfnt.cpp        Wed Feb 10 21:54:56 2021
@@ -14,6 +14,36 @@
 #include FT_TRUETYPE_IDS_H
 
 #include <QDebug>
+
+#include <harfbuzz/hb.h>
+#include <harfbuzz/hb-subset.h>
+
+struct HbBlobDeleter
+{
+       static void cleanup(void *pointer)
+       {
+               if (pointer)
+                       hb_blob_destroy((hb_blob_t*) pointer);
+       }
+};
+
+struct HbFaceDeleter
+{
+       static void cleanup(void *pointer)
+       {
+               if (pointer)
+                       hb_face_destroy((hb_face_t*) pointer);
+       }
+};
+
+struct HbSubsetInputDeleter
+{
+       static void cleanup(void *pointer)
+       {
+               if (pointer)
+                       hb_subset_input_destroy((hb_subset_input_t*) pointer);
+       }
+};
 
 namespace sfnt {
        
@@ -997,7 +1027,6 @@
                
                return result;
        }
-       
 
        QList<uint> copyGlyph(QList<uint>& destLoca, QByteArray& destGlyf, uint 
destGid,
                                                  const QList<uint>& srcLoca, 
const QByteArray& srcGlyf, uint srcGid,
@@ -1039,13 +1068,13 @@
                return compositeElements;
        }
        
-       QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs)
+       QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs, 
QMap<uint, uint>& glyphMap)
        {
                QMap<QByteArray,QByteArray> tables;
                
                //              qDebug() << "loca table:" << (void*) oldLoca[0] 
<< (void*) oldLoca[1] << (void*) oldLoca[2] << (void*) oldLoca[3] << (void*) 
oldLoca[4] << (void*) oldLoca[5] << (void*) oldLoca[6] << (void*) oldLoca[7];
 
-               QMap<uint,uint> newForOldGid;
+               QMap<uint, uint> newForOldGid;
                if (glyphs.length() == 0)
                {
                        tables["loca"] = getTable(ttf, "loca");
@@ -1080,7 +1109,6 @@
                        tables["loca"] = writeLoca(newLoca, 
hasLongLocaFormat(ttf));
                        tables["glyf"] = newGlyf;
                }
-               
                
                QMap<uint,uint> cmap = readCMap(ttf);
                QMap<uint,uint>::iterator it;
@@ -1106,7 +1134,6 @@
                        }
                }
                tables["cmap"] = writeCMap(cmap);
-
                
                QByteArray os2 = getTable(ttf, "OS/2");
                if (os2.length() > ttf_os2_usLastCharIndex)
@@ -1194,9 +1221,69 @@
                headTable = word(font, headTable + ttf_TableRecord_offset);
                putWord(font, headTable + ttf_head_checkSumAdjustment, 
checkSumAdjustment);
                
+               glyphMap.clear();
+               for (int i = 0; i < glyphs.length(); ++i)
+                       glyphMap[glyphs[i]] = i;
                // done!
                
                return font;
+       }
+
+       QByteArray subsetFaceWithHB(const QByteArray& cff, QList<uint> cids, 
int faceIndex, QMap<uint, uint>& glyphMap)
+       {
+               glyphMap.clear();
+
+#if HB_VERSION_ATLEAST(2, 0, 0)
+               QScopedPointer<hb_blob_t, HbBlobDeleter> 
hbBlob(hb_blob_create(cff.data(), cff.length(), HB_MEMORY_MODE_READONLY, 
nullptr, nullptr));
+               if (hbBlob.isNull())
+                       return QByteArray();
+
+               QScopedPointer<hb_face_t, HbFaceDeleter> 
hbFullFace(hb_face_create(hbBlob.get(), faceIndex));
+               if (hbFullFace.isNull())
+                       return QByteArray();
+
+               QScopedPointer<hb_subset_input_t, HbSubsetInputDeleter> 
hbSubsetInput(hb_subset_input_create_or_fail());
+               hb_set_t* glyphSet = 
hb_subset_input_glyph_set(hbSubsetInput.get());
+               if (glyphSet == nullptr)
+                       return QByteArray();
+
+               for (int i = 0; i < cids.count(); ++i)
+                       hb_set_add(glyphSet, cids.at(i));
+       
+               hb_subset_input_set_retain_gids(hbSubsetInput.get(), true);
+               hb_subset_input_set_drop_hints(hbSubsetInput.get(), false);
+               hb_subset_input_set_name_legacy(hbSubsetInput.get(), true);
+
+               QScopedPointer<hb_face_t, HbFaceDeleter> 
hbSubsetFace(hb_subset(hbFullFace.get(), hbSubsetInput.get()));
+               if (hbSubsetFace.isNull())
+                       return QByteArray();
+
+               QScopedPointer<hb_blob_t, HbBlobDeleter> 
hbSubsetBlob(hb_face_reference_blob(hbSubsetFace.get()));
+               if (hbSubsetBlob.isNull())
+                       return QByteArray();
+
+               unsigned int length;
+               const char* subsetData = hb_blob_get_data(hbSubsetBlob.get(), 
&length);
+
+               QByteArray subset(subsetData, length);
+               if (!subset.isEmpty())
+               {
+                       for (int i = 0; i < cids.length(); ++i)
+                               glyphMap[cids[i]] = cids[i];
+               }
+               return subset;
+#else
+               return QByteArray();
+#endif
+       }
+
+       bool canSubsetOpenTypeFonts()
+       {
+#if HB_VERSION_ATLEAST(2, 0, 0)
+               return true;
+#else
+               return false;
+#endif
        }
        
 } // namespace sfnt

Modified: trunk/Scribus/scribus/fonts/sfnt.h
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/fonts/sfnt.h
==============================================================================
--- trunk/Scribus/scribus/fonts/sfnt.h  (original)
+++ trunk/Scribus/scribus/fonts/sfnt.h  Wed Feb 10 21:54:56 2021
@@ -31,30 +31,33 @@
        bool  copy(QByteArray & dst, uint to, const QByteArray & src, uint 
from, uint len);
        QByteArray tag(QByteArray const & bb, uint pos);
        QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag);
+       
+       QByteArray extractFace(const QByteArray& ttfColl, int faceIndex);
+       QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs, 
QMap<uint, uint>& glyphMap);
+       QByteArray subsetFaceWithHB(const QByteArray& cff, QList<uint> glyphs, 
int faceIndex, QMap<uint, uint>& glyphMap);
 
-       QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs);
-       QByteArray extractFace(const QByteArray& ttfColl, int faceIndex);
+       bool canSubsetOpenTypeFonts();
 
-/**
- This class checks the post table of a ttf font.
- */
-class SCRIBUS_API PostTable
-{
-       public:
-               PostTable() = default;
-               uint numberOfGlyphs() const;
-               QString nameFor(uint glyphId) const;
-               void readFrom(FT_Face face);
-               bool usable() const;
-               void setUsable(bool usable);
-               QString errorMsg() const;
-               void setErrorMsg(const QString& errorMsg);
+       /**
+        This class checks the post table of a ttf font.
+        */
+       class SCRIBUS_API PostTable
+       {
+               public:
+                       PostTable() = default;
+                       uint numberOfGlyphs() const;
+                       QString nameFor(uint glyphId) const;
+                       void readFrom(FT_Face face);
+                       bool usable() const;
+                       void setUsable(bool usable);
+                       QString errorMsg() const;
+                       void setErrorMsg(const QString& errorMsg);
 
-       private:
-               QList<QString> m_names;
-               bool m_usable {false};
-               QString m_errorMsg;
-};
+               private:
+                       QList<QString> m_names;
+                       bool m_usable {false};
+                       QString m_errorMsg;
+       };
 
 } //namespace
 #endif

Modified: trunk/Scribus/scribus/pdflib_core.cpp
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/pdflib_core.cpp
==============================================================================
--- trunk/Scribus/scribus/pdflib_core.cpp       (original)
+++ trunk/Scribus/scribus/pdflib_core.cpp       Wed Feb 10 21:54:56 2021
@@ -2162,7 +2162,6 @@
 }
 */
 
-
 PdfFont PDFLibCore::PDF_WriteTtfSubsetFont(const QByteArray& fontName, ScFace& 
face, const QMap<uint, QString>& usedGlyphs)
 {
        QByteArray font;
@@ -2171,7 +2170,10 @@
        QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
        glyphs.removeAll(0);
        glyphs.prepend(0);
-       QByteArray subset = sfnt::subsetFace(font, glyphs);
+
+       QMap<uint, uint> glyphMap;
+       QByteArray subset = sfnt::subsetFace(font, glyphs, glyphMap);
+
        /*dumpFont(face.psName() + "subs.ttf", subset);*/
        QByteArray baseFont   = sanitizeFontName(face.psName());
        QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
@@ -2179,17 +2181,9 @@
        PdfId embeddedFontObj = PDF_EmbedFontObject(subset, QByteArray());
        PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, 
face.format(), embeddedFontObj);
        
-       QMap<uint,uint> glyphmap;
-       for (int i = 0; i < glyphs.length(); ++i)
-       {
-               glyphmap[glyphs[i]] = i;
-               //qDebug() << glyphs[i] << " --> " << i << 
QChar(fullEncoding[glyphs[i]].charcode);
-       }
-       
-       PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, 
usedGlyphs, glyphmap);
+       PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, 
usedGlyphs, glyphMap);
        return result;
 }
-
 
 PdfFont PDFLibCore::PDF_WriteCffSubsetFont(const QByteArray& fontName, ScFace& 
face, const QMap<uint, QString>& usedGlyphs)
 {
@@ -2213,25 +2207,52 @@
        QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
        glyphs.removeAll(0);
        glyphs.prepend(0);
-       QByteArray subset = cff::subsetFace(font, glyphs);
+
+       QMap<uint, uint> glyphMap;
+       QByteArray subset = cff::subsetFace(font, glyphs, glyphMap);
+       if (subset.isEmpty())
+       {
+               PdfFont result = PDF_WriteType3Font(fontName, face, usedGlyphs);
+               return result;
+       }
+
        /*dumpFont(face.psName() + "subs.cff", subset);*/
        QByteArray baseFont   = sanitizeFontName(face.psName());
        QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
        QByteArray subsetName = subsetTag + '+' + baseFont;
        PdfId embeddedFontObj = PDF_EmbedFontObject(subset, "/CIDFontType0C");
        PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, 
face.format(), embeddedFontObj);
-
-       QMap<uint,uint> glyphmap;
-       for (int i = 0; i < glyphs.length(); ++i)
-       {
-               glyphmap[glyphs[i]] = i;
-               //qDebug() << glyphs[i] << " --> " << i << 
QChar(fullEncoding[glyphs[i]].charcode);
-       }
        
-       PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, 
usedGlyphs, glyphmap);
+       PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, 
usedGlyphs, glyphMap);
        return result;
 }
 
+PdfFont PDFLibCore::PDF_WriteOpenTypeSubsetFont(const QByteArray& fontName, 
ScFace& face, const QMap<uint, QString>& usedGlyphs)
+{
+       QByteArray data;
+       face.rawData(data);
+       QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
+       glyphs.removeAll(0);
+       glyphs.prepend(0);
+
+       QMap<uint, uint> glyphMap;
+       QByteArray subset = sfnt::subsetFaceWithHB(data, glyphs, 
face.faceIndex(), glyphMap);
+       if (subset.isEmpty())
+       {
+               PdfFont result = PDF_WriteType3Font(fontName, face, usedGlyphs);
+               return result;
+       }
+
+       /*dumpFont(face.psName() + "subs.otf", subset);*/
+       QByteArray baseFont   = sanitizeFontName(face.psName());
+       QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
+       QByteArray subsetName = subsetTag + '+' + baseFont;
+       PdfId embeddedFontObj = PDF_EmbedFontObject(subset, "/OpenType");
+       PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, 
face.format(), embeddedFontObj);
+       
+       PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, 
usedGlyphs, glyphMap);
+       return result;
+}
 
 PdfId PDFLibCore::PDF_EmbedType1AsciiFontObject(const QByteArray& fon)
 {
@@ -2395,6 +2416,10 @@
                                        if (face.type() == ScFace::TTF)
                                        {
                                                pdfFont = 
PDF_WriteTtfSubsetFont(fontName, face, usedGlyphs);
+                                       }
+                                       else if (face.type() == ScFace::OTF && 
face.isCIDKeyed() && Options.supportsEmbeddedOpenTypeFonts() && 
sfnt::canSubsetOpenTypeFonts())
+                                       {
+                                               pdfFont = 
PDF_WriteOpenTypeSubsetFont(fontName, face, usedGlyphs);
                                        }
                                        else if (face.isCIDKeyed())
                                        {

Modified: trunk/Scribus/scribus/pdflib_core.h
URL: 
http://scribus.net/websvn/diff.php?repname=Scribus&rev=24464&path=/trunk/Scribus/scribus/pdflib_core.h
==============================================================================
--- trunk/Scribus/scribus/pdflib_core.h (original)
+++ trunk/Scribus/scribus/pdflib_core.h Wed Feb 10 21:54:56 2021
@@ -109,6 +109,7 @@
        PdfId PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, 
ScFace::FontFormat fformat, PdfId embeddedFontObject);
        PdfFont PDF_WriteTtfSubsetFont(const QByteArray& fontName, ScFace& 
face, const QMap<uint, QString>& usedGlyphs);
        PdfFont PDF_WriteCffSubsetFont(const QByteArray& fontName, ScFace& 
face, const QMap<uint, QString>& usedGlyphs);
+       PdfFont PDF_WriteOpenTypeSubsetFont(const QByteArray& fontName, ScFace& 
face, const QMap<uint, QString>& usedGlyphs);
        PdfFont PDF_EncodeSimpleFont(const QByteArray& fontname, ScFace& face,  
const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId 
fontDes, const QMap<uint, QString>& usedGlyphs);
        PdfFont PDF_EncodeCidFont(const QByteArray& fontname, ScFace& face, 
const QByteArray& baseFont, PdfId fontDes, const QMap<uint, QString>& 
usedGlyphs, const QMap<uint, uint>& glyphmap);
        PdfFont PDF_EncodeFormFont(const QByteArray& fontname, ScFace& face,  
const QByteArray& baseFont, const QByteArray& subtype, PdfId fontDes);


_______________________________________________
scribus-commit mailing list
[email protected]
http://lists.scribus.net/mailman/listinfo/scribus-commit

Reply via email to