r24464 by jghali - PDF: subset CID-keyed OpenType fonts as OpenType with the help of Harfbuzz when possible

scribus-commit scribus-commit at lists.scribus.net
Wed Feb 10 21:54:56 UTC 2021


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);




More information about the scribus-commit mailing list