/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <cstdio>
#include <cstring>
#include <assert.h>

#include "fontsubset.hxx"

#include <vcl/strhelper.hxx>

//#define IGNORE_HINTS

typedef unsigned char U8;
typedef unsigned short U16;
typedef long long S64;

typedef sal_Int32 GlyphWidth;

typedef double RealType;
typedef RealType ValType;
#include <vector>
typedef std::vector<ValType> ValVector;

// ====================================================================

static const char* pStringIds[] = {
/*0*/	".notdef",		"space",			"exclam",			"quotedbl",
	"numbersign",		"dollar",			"percent",			"ampersand",
	"quoteright",		"parenleft",		"parenright",		"asterisk",
	"plus",				"comma",			"hyphen",			"period",
/*16*/	"slash",		"zero",				"one",				"two",
	"three",			"four",				"five",				"six",
	"seven",			"eight",			"nine",				"colon",
	"semicolon",		"less",				"equal",			"greater",
/*32*/	"question",		"at",				"A",				"B",
	"C",				"D",				"E",				"F",
	"G",				"H",				"I",				"J",
	"K",				"L",				"M",				"N",
/*48*/	"O",			"P",				"Q",				"R",
	"S",				"T",				"U",				"V",
	"W",				"X",				"Y",				"Z",
	"bracketleft",		"backslash",		"bracketright",		"asciicircum",
/*64*/	"underscore",	"quoteleft",		"a",				"b",
	"c",				"d",				"e",				"f",
	"g",				"h",				"i",				"j",
	"k",				"l",				"m",				"n",
/*80*/	"o",			"p",				"q",				"r",
	"s",				"t",				"u",				"v",
	"w",				"x",				"y",				"z",
	"braceleft",		"bar",				"braceright",		"asciitilde",
/*96*/	"exclamdown",	"cent",				"sterlin",			"fraction",
	"yen",				"florin",			"section",			"currency",
	"quotesingle",		"quotedblleft",		"guillemotleft",	"guilsinglleft",
	"guilsinglright",	"fi",				"fl",				"endash",
/*112*/	"dagger",		"daggerdbl",		"periodcentered",	"paragraph",
	"bullet",			"quotesinglbase",	"quotedblbase",		"quotedblright",
	"guillemotright",	"ellipsis",			"perthousand",		"questiondown",
	"grave",			"acute",			"circumflex",		"tilde",
/*128*/	"macron",		"breve",			"dotaccent",		"dieresis",
	"ring",				"cedilla",			"hungarumlaut",		"ogonek",
	"caron",			"endash",			"AE",				"ordfeminine",
	"Lslash",			"Oslash",			"OE",				"ordmasculine",
/*144*/	"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",
/*176*/	"Atilde",		"Ccedilla",			"Eacute",			"Ecircumflex",
	"Edieresis",		"Egrave",			"Iacute",			"Icircumflex",
	"Idieresis",		"Igrave",			"Ntilde",			"Oacute",
	"Ocircumflex",		"Odieresis",		"Ograve",			"Otilde",
/*192*/	"Scaron",		"Uacute",			"Ucircumflex",		"Udieresis",
	"Ugrave",			"Yacute",			"Ydieresis",		"Zcaron",
	"aacute",			"acircumflex",		"adieresis",		"agrave",
	"aring",			"atilde",			"ccedilla",			"eacute",
/*208*/	"ecircumflex",	"edieresis",		"egrave",			"iacute",
	"icircumflex",		"idieresis",		"igrave",			"ntilde",
	"oacute",			"ocircumflex",		"odieresis",		"ograve",
	"otilde",			"scaron",			"uacute",			"ucircumflex",
/*224*/	"udieresis",	"ugrave",			"yacute",			"ydieresis",
	"zcaron",			"exclamsmall",		"Hungarumlautsmall","dollaroldstyle",
	"dollarsuperior",	"ampersandsmall",	"Acutesmall",		"parenleftsuperior",
	"parenrightsuperior","twodotenleader",	"onedotenleader",	"zerooldstyle",
/*240*/	"oneoldstyle",	"twooldstyle",		"threeoldstyle",	"fouroldstyle",
	"fiveoldstyle",		"sixoldstyle",		"sevenoldstyle",	"eightoldstyle",
	"nineoldstile",		"commasuperior",	"threequartersemdash","periodsuperior",
	"questionsmall",	"asuperior",		"bsuperior",		"centsuperior",
/*256*/	"dsuperior",	"esuperior",		"isuperior",		"lsuperior",
	"msuperior",		"nsuperior",		"osuperior",		"rsuperior",
	"ssuperior",		"tsuperior",		"ff",				"ffi",
	"ffl",				"parenleftinferior","parenrightinferior","Circumflexsmall",
/*272*/	"hyphensuperior","Gravesmall",		"Asmall",			"Bsmall",
	"Csmall",			"Dsmall",			"Esmall",			"Fsmall",
	"Gsmall",			"Hsmall",			"Ismall",			"Jsmall",
	"Ksmall",			"Lsmall",			"Msmall",			"Nsmall",
/*288*/	"Osmall",		"Psmall",			"Qsmall",			"Rsmall",
	"Ssmall",			"Tsmall",			"Usmall",			"Vsmall",
	"Wsmall",			"Xsmall",			"Ysmall",			"Zsmall",
	"colonmonetary",	"onefitted",		"rupia",			"Tildesmall",
/*304*/	"exclamdownsmall","centoldstyle",	"Lslashsmall",		"Scaronsmall",
	"Zcaronsmall",		"Dieresissmall",	"Brevesmall",		"Caronsmall",
	"Dotaccentsmall",	"Macronsmall",		"figuredash",		"hypheninferior",
	"Ogoneksmall",		"Ringsmall",		"Cedillasmall",		"questiondownsmall",
/*320*/	"oneeight",		"threeeights",		"fiveeights",		"seveneights",
	"onethird",			"twothirds",		"zerosuperior",		"foursuperior",
	"fivesuperior",		"sixsuperior",		"sevensuperior",	"eightsuperior",
	"ninesuperior",		"zeroinferior",		"oneinferior",		"twoinferior",
/*336*/	"threeinferior","fourinferior",		"fiveinferior",		"sixinferior",
	"seveninferior",	"eightinferior",	"nineinferior",		"centinferior",
	"dollarinferior",	"periodinferior",	"commainferior",	"Agravesmall",
	"Aacutesmall",		"Acircumflexsmall",	"Atildesmall",		"Adieresissmall",
/*352*/	"Aringsmall",	"AEsmall",			"Ccedillasmall",	"Egravesmall",
	"Eacutesmall",		"Ecircumflexsmall",	"Edieresissmall",	"Igravesmall",
	"Iacutesmall",		"Icircumflexsmall",	"Idieresissmall",	"Ethsmall",
	"Ntildesmall",		"Ogravesmall",		"Oacutesmall",		"Ocircumflexsmall",
/*368*/	"Otildesmall",	"Odieressissmall",	"OEsmall",			"Oslashsmall",
	"Ugravesmall",		"Uacutesmall",		"Ucircumflexsmall",	"Udieresissmall",
	"Yacutesmall",		"Thornsmall",		"Ydieresissmall",	"001.000",
	"001.001",			"001.002",			"001.003",			"Black",
/*384*/	"Bold",			"Book",				"Light",			"Medium",
	"Regular",			"Roman",			"Semibold"
};

// --------------------------------------------------------------------

#if 0 // TODO: use them
static const char* pStdEncNames[] = {
	"ISOAdobe",	"Expert",	"ExpertSubSet"
};
#endif

// --------------------------------------------------------------------

/** TOP DICT keywords (also covers PRIV DICT keywords)
 *
 * Refer to the CFF Specification, tables 9 and 23.
 *
 * This array is indexed by operand.
 *
 * The first character tells the type of operand ('s': SID, 'b': boolean etc.).
 */
static const char* pDictOps[] = {
	"sVersion",			"sNotice",				"sFullName",		"sFamilyName",
	"sWeight",			"aFontBBox",			"dBlueValues",		"dOtherBlues",
	"dFamilyBlues",		"dFamilyOtherBlues",	"nStdHW",			"nStdVW",
	"xESC",				"nUniqueID",			"aXUID",			"nCharset",
	"nEncoding",		"nCharStrings",			"PPrivate",			"nSubrs",
	"nDefaultWidthX",	"nNominalWidthX",		NULL,				NULL,
	NULL,				NULL,					NULL,				NULL,
	"shortint",			"longint",				"BCD",				NULL
};

// --------------------------------------------------------------------

/** TOP DICT escapes (also covers PRIV DICT escapes)
 *
 * Refer to the CFF Specification, tables 9 and 23.
 *
 * These operators come after the escape operator (no. 12).
 *
 * This array is indexed by operand.
 *
 * The first character tells the type of operand ('s': SID, 'b': boolean etc.).
 */
static const char* pDictEscs[] = {
	"sCopyright",			"bIsFixedPitch",	"nItalicAngle",		"nUnderlinePosition",
	"nUnderlineThickness",	"nPaintType",		"tCharstringType",	"aFontMatrix",
	"nStrokeWidth",			"nBlueScale",		"nBlueShift",		"nBlueFuzz",
	"dStemSnapH",			"dStemSnapV",		"bForceBold",		NULL,
	NULL,					"nLanguageGroup",	"nExpansionFactor",	"nInitialRandomSeed",
	"nSyntheticBase",		"sPostScript",		"sBaseFontName",	"dBaseFontBlend",
	NULL,					NULL,				NULL,				NULL,
	NULL,					NULL,				"rROS",				"nCIDFontVersion",
	"nCIDFontRevision",		"nCIDFontType",		"nCIDCount",		"nUIDBase",
	"nFDArray",				"nFDSelect",		"sFontName"
};

// --------------------------------------------------------------------

static const char* pType1Ops[] = {
	NULL,				"2hstem",			NULL,				"2vstem",
	"1vmoveto",			"Arlineto",			"1hlineto",			"1vlineto",
	"Crrcurveto",		"0closepath",		"Lcallsubr",		"0return",
	"xT1ESC",			"2hsbw",			"0endchar",			NULL,
	NULL,				NULL,				NULL,				NULL,
	NULL,				"2rmoveto",			"1hmoveto",			NULL,
	NULL,				NULL,				NULL,				NULL,
	NULL,				NULL,				"4vhcurveto",		"4hvcurveto"
};

// --------------------------------------------------------------------

static const char* pT1EscOps[] = {
	"0dotsection",		"6vstem3",			"6hstem3",			NULL,
	NULL,				NULL,				"5seac",			"4sbw",
	NULL,				"1abs",				"2add",				"2sub",
	"2div",				NULL,				NULL,				NULL,
	"Gcallothersubr",	"1pop",				NULL,				NULL,
	NULL,				NULL,				NULL,				NULL,
	NULL,				NULL,				NULL,				NULL,
	NULL,				NULL,				NULL,				NULL,
	NULL,				"2setcurrentpoint"
};

// --------------------------------------------------------------------

struct TYPE1OP
{
	enum OPS
	{
		HSTEM=1,		VSTEM=3,		VMOVETO=4,		RLINETO=5,
		HLINETO=6,		VLINETO=7,		RCURVETO=8,		CLOSEPATH=9,
		CALLSUBR=10,	RETURN=11,		T1ESC=12,		HSBW=13,
		ENDCHAR=14,		RMOVETO=21,		HMOVETO=22,		VHCURVETO=30,
		HVCURVETO=31
	};

	enum ESCS
	{
		DOTSECTION=0,	VSTEM3=1,			HSTEM3=2,	SEAC=6,
		SBW=7,			ABS=9,				ADD=10,		SUB=11,
		DIV=12,			CALLOTHERSUBR=16,	POP=17,		SETCURRENTPOINT=33
	};
};

// --------------------------------------------------------------------

static const char* pType2Ops[] = {
	NULL,			"hhstem",		NULL,			"vvstem",
	"mvmoveto",		"Arlineto",		"Ehlineto",		"Evlineto",
	"Crrcurveto",	NULL,			"Lcallsubr",	"Xreturn",
	"xT2ESC",		NULL,			"eendchar",		NULL,
	NULL,			NULL,			"Hhstemhm",		"Khintmask",
	"Kcntrmask",	"Mrmoveto",		"mhmoveto",		"Vvstemhm",
	".rcurveline",	".rlinecurve",	".vvcurveto",	".hhcurveto",
	".shortint",	"Gcallgsubr",	".vhcurveto",	".hvcurveto"
};

// --------------------------------------------------------------------

static const char* pT2EscOps[] = {
	NULL,		NULL,		NULL,		"2and",
	"2or",		"1not",		NULL,		NULL,
	NULL,		"1abs",		"2add",		"2sub",
	"2div",		NULL,		"1neg",		"2eq",
	NULL,		NULL,		"1drop",	NULL,
	"1put",		"1get",		"4ifelse",	"0random",
	"2mul",		NULL,		"1sqrt",	"1dup",
	"2exch",	"Iindex",	"Rroll",	NULL,
	NULL,		NULL,		"7hflex",	"Fflex",
	"9hflex1",	"fflex1"
};

// --------------------------------------------------------------------

struct TYPE2OP
{
	enum OPS
	{
		HSTEM=1,		VSTEM=3,		VMOVETO=4,		RLINETO=5,
		HLINETO=6,		VLINETO=7,		RCURVETO=8,		CALLSUBR=10,
		RETURN=11,		T2ESC=12,		ENDCHAR=14,		HSTEMHM=18,
		HINTMASK=19,	CNTRMASK=20,	RMOVETO=21,		HMOVETO=22,
		VSTEMHM=23,		RCURVELINE=24,	RLINECURVE=25,	VVCURVETO=26,
		HHCURVETO=27,	SHORTINT=28,	CALLGSUBR=29,	VHCURVETO=30,
		HVCURVETO=31
	};

	enum ESCS
	{
		AND=3,		OR=4,		NOT=5,		ABS=9,
		ADD=10,		SUB=11,		DIV=12,		NEG=14,
		EQ=15,		DROP=18,	PUT=20,		GET=21,
		IFELSE=22,	RANDOM=23,	MUL=24,		SQRT=26,
		DUP=27,		EXCH=28,	INDEX=29,	ROLL=30,
		HFLEX=34,	FLEX=35,	HFLEX1=36,	FLEX1=37
	};
};

// ====================================================================

/** Data layout of a CFF FontSet
 *
 * Refer to the CFF specification, chapter 2
 */
struct CffGlobal
{
	explicit CffGlobal();

	// Offset of the Name INDEX inside the CFF data
	int		mnNameIdxBase;
	// Number of objects stored in the Name INDEX
	int		mnNameIdxCount;
	int		mnStringIdxBase;
	int		mnStringIdxCount;
	bool 	mbCIDFont;
	int		mnCharStrBase;
	int		mnCharStrCount;
	int		mnEncodingBase;
	int		mnCharsetBase;
	int		mnGlobalSubrBase;
	int		mnGlobalSubrCount;
	int		mnGlobalSubrBias;
	int		mnFDSelectBase;
	int		mnFontDictBase;
	int		mnFDAryCount;

	ValVector	maFontBBox;
	ValVector	maFontMatrix;

	int		mnFontNameSID;
	int		mnFullNameSID;
	int		mnFamilyNameSID;
};

// ====================================================================

struct CffLocal
{
	explicit CffLocal();

	int		mnPrivDictBase;
	int		mnPrivDictSize;
	int		mnLocalSubrOffs;
	int		mnLocalSubrBase;
	int		mnLocalSubrCount;
	int		mnLocalSubrBias;

	ValType	maNominalWidth;
	ValType maDefaultWidth;

	// ATM hinting related values
	ValType		maStemStdHW;
	ValType		maStemStdVW;
	ValVector	maStemSnapH;
	ValVector	maStemSnapV;
	ValVector	maBlueValues;
	ValVector	maOtherBlues;
	ValVector	maFamilyBlues;
	ValVector	maFamilyOtherBlues;
	RealType	mfBlueScale;
	RealType	mfBlueShift;
	RealType	mfBlueFuzz;
	RealType	mfExpFactor;
	int			mnLangGroup;
	bool		mbForceBold;
};

// ====================================================================

class CffSubsetterContext
:	private CffGlobal
{
public:
	// Refer to Type 2 charstring format appendix B, "Type 2 Charstring Implementation Limits"
	static const int NMAXSTACK = 48;	// argument stack
	static const int NMAXHINTS = 2*96;	// number of stem hints (H/V total)
	static const int NMAXTRANS = 32;	// TransientArray elements
public:
	explicit CffSubsetterContext( const U8* pBasePtr, int nBaseLen);
	virtual	~CffSubsetterContext( void);

	// Begin parsing the CFF data
	void	initialCffRead( void);
	bool	emitAsType1( class Type1Emitter&,
				const sal_GlyphId* pGlyphIds, const U8* pEncoding,
				GlyphWidth* pGlyphWidths, int nGlyphCount, FontSubsetInfo& );

	// used by charstring converter
	void	setCharStringType( int);
	void	fakeLocalSubrCount( int nLocalSubrs ) { maCffLocal[0].mnLocalSubrCount=nLocalSubrs;}
protected:
	int		convert2Type1Ops( CffLocal*, const U8* pType2Ops, int nType2Len, U8* pType1Ops);
private:
	void	convertOneTypeOp( void);
	void	convertOneTypeEsc( void);
	void	callType2Subr( bool bGlobal, int nSubrNumber);
	long	getReadOfs( void) const { return (long)(mpReadPtr - mpBasePtr);}

	// First byte of CFF font data
	const U8* mpBasePtr;
	// Last byte of CFF font data
	const U8* mpBaseEnd;

	// Moving cursors inside CFF font data
	const U8* mpReadPtr;
	const U8* mpReadEnd;

	U8*		mpWritePtr;
	bool	mbSawError;
	bool	mbNeedClose;
	bool	mbIgnoreHints;
	long	mnCntrMask;

private:
	/** Prepare to access an element inside a CFF/CID index table
	 *
	 * nIndexBase: offset of the INDEX structure inside the CFF font data.
	 * nDataIndex: offset of the element inside the INDEX structure.
	 *
	 * Sets mpReadPtr to the beginning of the element and mpReadEnd to the end of the element.
	 *
	 * Returns the size of the element, or -1 if the data is not valid (e.g. indices are too big).
	 */
	int		seekIndexData( int nIndexBase, int nDataIndex);
	/** Seek to the end of an INDEX structure
	 *
	 * nIndexBase: offset of the INDEX structure inside the CFF font data.
	 *
	 * Sets mpReadPtr to the first byte after the indicated structure.
	 */
	void	seekIndexEnd( int nIndexBase);

private:
	const char**	mpCharStringOps;
	const char**	mpCharStringEscs;

	std::vector<CffLocal>	maCffLocal;
	CffLocal*	mpCffLocal;

	void		readDictOp( void);
	RealType	readRealVal( void);
	const char*	getString( int nStringID);
	int			getFDSelect( int nGlyphIndex) const;
	int			getGlyphSID( int nGlyphIndex) const;
	const char* getGlyphName( int nGlyphIndex);

	/** Decode an integer DICT Data Operand and push it.
	 *
	 * Refer to the CFF Specification, table 3.
	 *
	 * Advances mpReadPtr.
	 */
	void	read2push( void);
	void	pop2write( void);
	void	writeType1Val( ValType);
	void	writeTypeOp( int nTypeOp);
	void	writeTypeEsc( int nTypeOp);
	void	writeCurveTo( int nStackPos, int nIX1, int nIY1, int nIX2, int nIY2, int nIX3, int nIY3);
	void	pop2MultiWrite( int nArgsPerTypo, int nTypeOp, int nTypeXor=0);
	void	popAll2Write( int nTypeOp);

public: // TODO: is public really needed?
	// accessing the value stack
	// TODO: add more checks
	void	push( ValType nVal) { mnValStack[ mnStackIdx++] = nVal;}
	ValType	popVal( void) { return ((mnStackIdx>0) ? mnValStack[ --mnStackIdx] : 0);}
	ValType peekVal( void) const { return ((mnStackIdx>0) ? mnValStack[ mnStackIdx-1] : 0);}
	ValType getVal( int nIndex) const { return mnValStack[ nIndex];}
	int		popInt( void);
	int		peekInt( void) const;
	int		getInt( int nIndex) const;
	int		size( void) const { return mnStackIdx;}
	bool	empty( void) const { return !mnStackIdx;}
	void	clear( void) { mnStackIdx = 0;}

	// accessing the charstring hints
	void	addHints( bool bVerticalHints);
	int		getHorzHintCount( void) const { return (mnHorzHintSize/2);}
	int		getVertHintCount( void) const { return (mnHintSize-mnHorzHintSize)/2;}
	void	getHintPair( int nIndex, ValType* nMin, ValType* nEnd) const;

	// accessing other charstring specifics
	bool	hasCharWidth( void) const { return (maCharWidth > 0);}
	ValType	getCharWidth( void) const { return maCharWidth;}
	void	setNominalWidth( ValType aWidth) { mpCffLocal->maNominalWidth = aWidth;}
	void	setDefaultWidth( ValType aWidth) { mpCffLocal->maDefaultWidth = aWidth;}
	void	updateWidth( bool bUseFirstVal);

private:
	// typeop exceution context

	// Count of mnValStack elements 
	int	mnStackIdx;
	// Stack for holding CFF DICT operands
	ValType	mnValStack[ NMAXSTACK+4];
	// Transient array for Type 2 storage operators (PUT, GET)
	ValType	mnTransVals[ NMAXTRANS];

	int	mnHintSize;
	int	mnHorzHintSize;
	ValType	mnHintStack[ NMAXHINTS];

	ValType	maCharWidth;
};

// --------------------------------------------------------------------

CffSubsetterContext::CffSubsetterContext( const U8* pBasePtr, int nBaseLen)
:	mpBasePtr( pBasePtr)
,	mpBaseEnd( pBasePtr+nBaseLen)
,	mnStackIdx(0)
,	mnHintSize(0)
,	mnHorzHintSize(0)
,	maCharWidth(-1)
{
//	setCharStringType( 1);
	maCffLocal.resize(1);
	mpCffLocal = &maCffLocal[0];
}

// --------------------------------------------------------------------

CffSubsetterContext::~CffSubsetterContext( void)
{
}

// --------------------------------------------------------------------

inline int CffSubsetterContext::popInt( void)
{
	const ValType aVal = popVal();
	const int nInt = static_cast<int>(aVal);
	assert( nInt == aVal);
	return nInt;
}

// --------------------------------------------------------------------

inline int CffSubsetterContext::peekInt( void) const
{
	const ValType aVal = peekVal();
	const int nInt = static_cast<int>(aVal);
	assert( nInt == aVal);
	return nInt;
}

// --------------------------------------------------------------------

inline int CffSubsetterContext::getInt( int nIndex) const
{
	const ValType aVal = getVal( nIndex);
	const int nInt = static_cast<int>(aVal);
	assert( nInt == aVal);
	return nInt;
}

// --------------------------------------------------------------------

inline void CffSubsetterContext::updateWidth( bool bUseFirstVal)
{
#if 1 // TODO: is this still needed?
	// the first value is not a hint but the charwidth
	if( hasCharWidth())
		return;
#endif
	if( bUseFirstVal) {
		maCharWidth = mpCffLocal->maNominalWidth + mnValStack[0];
		// remove bottom stack entry
		--mnStackIdx;
		for( int i = 0; i < mnStackIdx; ++i)
			mnValStack[ i] = mnValStack[ i+1];
	} else {
		maCharWidth = mpCffLocal->maDefaultWidth;
	}
}

// --------------------------------------------------------------------

void CffSubsetterContext::addHints( bool bVerticalHints)
{
	// the first charstring value may a charwidth instead of a charwidth
	updateWidth( (mnStackIdx & 1) != 0);
	// return early (e.g. no implicit hints for hintmask)
	if( !mnStackIdx)
		return;

	// copy the remaining values to the hint arrays
	// assert( (mnStackIdx & 1) == 0); // depends on called subrs
	if( mnStackIdx & 1) --mnStackIdx;//#######
	// TODO: if( !bSubr) assert( mnStackIdx >= 2);

	assert( (mnHintSize + mnStackIdx) <= 2*NMAXHINTS);

#ifdef IGNORE_HINTS
	mnHintSize += mnStackIdx;
#else
	ValType nHintOfs = 0;
	for( int i = 0; i < mnStackIdx; ++i) {
		nHintOfs += mnValStack[ i ];
		mnHintStack[ mnHintSize++] = nHintOfs;
	}
#endif // IGNORE_HINTS
	if( !bVerticalHints)
		mnHorzHintSize = mnHintSize;

	// clear all values from the stack
	mnStackIdx = 0;
}

// --------------------------------------------------------------------

void CffSubsetterContext::getHintPair( int nIndex, ValType* pMin, ValType* pEnd) const
{
	nIndex *= 2;
	assert( nIndex < mnHintSize);
	assert( nIndex >= 0);
	const ValType* pHint = &mnHintStack[ nIndex ];
	*pMin = pHint[0];
	*pEnd = pHint[1];
}

// --------------------------------------------------------------------

void CffSubsetterContext::setCharStringType( int nVal)
{
	switch( nVal) {
		case 1: mpCharStringOps=pType1Ops; mpCharStringEscs=pT1EscOps; break;
		case 2: mpCharStringOps=pType2Ops; mpCharStringEscs=pT2EscOps; break;
		default: fprintf( stderr, "Unknown CharstringType=%d\n",nVal); break;
	}
}

// --------------------------------------------------------------------

/** Read DICT operator at mpReadPtr.
 *
 * Sets the attributes of CffSubsetterContext::mpCffLocal
 */
void CffSubsetterContext::readDictOp( void)
{
	ValType nVal = 0;
	int nInt = 0;
	const U8 c = *mpReadPtr;
	if( c <= 21 ) { // we are looking at an operator
		int nOpId = *(mpReadPtr++);
		const char* pCmdName;
		if( nOpId != 12)
			pCmdName = pDictOps[ nOpId];
		else { // escape: the operator is indicated in the following byte
			const U8 nExtId = *(mpReadPtr++);
			pCmdName = pDictEscs[ nExtId];
			nOpId = 900 + nExtId;
		}

		//TODO: if( nStackIdx > 0)
		// The first byte of pCmdName indicates the type of operand
		switch( *pCmdName) {
		default: fprintf( stderr, "unsupported DictOp.type=\'%c\'\n", *pCmdName); break;
		case 'b':	// bool
			nInt = popInt();
			switch( nOpId) {
			case 915: mpCffLocal->mbForceBold = nInt; break;    // "ForceBold"
			default: break; // TODO: handle more boolean dictops?
			}
			break;
		case 'n':	// dict-op number
			nVal = popVal();
			nInt = static_cast<int>(nVal);
			switch( nOpId) {
			case  10: mpCffLocal->maStemStdHW = nVal; break; 	// "StdHW"
			case  11: mpCffLocal->maStemStdVW = nVal; break; 	// "StdVW"
			case  15: mnCharsetBase = nInt; break;				// "charset"
			case  16: mnEncodingBase = nInt; break;				// "nEncoding"
			case  17: mnCharStrBase = nInt; break;				// "nCharStrings"
			case  19: mpCffLocal->mnLocalSubrOffs = nInt; break;// "nSubrs"
			case  20: setDefaultWidth( nVal ); break;			// "defaultWidthX"
			case  21: setNominalWidth( nVal ); break;			// "nominalWidthX"
			case 909: mpCffLocal->mfBlueScale = nVal; break; 	// "BlueScale"
			case 910: mpCffLocal->mfBlueShift = nVal; break; 	// "BlueShift"
			case 911: mpCffLocal->mfBlueFuzz = nVal; break; 	// "BlueFuzz"
			case 912: mpCffLocal->mfExpFactor = nVal; break;	// "ExpansionFactor"
			case 917: mpCffLocal->mnLangGroup = nInt; break;	// "LanguageGroup"
			case 936: mnFontDictBase = nInt; break;				// "nFDArray"
			case 937: mnFDSelectBase = nInt; break;				// "nFDSelect"
			default: break; // TODO: handle more numeric dictops?
			}
			break;
		case 'a': {	// array
			switch( nOpId) {
			case   5: maFontBBox.clear(); break;     // "FontBBox"
			case 907: maFontMatrix.clear(); break; // "FontMatrix"
			default: break; // TODO: reset other arrays?
			}
			for( int i = 0; i < size(); ++i ) {
				nVal = getVal(i);
				switch( nOpId) {
				case   5: maFontBBox.push_back( nVal); break;     // "FontBBox"
				case 907: maFontMatrix.push_back( nVal); break; // "FontMatrix"
				default: break; // TODO: handle more array dictops?
				}
			}
			clear();
			} break;
		case 'd': {	// delta array
			nVal = 0;
			for( int i = 0; i < size(); ++i ) {
				nVal += getVal(i);
				switch( nOpId) {
				case   6: mpCffLocal->maBlueValues.push_back( nVal); break;		// "BlueValues"
				case   7: mpCffLocal->maOtherBlues.push_back( nVal); break;		// "OtherBlues"
				case   8: mpCffLocal->maFamilyBlues.push_back( nVal); break;	// "FamilyBlues"
				case   9: mpCffLocal->maFamilyOtherBlues.push_back( nVal); break;// "FamilyOtherBlues"
				case 912: mpCffLocal->maStemSnapH.push_back( nVal); break;		// "StemSnapH"
				case 913: mpCffLocal->maStemSnapV.push_back( nVal); break;		// "StemSnapV"
				default: break; // TODO: handle more delta-array dictops?
				}
			}
			clear();
			} break;
		case 's':	// stringid (SID)
			nInt = popInt();
			switch( nOpId ) {
			case   2: mnFullNameSID = nInt; break;		// "FullName"
			case   3: mnFamilyNameSID = nInt; break;	// "FamilyName"
			case 938: mnFontNameSID = nInt; break;		// "FontName"
			default: break; // TODO: handle more string dictops?
			}
			break;
		case 'P': 	// private dict
			mpCffLocal->mnPrivDictBase = popInt();
			mpCffLocal->mnPrivDictSize = popInt();
			break;
		case 'r': {	// ROS operands
			int nSid1 = popInt();
			int nSid2 = popInt();
			(void)nSid1; // TODO: use
			(void)nSid2; // TODO: use
			nVal = popVal();
			mbCIDFont = true;
			} break;
		case 't':	// CharstringType
			nInt = popInt();
			setCharStringType( nInt );
			break;
		}

		return; 
	} else if( (c >= 32) || (c == 28) ) {
//		--mpReadPtr;
		read2push();
	} else if( c == 29 ) { // we are looking at a 32-bit operand
		++mpReadPtr;			// skip 29
		int nS32 = mpReadPtr[0] << 24;
		nS32 += mpReadPtr[1] << 16;
		nS32 += mpReadPtr[2] << 8;
		nS32 += mpReadPtr[3] << 0;
		if( (sizeof(nS32) != 4) && (nS32 & (1<<31)))
			nS32 |= (~0U) << 31;	// assuming 2s complement
		mpReadPtr += 4;
		nVal = static_cast<ValType>(nS32);
		push( nVal );
	} else if( c == 30) { // we are looking at a real number operand
		++mpReadPtr; // skip 30
		const RealType fReal = readRealVal();
		// push value onto stack
		nVal = fReal;
		push( nVal);
	}
}

// --------------------------------------------------------------------

void CffSubsetterContext::read2push()
{
	ValType aVal = 0;

	const U8*& p = mpReadPtr;
	const U8 c = *p;
	if( c == 28 ) {			// -32767..+32767
		short nS16 = (p[1] << 8) + p[2];
		if( (sizeof(nS16) != 2) && (nS16 & (1<<15)))
			nS16 |= (~0U) << 15;	// assuming 2s complement
		aVal = nS16;
		p += 3;
	} else if( c <= 246 ) {		// -107..+107
		aVal = static_cast<ValType>(p[0] - 139);
		p += 1;
	} else if( c <= 250 ) {		// +108..+1131
		aVal = static_cast<ValType>(((p[0] << 8) + p[1]) - 63124);
		p += 2;
	} else if( c <= 254 ) {		// -108..-1131
		aVal = static_cast<ValType>(64148 - ((p[0] << 8) + p[1]));
		p += 2;
	} else /*if( c == 255)*/ {	// Fixed16.16
		int nS32 = (p[1] << 24) + (p[2] << 16) + (p[3] << 8) + p[4];
		if( (sizeof(nS32) != 2) && (nS32 & (1<<31)))
			nS32 |= (~0U) << 31;	// assuming 2s complement
		aVal = static_cast<ValType>(nS32 * (1.0 / 0x10000));
		p += 5;
	}

	push( aVal);
}

// --------------------------------------------------------------------

void CffSubsetterContext::writeType1Val( ValType aVal)
{
	U8* pOut = mpWritePtr;

	int nInt = static_cast<int>(aVal);
	static const int nOutCharstrType = 1;
	if( (nInt != aVal) && (nOutCharstrType == 2)) {
		// numtype==255 means int32 for Type1, but 16.16 for Type2 charstrings!!!
		*(pOut++) = 255;                			// Fixed 16.16
		*(pOut++) = static_cast<U8>(nInt >> 8);
		*(pOut++) = static_cast<U8>(nInt);
		nInt = static_cast<int>(aVal * 0x10000) & 0xFFFF;
		*(pOut++) = static_cast<U8>(nInt >> 8);
		*(pOut++) = static_cast<U8>(nInt);
	} else if( (nInt >= -107) && (nInt <= +107)) {
		*(pOut++) = static_cast<U8>(nInt + 139);	// -107..+107
	} else if( (nInt >= -1131) && (nInt <= +1131)) {
		if( nInt >= 0)
			nInt += 63124;							// +108..+1131
		else
			nInt = 64148 - nInt;					// -108..-1131
		*(pOut++) = static_cast<U8>(nInt >> 8);
		*(pOut++) = static_cast<U8>(nInt);
	} else if( nOutCharstrType == 1) {
		// numtype==255 means int32 for Type1, but 16.16 for Type2 charstrings!!!
		*(pOut++) = 255;
        *(pOut++) = static_cast<U8>(nInt >> 24);
        *(pOut++) = static_cast<U8>(nInt >> 16);
        *(pOut++) = static_cast<U8>(nInt >> 8);
        *(pOut++) = static_cast<U8>(nInt);
	}

	mpWritePtr = pOut;
}

// --------------------------------------------------------------------

inline void CffSubsetterContext::pop2write( void)
{
	const ValType aVal = popVal();
	writeType1Val( aVal);
}

// --------------------------------------------------------------------

inline void CffSubsetterContext::writeTypeOp( int nTypeOp)
{
	*(mpWritePtr++) = static_cast<U8>(nTypeOp);
}

// --------------------------------------------------------------------

inline void CffSubsetterContext::writeTypeEsc( int nTypeEsc)
{
	*(mpWritePtr++) = TYPE1OP::T1ESC;
	*(mpWritePtr++) = static_cast<U8>(nTypeEsc);
}

// --------------------------------------------------------------------

void CffSubsetterContext::pop2MultiWrite( int nArgsPerTypo, int nTypeOp, int nTypeXor)
{
	for( int i = 0; i < mnStackIdx;) {
		for( int j = 0; j < nArgsPerTypo; ++j) {
			const ValType aVal = mnValStack[i+j];
			writeType1Val( aVal);
		}
		i += nArgsPerTypo;
		writeTypeOp( nTypeOp);
		nTypeOp ^= nTypeXor;	// for toggling vlineto/hlineto
	}
	clear();
}

// --------------------------------------------------------------------

void CffSubsetterContext::popAll2Write( int nTypeOp)
{
	// pop in reverse order, then write
	for( int i = 0; i < mnStackIdx; ++i) {
		const ValType aVal = mnValStack[i];
		writeType1Val( aVal);
	}
	clear();
	writeTypeOp( nTypeOp);
}

// --------------------------------------------------------------------

void CffSubsetterContext::writeCurveTo( int nStackPos,
	int nIX1, int nIY1, int nIX2, int nIY2, int nIX3, int nIY3)
{
	// get the values from the stack
	const ValType nDX1 = nIX1 ? mnValStack[ nStackPos+nIX1 ] : 0;
	const ValType nDY1 = nIY1 ? mnValStack[ nStackPos+nIY1 ] : 0;
	const ValType nDX2 = nIX2 ? mnValStack[ nStackPos+nIX2 ] : 0;
	const ValType nDY2 = nIY2 ? mnValStack[ nStackPos+nIY2 ] : 0;
	const ValType nDX3 = nIX3 ? mnValStack[ nStackPos+nIX3 ] : 0;
	const ValType nDY3 = nIY3 ? mnValStack[ nStackPos+nIY3 ] : 0;

	// emit the curveto operator and operands
	// TODO: determine the most efficient curveto operator
	// TODO: depending on type1op or type2op target
	writeType1Val( nDX1 );
	writeType1Val( nDY1 );
	writeType1Val( nDX2 );
	writeType1Val( nDY2 );
	writeType1Val( nDX3 );
	writeType1Val( nDY3 );
	writeTypeOp( TYPE1OP::RCURVETO );
}

// --------------------------------------------------------------------

void CffSubsetterContext::convertOneTypeOp( void)
{
	const int nType2Op = *(mpReadPtr++);

	int i, nInt; // prevent WAE for declarations inside switch cases
	// convert each T2op
	switch( nType2Op) {
	case TYPE2OP::T2ESC:
		convertOneTypeEsc();
		break;
	case TYPE2OP::HSTEM:
	case TYPE2OP::VSTEM:
		addHints( nType2Op == TYPE2OP::VSTEM );
#ifndef IGNORE_HINTS
		for( i = 0; i < mnHintSize; i+=2 ) {
			writeType1Val( mnHintStack[i]);
			writeType1Val( mnHintStack[i+1] - mnHintStack[i]);
			writeTypeOp( nType2Op );
		}
#endif // IGNORE_HINTS
		break;
	case TYPE2OP::HSTEMHM:
	case TYPE2OP::VSTEMHM:
		addHints( nType2Op == TYPE2OP::VSTEMHM);
		break;
	case TYPE2OP::CNTRMASK:
		// TODO: replace cntrmask with vstem3/hstem3
		addHints( true);
#ifdef IGNORE_HINTS
		mpReadPtr += (mnHintSize + 15) / 16;
		mbIgnoreHints = true;
#else
		{
		U8 nMaskBit = 0;
		U8 nMaskByte = 0;
		for( i = 0; i < mnHintSize; i+=2, nMaskBit>>=1) {
			if( !nMaskBit) {
				nMaskByte = *(mpReadPtr++);
				nMaskBit = 0x80;
			}
			if( !(nMaskByte & nMaskBit))
				continue;
			if( i >= 8*(int)sizeof(mnCntrMask))
				mbIgnoreHints = true;
			if( mbIgnoreHints)
				continue;
			mnCntrMask |= (1U << i);
		}
		}
#endif
		break;
	case TYPE2OP::HINTMASK:
		addHints( true);
#ifdef IGNORE_HINTS
		mpReadPtr += (mnHintSize + 15) / 16;
#else
		{
		long nHintMask = 0;
		int nCntrBits[2] = {0,0};
		U8 nMaskBit = 0;
		U8 nMaskByte = 0;
		for( i = 0; i < mnHintSize; i+=2, nMaskBit>>=1) {
			if( !nMaskBit) {
				nMaskByte = *(mpReadPtr++);
				nMaskBit = 0x80;
			}
			if( !(nMaskByte & nMaskBit))
				continue;
			if( i >= 8*(int)sizeof(nHintMask))
				mbIgnoreHints = true;
			if( mbIgnoreHints)
				continue;
			nHintMask |= (1U << i);
			nCntrBits[ i < mnHorzHintSize] += (mnCntrMask >> i) & 1;
		}

		mbIgnoreHints |= (nCntrBits[0] && (nCntrBits[0] != 3));
		mbIgnoreHints |= (nCntrBits[1] && (nCntrBits[1] != 3));
		if( mbIgnoreHints)
			break;

		for( i = 0; i < mnHintSize; i+=2) {
			if( !(nHintMask & (1U << i)))
				continue;
			writeType1Val( mnHintStack[i]);
			writeType1Val( mnHintStack[i+1] - mnHintStack[i]);
			const bool bHorz = (i < mnHorzHintSize);
			if( !nCntrBits[ bHorz])
				writeTypeOp( bHorz ? TYPE1OP::HSTEM : TYPE1OP::VSTEM);
			else if( !--nCntrBits[ bHorz])
				writeTypeEsc( bHorz ? TYPE1OP::HSTEM3 : TYPE1OP::VSTEM3);
		}
		}
#endif
		break;
	case TYPE2OP::CALLSUBR:
	case TYPE2OP::CALLGSUBR:
		{
		nInt = popInt();
		const bool bGlobal = (nType2Op == TYPE2OP::CALLGSUBR);
		callType2Subr( bGlobal, nInt);
		}
		break;
	case TYPE2OP::RETURN:
		// TODO: check that we are in a subroutine
		return;
	case TYPE2OP::VMOVETO:
	case TYPE2OP::HMOVETO:
		if( mbNeedClose)
			writeTypeOp( TYPE1OP::CLOSEPATH);
		else
			updateWidth( size() > 1);
		mbNeedClose = true;
		pop2MultiWrite( 1, nType2Op);
		break;
	case TYPE2OP::VLINETO:
	case TYPE2OP::HLINETO:
		pop2MultiWrite( 1, nType2Op,
			TYPE1OP::VLINETO ^ TYPE1OP::HLINETO);
		break;
	case TYPE2OP::RMOVETO:
		// TODO: convert rmoveto to vlineto/hlineto if possible
		if( mbNeedClose)
			writeTypeOp( TYPE1OP::CLOSEPATH);
		else
			updateWidth( size() > 2);
		mbNeedClose = true;
		pop2MultiWrite( 2, nType2Op);
		break;
	case TYPE2OP::RLINETO:
		// TODO: convert rlineto to vlineto/hlineto if possible
		pop2MultiWrite( 2, nType2Op);
		break;
	case TYPE2OP::RCURVETO:
		// TODO: convert rcurveto to vh/hv/hh/vv-curveto if possible
		pop2MultiWrite( 6, nType2Op);
		break;
	case TYPE2OP::RCURVELINE:
		i = 0;
		while( (i += 6) <= mnStackIdx)
			writeCurveTo( i, -6, -5, -4, -3, -2, -1 );
		i -= 6;
		while( (i += 2) <= mnStackIdx) {
			writeType1Val( mnValStack[i-2]);
			writeType1Val( mnValStack[i-1]);
			writeTypeOp( TYPE2OP::RLINETO);
		}
		clear();
		break;
	case TYPE2OP::RLINECURVE:
		i = 0;
		while( (i += 2) <= mnStackIdx-6) {
			writeType1Val( mnValStack[i-2]);
			writeType1Val( mnValStack[i-1]);
			writeTypeOp( TYPE2OP::RLINETO);
		}
		i -= 2;
		while( (i += 6) <= mnStackIdx)
			writeCurveTo( i, -6, -5, -4, -3, -2, -1 );
		clear();
		break;
	case TYPE2OP::VHCURVETO:
	case TYPE2OP::HVCURVETO:
		{
		bool bVert = (nType2Op == TYPE2OP::VHCURVETO);
		i = 0;
		nInt = 0;
		if( mnStackIdx & 1 )
			nInt = static_cast<int>(mnValStack[ --mnStackIdx ]);
		while( (i += 4) <= mnStackIdx) {
			// TODO: use writeCurveTo()
			if( bVert ) writeType1Val( 0 );
			writeType1Val( mnValStack[i-4] );
			if( !bVert ) writeType1Val( 0);
			writeType1Val( mnValStack[i-3] );
			writeType1Val( mnValStack[i-2] );
			if( !bVert ) writeType1Val( static_cast<ValType>((i==mnStackIdx) ? nInt : 0) );
			writeType1Val( mnValStack[i-1] );
			if( bVert ) writeType1Val( static_cast<ValType>((i==mnStackIdx) ? nInt : 0) );
			bVert = !bVert;
			writeTypeOp( TYPE2OP::RCURVETO);
		}
		}
		clear();
		break;
	case TYPE2OP::HHCURVETO:
		i = (mnStackIdx & 1);
		while( (i += 4) <= mnStackIdx) {
			if( i != 5)
				writeCurveTo( i, -4,  0, -3, -2, -1, 0);
			else
				writeCurveTo( i, -4, -5, -3, -2, -1, 0);
		}
		clear();
		break;
	case TYPE2OP::VVCURVETO:
		i = (mnStackIdx & 1);
		while( (i += 4) <= mnStackIdx) {
			if( i != 5)
				writeCurveTo( i,  0, -4, -3, -2, 0, -1);
			else
				writeCurveTo( i, -5, -4, -3, -2, 0, -1);
		}
		clear();
		break;
	case TYPE2OP::ENDCHAR:
		if( mbNeedClose)
			writeTypeOp( TYPE1OP::CLOSEPATH);
		else
			updateWidth( size() >= 1);
		// mbNeedClose = true;
		writeTypeOp( TYPE1OP::ENDCHAR);
		break;
	default:
		if( ((nType2Op >= 32) && (nType2Op <= 255)) || (nType2Op == 28)) {
			--mpReadPtr;
			read2push();
		} else {
			popAll2Write( nType2Op);
			assert( false); // TODO?
		}
		break;
	}
}

// --------------------------------------------------------------------

void CffSubsetterContext::convertOneTypeEsc( void)
{
	const int nType2Esc = *(mpReadPtr++);
	ValType* pTop = &mnValStack[ mnStackIdx-1];
	// convert each T2op
	switch( nType2Esc) {
	case TYPE2OP::AND:
		assert( mnStackIdx >= 2 );
		pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) & static_cast<int>(pTop[-1]));
		--mnStackIdx;
		break;
	case TYPE2OP::OR:
		assert( mnStackIdx >= 2 );
		pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) | static_cast<int>(pTop[-1]));
		--mnStackIdx;
		break;
	case TYPE2OP::NOT:
		assert( mnStackIdx >= 1 );
		pTop[0] = (pTop[0] == 0);
		break;
	case TYPE2OP::ABS:
		assert( mnStackIdx >= 1 );
		if( pTop[0] >= 0)
			break;
		// fall through
	case TYPE2OP::NEG:
		assert( mnStackIdx >= 1 );
		pTop[0] = -pTop[0];
		break;
	case TYPE2OP::ADD:
		assert( mnStackIdx >= 2 );
		pTop[0] += pTop[-1];
		--mnStackIdx;
		break;
	case TYPE2OP::SUB:
		assert( mnStackIdx >= 2 );
		pTop[0] -= pTop[-1];
		--mnStackIdx;
		break;
	case TYPE2OP::MUL:
		assert( mnStackIdx >= 2 );
		if( pTop[-1])
			pTop[0] *= pTop[-1];
		--mnStackIdx;
		break;
	case TYPE2OP::DIV:
		assert( mnStackIdx >= 2 );
		if( pTop[-1])
			pTop[0] /= pTop[-1];
		--mnStackIdx;
		break;
	case TYPE2OP::EQ:
		assert( mnStackIdx >= 2 );
		pTop[0] = (pTop[0] == pTop[-1]);
		--mnStackIdx;
		break;
	case TYPE2OP::DROP:
		assert( mnStackIdx >= 1 );
		--mnStackIdx;
		break;
	case TYPE2OP::PUT: {
		assert( mnStackIdx >= 2 );
		const int nIdx = static_cast<int>(pTop[0]);
		assert( nIdx >= 0 );
		assert( nIdx < NMAXTRANS );
		mnTransVals[ nIdx] = pTop[-1];
		mnStackIdx -= 2;
		break;
		}
	case TYPE2OP::GET: {
		assert( mnStackIdx >= 1 );
		const int nIdx = static_cast<int>(pTop[0]);
		assert( nIdx >= 0 );
		assert( nIdx < NMAXTRANS );
		pTop[0] = mnTransVals[ nIdx ];
		break;
		}
	case TYPE2OP::IFELSE: {
		assert( mnStackIdx >= 4 );
		if( pTop[-1] > pTop[0] )
			pTop[-3] = pTop[-2];
		mnStackIdx -= 3;
		break;
		}
	case TYPE2OP::RANDOM:
		pTop[+1] = 1234; // TODO
		++mnStackIdx;
		break;
	case TYPE2OP::SQRT:
		// TODO: implement
		break;
	case TYPE2OP::DUP:
		assert( mnStackIdx >= 1 );
		pTop[+1] = pTop[0];
		++mnStackIdx;
		break;
	case TYPE2OP::EXCH: {
		assert( mnStackIdx >= 2 );
		const ValType nVal = pTop[0];
		pTop[0] = pTop[-1];
		pTop[-1] = nVal;
		break;
		}
	case TYPE2OP::INDEX: {
		assert( mnStackIdx >= 1 );
		const int nVal = static_cast<int>(pTop[0]);
		assert( nVal >= 0 );
		assert( nVal < mnStackIdx-1 );
		pTop[0] = pTop[-1-nVal];
		break;
		}
	case TYPE2OP::ROLL: {
		assert( mnStackIdx >= 1 );
		const int nNum = static_cast<int>(pTop[0]);
		assert( nNum >= 0);
		assert( nNum < mnStackIdx-2 );
		(void)nNum; // TODO: implement
		const int nOfs = static_cast<int>(pTop[-1]);
		mnStackIdx -= 2;
		(void)nOfs;// TODO: implement
		break;
		}
	case TYPE2OP::HFLEX1: {
			assert( mnStackIdx == 9);
#if 0 // emulate hflex1 as straight line
			const ValType* pX = &mnValStack[ mnStackIdx];
			const ValType fDX = pX[-9] + pX[-7] + pX[-5] + pX[-4] + pX[-3] + pX[-1];
			writeType1Val( fDX);
			writeTypeOp( TYPE1OP::HLINETO);
#else // emulate hflex1 as two curves
			writeCurveTo( mnStackIdx, -9, -8, -7, -6, -5,  0);
			writeCurveTo( mnStackIdx, -4,  0, -3, -2, -1,  0);
		// TODO: emulate hflex1 using othersubr call
#endif
			mnStackIdx -= 9;
		}
		break;
	case TYPE2OP::HFLEX: {
			assert( mnStackIdx == 7);
			ValType* pX = &mnValStack[ mnStackIdx];
#if 0 // emulate hflex as straight line
			const ValType fDX = pX[-7] + pX[-6] + pX[-4] + pX[-3] + pX[-2] + pX[-1];
			writeType1Val( fDX);
			writeTypeOp( TYPE1OP::HLINETO);
#else // emulate hflex as two curves
			pX[+1] = -pX[-5]; // temp: +dy5==-dy2
			writeCurveTo( mnStackIdx, -7,  0, -6, -5, -4,  0);
			writeCurveTo( mnStackIdx, -3,  0, -2, +1, -1,  0);
		// TODO: emulate hflex using othersubr call
#endif
			mnStackIdx -= 7;
		}
		break;
	case TYPE2OP::FLEX: {
			assert( mnStackIdx == 13 );
			writeCurveTo( mnStackIdx, -13, -12, -11, -10, -9, -8 );
			writeCurveTo( mnStackIdx,  -7,  -6,  -5,  -4, -3, -2 );
			const ValType nFlexDepth =  mnValStack[ mnStackIdx-1 ];
			(void)nFlexDepth; // ignoring nFlexDepth
			mnStackIdx -= 13;
		}
		break;
	case TYPE2OP::FLEX1: {
			assert( mnStackIdx == 11 );
			// write the first part of the flex1-hinted curve
			writeCurveTo( mnStackIdx, -11, -10, -9, -8, -7, -6 );

			// determine if nD6 is horizontal or vertical
			const int i = mnStackIdx;
			ValType nDeltaX = mnValStack[i-11] + mnValStack[i-9] + mnValStack[i-7] + mnValStack[i-5] + mnValStack[i-3];
			if( nDeltaX < 0 ) nDeltaX = -nDeltaX;
			ValType nDeltaY = mnValStack[i-10] + mnValStack[i-8] + mnValStack[i-6] + mnValStack[i-4] + mnValStack[i-2];
			if( nDeltaY < 0 ) nDeltaY = -nDeltaY;
			const bool bVertD6 = (nDeltaY > nDeltaX);

			// write the second part of the flex1-hinted curve
			if( !bVertD6 )
				writeCurveTo( mnStackIdx, -5, -4, -3, -2, -1, 0);
			else
				writeCurveTo( mnStackIdx, -5, -4, -3, -2, 0, -1);
			mnStackIdx -= 11;
		}
		break;
	default:
		fprintf( stderr,"unhandled type2esc %d\n", nType2Esc);
		assert( false);
		break;
	}
}

// --------------------------------------------------------------------

void CffSubsetterContext::callType2Subr( bool bGlobal, int nSubrNumber)
{
	const U8* const pOldReadPtr = mpReadPtr;
	const U8* const pOldReadEnd = mpReadEnd;

	if( bGlobal ) {
		nSubrNumber += mnGlobalSubrBias;
		seekIndexData( mnGlobalSubrBase, nSubrNumber);
	} else {
		nSubrNumber += mpCffLocal->mnLocalSubrBias;
		seekIndexData( mpCffLocal->mnLocalSubrBase, nSubrNumber);
	}

	while( mpReadPtr < mpReadEnd)
		convertOneTypeOp();

	mpReadPtr = pOldReadPtr;
	mpReadEnd = pOldReadEnd;
}

// --------------------------------------------------------------------

static const int MAX_T1OPS_SIZE = 81920; // TODO: use dynamic value

int CffSubsetterContext::convert2Type1Ops( CffLocal* pCffLocal, const U8* const pT2Ops, int nT2Len, U8* const pT1Ops)
{
	mpCffLocal = pCffLocal;

	// prepare the charstring conversion
	mpWritePtr = pT1Ops;
#if 1 	// TODO: update caller
	U8 aType1Ops[ MAX_T1OPS_SIZE];
	if( !pT1Ops)
		mpWritePtr = aType1Ops;
	*const_cast<U8**>(&pT1Ops) = mpWritePtr;
#else
	assert( pT1Ops);
#endif

	// prepend random seed for T1crypt
	*(mpWritePtr++) = 0x48;
	*(mpWritePtr++) = 0x44;
	*(mpWritePtr++) = 0x55;
	*(mpWritePtr++) = ' ';
#if 1 // convert the Type2 charstring to Type1
	mpReadPtr = pT2Ops;
	mpReadEnd = pT2Ops + nT2Len;
	// prepend "hsbw" or "sbw"
	// TODO: only emit hsbw when charwidth is known
	// TODO: remove charwidth from T2 stack
	writeType1Val( 0); // TODO: aSubsetterContext.getLeftSideBearing();
	writeType1Val( 1000/*###getCharWidth()###*/);
	writeTypeOp( TYPE1OP::HSBW);
mbSawError = false;
mbNeedClose = false;
mbIgnoreHints = false;
mnHintSize=mnHorzHintSize=mnStackIdx=0; maCharWidth=-1;//#######
mnCntrMask = 0;
	while( mpReadPtr < mpReadEnd)
		convertOneTypeOp();
//	if( bActivePath)
//		writeTypeOp( TYPE1OP::CLOSEPATH);
//	if( bSubRoutine)
//		writeTypeOp( TYPE1OP::RETURN);
if( mbSawError) {
	mpWritePtr = pT1Ops+4;
 	// create an "idiotproof" charstring
	writeType1Val( 0);
	writeType1Val( 800);
	writeTypeOp( TYPE1OP::HSBW);
	writeType1Val( 50);
	writeTypeOp( TYPE1OP::HMOVETO);
	writeType1Val( 650);
	writeType1Val( 100);
	writeTypeOp( TYPE1OP::RLINETO);
	writeType1Val( -350);
	writeType1Val( 700);
	writeTypeOp( TYPE1OP::RLINETO);
#if 0
	writeType1Val( -300);
	writeType1Val( -800);
	writeTypeOp( TYPE1OP::RLINETO);
#else
	writeTypeOp( TYPE1OP::CLOSEPATH);
#endif
	writeTypeOp( TYPE1OP::ENDCHAR);
}
#else // useful for manually encoding charstrings
	mpWritePtr = pT1Ops;
	mpWritePtr += sprintf( (char*)mpWritePtr, "OOo_\x8b\x8c\x0c\x10\x0b");
#endif
	const int nType1Len = mpWritePtr - pT1Ops;

	// encrypt the Type1 charstring
	int nRDCryptR = 4330; // TODO: mnRDCryptSeed;
	for( U8* p = pT1Ops; p < mpWritePtr; ++p) {
		*p ^= (nRDCryptR >> 8); 
		nRDCryptR = (*(U8*)p + nRDCryptR) * 52845 + 22719;
	}

	return nType1Len;
}

// --------------------------------------------------------------------

RealType CffSubsetterContext::readRealVal()
{
	// TODO: more thorough number validity test
	bool bComma = false;
	int nExpVal = 0;
	int nExpSign = 0;
	S64 nNumber = 0;
	RealType fReal = +1.0;
	for(;;){
		const U8 c = *(mpReadPtr++); // read nibbles
		// parse high nibble
		const U8 nH = c >> 4U;
		if( nH <= 9) {
			nNumber = nNumber * 10 + nH;
			--nExpVal;
		} else if( nH == 10) {	// comma
			nExpVal = 0;
			bComma = true;
		} else if( nH == 11) {	// +exp
			fReal *= nNumber;
			nExpSign = +1;
			nNumber = 0;
		} else if( nH == 12) {	// -exp
			fReal *= nNumber;
			nExpSign = -1;
			nNumber = 0;
		} else if( nH == 13) {	// reserved
			// TODO: ignore or error?
		} else if( nH == 14)	// minus
			fReal = -fReal;
		else if( nH == 15)	// end
			break;
		// parse low nibble
		const U8 nL = c & 0x0F;
		if( nL <= 9) {
			nNumber = nNumber * 10 + nL;
			--nExpVal;
		} else if( nL == 10) {	// comma
			nExpVal = 0;
			bComma = true;
		} else if( nL == 11) {	// +exp
			fReal *= nNumber;
			nNumber = 0;
			nExpSign = +1;
		} else if( nL == 12) {	// -exp
			fReal *= nNumber;
			nNumber = 0;
			nExpSign = -1;
		} else if( nL == 13) {	// reserved
			// TODO: ignore or error?
		} else if( nL == 14)	// minus
			fReal = -fReal;
		else if( nL == 15)	// end
			break;
	}

	// merge exponents
	if( !bComma)
		nExpVal = 0;
	if( !nExpSign) { fReal *= nNumber;}
	else if( nExpSign > 0) { nExpVal += static_cast<int>(nNumber);}
	else if( nExpSign < 0) { nExpVal -= static_cast<int>(nNumber);}

	// apply exponents
	if( !nExpVal) { /*nothing to apply*/}
	else if( nExpVal > 0) { while( --nExpVal >= 0) fReal *= 10.0;}
	else if( nExpVal < 0) { while( ++nExpVal <= 0) fReal /= 10.0;}
	return fReal;
}

// --------------------------------------------------------------------

int CffSubsetterContext::seekIndexData( int nIndexBase, int nDataIndex)
{
	assert( (nIndexBase > 0) && (mpBasePtr + nIndexBase + 3 <= mpBaseEnd));
	if( nDataIndex < 0)
		return -1;
	mpReadPtr = mpBasePtr + nIndexBase;
	const int nDataCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	if( nDataIndex >= nDataCount)
		return -1;
	const int nDataOfsSz = mpReadPtr[2];
	mpReadPtr += 3 + (nDataOfsSz * nDataIndex);
	int nOfs1 = 0;
	switch( nDataOfsSz) {
		default: fprintf( stderr, "\tINVALID nDataOfsSz=%d\n\n", nDataOfsSz); return -1;
		case 1:	nOfs1 = mpReadPtr[0]; break;
		case 2:	nOfs1 = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
		case 3:	nOfs1 = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2]; break;
		case 4:	nOfs1 = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
	}
	mpReadPtr += nDataOfsSz;

	int nOfs2 = 0;
	switch( nDataOfsSz) {
		case 1:	nOfs2 = mpReadPtr[0]; break;
		case 2:	nOfs2 = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
		case 3:	nOfs2 = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2]; break;
		case 4:	nOfs2 = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
	}

	mpReadPtr = mpBasePtr + (nIndexBase + 2) + nDataOfsSz * (nDataCount + 1) + nOfs1;
	mpReadEnd = mpReadPtr + (nOfs2 - nOfs1);
	assert( nOfs1 >= 0);
	assert( nOfs2 >= nOfs1);
	assert( mpReadPtr <= mpBaseEnd);
	assert( mpReadEnd <= mpBaseEnd);
	return (nOfs2 - nOfs1);
}

// --------------------------------------------------------------------

void CffSubsetterContext::seekIndexEnd( int nIndexBase)
{
	assert( (nIndexBase > 0) && (mpBasePtr + nIndexBase + 3 <= mpBaseEnd));
	mpReadPtr = mpBasePtr + nIndexBase;
	const int nDataCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	const int nDataOfsSz = mpReadPtr[2];
	mpReadPtr += 3 + nDataOfsSz * nDataCount;
	assert( mpReadPtr <= mpBaseEnd);
	int nEndOfs = 0;
	switch( nDataOfsSz) {
		default: fprintf( stderr, "\tINVALID nDataOfsSz=%d\n\n", nDataOfsSz); return;
		case 1:	nEndOfs = mpReadPtr[0]; break;
		case 2:	nEndOfs = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
		case 3:	nEndOfs = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2];break;
		case 4:	nEndOfs = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
	}
	mpReadPtr += nDataOfsSz;
	mpReadPtr += nEndOfs - 1;
	mpReadEnd = mpBaseEnd;
	assert( nEndOfs >= 0);
	assert( mpReadEnd <= mpBaseEnd);
}

// ====================================================================

// initialize FONTDICT specific values
CffLocal::CffLocal( void)
:	mnPrivDictBase( 0)
,	mnPrivDictSize( 0)
,	mnLocalSubrOffs( 0)
,	mnLocalSubrBase( 0)
,	mnLocalSubrCount( 0)
,	mnLocalSubrBias( 0)
,	maNominalWidth( 0)
,	maDefaultWidth( 0)
,	maStemStdHW( 0)
,	maStemStdVW( 0)
,	mfBlueScale( 0.0)
,	mfBlueShift( 0.0)
,	mfBlueFuzz( 0.0)
,	mfExpFactor( 0.0)
,	mnLangGroup( 0)
,	mbForceBold( false)
{
	maStemSnapH.clear();
	maStemSnapV.clear();
	maBlueValues.clear();
	maOtherBlues.clear();
	maFamilyBlues.clear();
	maFamilyOtherBlues.clear();
}

// --------------------------------------------------------------------

CffGlobal::CffGlobal( void)
:	mnNameIdxBase( 0)
,	mnNameIdxCount( 0)
,	mnStringIdxBase( 0)
,	mnStringIdxCount( 0)
,	mbCIDFont( false)
,	mnCharStrBase( 0)
,	mnCharStrCount( 0)
,	mnEncodingBase( 0)
,	mnCharsetBase( 0)
,	mnGlobalSubrBase( 0)
,	mnGlobalSubrCount( 0)
,	mnGlobalSubrBias( 0)
,	mnFDSelectBase( 0)
,	mnFontDictBase( 0)
,	mnFDAryCount( 1)
,	mnFontNameSID( 0)
,	mnFullNameSID( 0)
,	mnFamilyNameSID( 0)
{
	maFontBBox.clear();
	// TODO; maFontMatrix.clear();
}

// --------------------------------------------------------------------

void CffSubsetterContext::initialCffRead( void)
{
	// get the CFFHeader
	mpReadPtr = mpBasePtr;
	const U8 nVerMajor = *(mpReadPtr++);
	const U8 nVerMinor = *(mpReadPtr++);
	const U8 nHeaderSize = *(mpReadPtr++);
	const U8 nOffsetSize = *(mpReadPtr++);
	// TODO: is the version number useful for anything else?
	assert( (nVerMajor == 1) && (nVerMinor == 0));
	(void)(nVerMajor + nVerMinor + nOffsetSize); // avoid compiler warnings

	// prepare access to the NameIndex
	mnNameIdxBase = nHeaderSize;
	mpReadPtr = mpBasePtr + nHeaderSize;
	mnNameIdxCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	seekIndexEnd( mnNameIdxBase);

	// get the TopDict index
	const long nTopDictBase = getReadOfs();
	const int nTopDictCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	if( nTopDictCount) {
		for( int i = 0; i < nTopDictCount; ++i) {
			seekIndexData( nTopDictBase, i);
			while( mpReadPtr < mpReadEnd)
				readDictOp();
			assert( mpReadPtr == mpReadEnd);
		}
	}

	// prepare access to the String index
	mnStringIdxBase =  getReadOfs();
	mnStringIdxCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	seekIndexEnd( mnStringIdxBase);

	// prepare access to the GlobalSubr index
	mnGlobalSubrBase =  getReadOfs();
	mnGlobalSubrCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
	mnGlobalSubrBias = (mnGlobalSubrCount<1240)?107:(mnGlobalSubrCount<33900)?1131:32768;
	// skip past the last GlobalSubr entry
//	seekIndexEnd( mnGlobalSubrBase);

	// get/skip the Encodings (we got mnEncodingBase from TOPDICT)
//	seekEncodingsEnd( mnEncodingBase);
	// get/skip the Charsets (we got mnCharsetBase from TOPDICT)
//	seekCharsetsEnd( mnCharStrBase);
	// get/skip FDSelect (CID only) data

	// prepare access to the CharStrings index (we got the base from TOPDICT)
	mpReadPtr = mpBasePtr + mnCharStrBase;
	mnCharStrCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
//	seekIndexEnd( mnCharStrBase);

	// read the FDArray index (CID only)
	if( mbCIDFont) {
//		assert( mnFontDictBase == tellRel());
		mpReadPtr = mpBasePtr + mnFontDictBase;
		mnFDAryCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
		if (maCffLocal.size() < static_cast<size_t>(mnFDAryCount))
			maCffLocal.resize(mnFDAryCount);

		// read FDArray details to get access to the PRIVDICTs
		for( int i = 0; i < mnFDAryCount; ++i) {
			mpCffLocal = &maCffLocal[i];
			seekIndexData( mnFontDictBase, i);
			while( mpReadPtr < mpReadEnd)
				readDictOp();
			assert( mpReadPtr == mpReadEnd);
		}
	}

	for( int i = 0; i < mnFDAryCount; ++i) {
		mpCffLocal = &maCffLocal[i];

		// get the PrivateDict index
		// (we got mnPrivDictSize and mnPrivDictBase from TOPDICT or FDArray)
		if( mpCffLocal->mnPrivDictSize != 0) {
			assert( mpCffLocal->mnPrivDictSize > 0);
			// get the PrivDict data
			mpReadPtr = mpBasePtr + mpCffLocal->mnPrivDictBase;
			mpReadEnd = mpReadPtr + mpCffLocal->mnPrivDictSize;
			assert( mpReadEnd <= mpBaseEnd);
			// read PrivDict details
			while( mpReadPtr < mpReadEnd)
				readDictOp();
		}

		// prepare access to the LocalSubrs (we got mnLocalSubrOffs from PRIVDICT)
		if( mpCffLocal->mnLocalSubrOffs) {
			// read LocalSubrs summary
			mpCffLocal->mnLocalSubrBase = mpCffLocal->mnPrivDictBase + mpCffLocal->mnLocalSubrOffs;
			mpReadPtr = mpBasePtr + mpCffLocal->mnLocalSubrBase;
			const int nSubrCount = (mpReadPtr[0] << 8) + mpReadPtr[1];
			mpCffLocal->mnLocalSubrCount = nSubrCount;
			mpCffLocal->mnLocalSubrBias = (nSubrCount<1240)?107:(nSubrCount<33900)?1131:32768;
//			seekIndexEnd( mpCffLocal->mnLocalSubrBase);
		}
	}

	// ignore the Notices info
}

// --------------------------------------------------------------------

// get a cstring from a StringID
const char* CffSubsetterContext::getString( int nStringID)
{
	// get a standard string if possible
	const static int nStdStrings = sizeof(pStringIds)/sizeof(*pStringIds);
	if( (nStringID >= 0) && (nStringID < nStdStrings))
		return pStringIds[ nStringID];

	// else get the string from the StringIndex table
	const U8* pReadPtr = mpReadPtr;
	const U8* pReadEnd = mpReadEnd;
	nStringID -= nStdStrings;
	int nLen = seekIndexData( mnStringIdxBase, nStringID);
	// assert( nLen >= 0);
	// TODO: just return the undecorated name
	// TODO: get rid of static char buffer
	static char aNameBuf[ 2560];
	if( nLen < 0) {
		sprintf( aNameBuf, "name[%d].notfound!", nStringID);
	} else {
		const int nMaxLen = sizeof(aNameBuf) - 1;
		if( nLen >= nMaxLen)
			nLen = nMaxLen;
		for( int i = 0; i < nLen; ++i)
			aNameBuf[i] = *(mpReadPtr++);
		aNameBuf[ nLen] = '\0';
	}
	mpReadPtr = pReadPtr;
	mpReadEnd = pReadEnd;
	return aNameBuf;
}

// --------------------------------------------------------------------

// access a CID's FDSelect table
int CffSubsetterContext::getFDSelect( int nGlyphIndex) const
{
	assert( nGlyphIndex >= 0);
	assert( nGlyphIndex < mnCharStrCount);
	if( !mbCIDFont)
		return 0;

	const U8* pReadPtr = mpBasePtr + mnFDSelectBase;
	const U8 nFDSelFormat = *(pReadPtr++);
	switch( nFDSelFormat) {
		case 0: { // FDSELECT format 0
				pReadPtr += nGlyphIndex;
				const U8 nFDIdx = *(pReadPtr++);
				return nFDIdx;
			} //break;
		case 3: { // FDSELECT format 3
				const U16 nRangeCount = (pReadPtr[0]<<8) + pReadPtr[1];
				assert( nRangeCount > 0);
				assert( nRangeCount <= mnCharStrCount);
				U16 nPrev = (pReadPtr[2]<<8) + pReadPtr[3];
				assert( nPrev == 0);
				pReadPtr += 4;
				// TODO? binary search
				for( int i = 0; i < nRangeCount; ++i) {
					const U8 nFDIdx = pReadPtr[0];
					const U16 nNext = (pReadPtr[1]<<8) + pReadPtr[2];
					assert( nPrev < nNext);
					if( nGlyphIndex < nNext)
						return nFDIdx;
					pReadPtr += 3;
					nPrev = nNext;
				}
			} break;
		default:	// invalid FDselect format
			fprintf( stderr, "invalid CFF.FdselType=%d\n", nFDSelFormat);
			break;
	}

	assert( false);
	return -1;
}

// --------------------------------------------------------------------

int CffSubsetterContext::getGlyphSID( int nGlyphIndex) const
{
	if( nGlyphIndex == 0)
		return 0;       // ".notdef"
	assert( nGlyphIndex >= 0);
	assert( nGlyphIndex < mnCharStrCount);
	if( (nGlyphIndex < 0) || (nGlyphIndex >= mnCharStrCount))
		return -1;

	// get the SID/CID from the Charset table
	 const U8* pReadPtr = mpBasePtr + mnCharsetBase;
	const U8 nCSetFormat = *(pReadPtr++);
	int nGlyphsToSkip = nGlyphIndex - 1;
	switch( nCSetFormat) {
		case 0: // charset format 0
			pReadPtr += 2 * nGlyphsToSkip;
			nGlyphsToSkip = 0;
			break;
		case 1: // charset format 1
			while( nGlyphsToSkip >= 0) {
				const int nLeft = pReadPtr[2];
				if( nGlyphsToSkip <= nLeft)
					break;
				nGlyphsToSkip -= nLeft + 1;
				pReadPtr += 3;
			}
			break;
		case 2: // charset format 2
			while( nGlyphsToSkip >= 0) {
				const int nLeft = (pReadPtr[2]<<8) + pReadPtr[3];
				if( nGlyphsToSkip <= nLeft)
					break;
				nGlyphsToSkip -= nLeft + 1;
				pReadPtr += 4;
			}
			break;
		default:
			fprintf( stderr, "ILLEGAL CFF-Charset format %d\n", nCSetFormat);
			return -2;
	}

	int nSID = (pReadPtr[0]<<8) + pReadPtr[1];
	nSID += nGlyphsToSkip;
	// NOTE: for CID-fonts the resulting SID is interpreted as CID
	return nSID;
}

// --------------------------------------------------------------------

// NOTE: the result becomes invalid with the next call to this method
const char* CffSubsetterContext::getGlyphName( int nGlyphIndex)
{
	// the first glyph is always the .notdef glyph
	const char* pGlyphName = ".notdef";
	if( nGlyphIndex == 0)
		return pGlyphName;

	// prepare a result buffer
	// TODO: get rid of static buffer
	static char aDefaultGlyphName[64];
	pGlyphName = aDefaultGlyphName;

	// get the glyph specific name
	const int nSID = getGlyphSID( nGlyphIndex);
	if( nSID < 0)           // default glyph name
		sprintf( aDefaultGlyphName, "gly%03d", nGlyphIndex);
	else if( mbCIDFont)     // default glyph name in CIDs
		 sprintf( aDefaultGlyphName, "cid%03d", nSID);
	else {                  // glyph name from string table
		const char* pSidName = getString( nSID);
		// check validity of glyph name
		if( pSidName) {
			const char* p = pSidName;
			while( (*p >= '0') && (*p <= 'z')) ++p;
			if( (p >= pSidName+1) && (*p == '\0'))
				pGlyphName = pSidName;
		}
		// if needed invent a fallback name
		if( pGlyphName != pSidName)
		 	sprintf( aDefaultGlyphName, "bad%03d", nSID);
	}

	 return pGlyphName;
}

// --------------------------------------------------------------------

class Type1Emitter
{
public:
	explicit	Type1Emitter( const char* pOutFileName, bool bPfbSubset = true);
	explicit	Type1Emitter( FILE* pOutFile, bool bPfbSubset = true);
	/*virtual*/	~Type1Emitter( void);
	void		setSubsetName( const char* );

	void		emitRawData( const char* pData, int nLength) const;
	void		emitAllRaw( void);
	void		emitAllHex( void);
	void		emitAllCrypted( void);
	int		tellPos( void) const;
	void		updateLen( int nTellPos, int nLength);
	void		emitValVector( const char* pLineHead, const char* pLineTail, const ValVector&);
private:
	FILE*		mpFileOut;
	bool		mbCloseOutfile;
	char		maBuffer[MAX_T1OPS_SIZE];	// TODO: dynamic allocation
	int		mnEECryptR;
public:
	char*		mpPtr;

	char		maSubsetName[256];
	bool		mbPfbSubset;
	int		mnHexLineCol;
};

// --------------------------------------------------------------------

Type1Emitter::Type1Emitter( const char* pPfbFileName, bool bPfbSubset)
:	mpFileOut( NULL)
,	mbCloseOutfile( true)
,	mnEECryptR( 55665)	// default eexec seed, TODO: mnEECryptSeed
,	mpPtr( maBuffer)
,	mbPfbSubset( bPfbSubset)
,	mnHexLineCol( 0)
{
	mpFileOut = fopen( pPfbFileName, "wb");
	maSubsetName[0] = '\0';
}

// --------------------------------------------------------------------

Type1Emitter::Type1Emitter( FILE* pOutFile, bool bPfbSubset)
:	mpFileOut( pOutFile)
,	mbCloseOutfile( false)
,	mnEECryptR( 55665)	// default eexec seed, TODO: mnEECryptSeed
,	mpPtr( maBuffer)
,	mbPfbSubset( bPfbSubset)
,	mnHexLineCol( 0)
{
	maSubsetName[0] = '\0';
}

// --------------------------------------------------------------------

Type1Emitter::~Type1Emitter( void)
{
	if( !mpFileOut)
		return;
	if( mbCloseOutfile )
		fclose( mpFileOut);
	mpFileOut = NULL;
}

// --------------------------------------------------------------------

void Type1Emitter::setSubsetName( const char* pSubsetName)
{
	maSubsetName[0] = '\0';
	if( pSubsetName)
		strncpy( maSubsetName, pSubsetName, sizeof(maSubsetName));
	maSubsetName[sizeof(maSubsetName)-1] = '\0';
}

// --------------------------------------------------------------------

int Type1Emitter::tellPos( void) const
{
	int nTellPos = ftell( mpFileOut);
	return nTellPos;
}

// --------------------------------------------------------------------

void Type1Emitter::updateLen( int nTellPos, int nLength)
{
	// update PFB segment header length
	U8 cData[4];
	cData[0] = static_cast<U8>(nLength >>  0);
	cData[1] = static_cast<U8>(nLength >>  8);
	cData[2] = static_cast<U8>(nLength >> 16);
	cData[3] = static_cast<U8>(nLength >> 24);
	const long nCurrPos = ftell( mpFileOut);
	fseek( mpFileOut, nTellPos, SEEK_SET);
	fwrite( cData, 1, sizeof(cData), mpFileOut);
	if( nCurrPos >= 0)
		fseek( mpFileOut, nCurrPos, SEEK_SET);
}

// --------------------------------------------------------------------

inline void Type1Emitter::emitRawData( const char* pData, int nLength) const
{
	fwrite( pData, 1, nLength, mpFileOut);
}

// --------------------------------------------------------------------

inline void Type1Emitter::emitAllRaw( void)
{
	// writeout raw data
	assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
	emitRawData( maBuffer, mpPtr - maBuffer);
	// reset the raw buffer
	mpPtr = maBuffer;
}

// --------------------------------------------------------------------

inline void Type1Emitter::emitAllHex( void)
{
	assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
	for( const char* p = maBuffer; p < mpPtr;) {
		// convert binary chunk to hex
		char aHexBuf[0x4000];
		char* pOut = aHexBuf;
		while( (p < mpPtr) && (pOut < aHexBuf+sizeof(aHexBuf)-4)) {
			// convert each byte to hex
			char cNibble = (*p >> 4) & 0x0F;
			cNibble += (cNibble < 10) ? '0' : 'A'-10;
			*(pOut++) = cNibble;
			cNibble = *(p++) & 0x0F;
			cNibble += (cNibble < 10) ? '0' : 'A'-10;
			*(pOut++) = cNibble;
			// limit the line length
			if( (++mnHexLineCol & 0x3F) == 0)
				*(pOut++) = '\n';
		}
		// writeout hex-converted chunk
		emitRawData( aHexBuf, pOut-aHexBuf);
	}
	// reset the raw buffer
	mpPtr = maBuffer;
}

// --------------------------------------------------------------------

void Type1Emitter::emitAllCrypted( void)
{
	// apply t1crypt
	for( char* p = maBuffer; p < mpPtr; ++p) {
		*p ^= (mnEECryptR >> 8);
		mnEECryptR = (*(U8*)p + mnEECryptR) * 52845 + 22719;
	}

	// emit the t1crypt result
	if( mbPfbSubset)
		emitAllRaw();
	else
		emitAllHex();
}

// --------------------------------------------------------------------

// #i110387# quick-and-dirty double->ascii conversion
// needed because sprintf/ecvt/etc. alone are too localized (LC_NUMERIC)
// also strip off trailing zeros in fraction while we are at it
inline int dbl2str( char* pOut, double fVal, int nPrecision=6)
{
	const int nLen = psp::getValueOfDouble( pOut, fVal, nPrecision);
	return nLen;
}

// --------------------------------------------------------------------

void Type1Emitter::emitValVector( const char* pLineHead, const char* pLineTail,
	const ValVector& rVector)
{
	// ignore empty vectors
	if( rVector.empty())
		return;

	// emit the line head
	mpPtr += sprintf( mpPtr, "%s", pLineHead);
	// emit the vector values
	ValVector::value_type aVal = 0;
	for( ValVector::const_iterator it = rVector.begin();;) {
		aVal = *it;
		if( ++it == rVector.end() )
			break;
		mpPtr += dbl2str( mpPtr, aVal);
		*(mpPtr++) = ' ';
	}
	// emit the last value
	mpPtr += dbl2str( mpPtr, aVal);
	// emit the line tail
	mpPtr += sprintf( mpPtr, "%s", pLineTail);
}

// --------------------------------------------------------------------

bool CffSubsetterContext::emitAsType1( Type1Emitter& rEmitter,
	const sal_GlyphId* pReqGlyphIds, const U8* pReqEncoding,
	GlyphWidth* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rFSInfo)
{
	// prepare some fontdirectory details
	static const int nUniqueIdBase = 4100000; // using private-interchange UniqueIds
	static int nUniqueId = nUniqueIdBase;
	++nUniqueId;

	char* pFontName = rEmitter.maSubsetName;
	if( !*pFontName ) {
		if( mnFontNameSID) {
			// get the fontname directly if available
			strncpy( pFontName, getString( mnFontNameSID), sizeof(rEmitter.maSubsetName));
		} else if( mnFullNameSID) {
			// approximate fontname as fullname-whitespace
			const char* pI = getString( mnFullNameSID);
			char* pO = pFontName;
			const char* pLimit = pFontName + sizeof(rEmitter.maSubsetName) - 1;
			while( pO < pLimit) {
				const char c = *(pI++);
				if( c != ' ')
					*(pO++) = c;
				if( !c)
					break;
			}
			*pO = '\0';
		} else {
			// fallback name of last resort
			strncpy( pFontName, "DummyName", sizeof(rEmitter.maSubsetName));
		}
	}
	const char* pFullName = pFontName;
	const char* pFamilyName = pFontName;

	char*& pOut = rEmitter.mpPtr; // convenience reference, TODO: cleanup

	// create a PFB+Type1 header
	if( rEmitter.mbPfbSubset ) {
		static const char aPfbHeader[] = "\x80\x01\x00\x00\x00\x00";
		rEmitter.emitRawData( aPfbHeader, sizeof(aPfbHeader)-1);
	}

	pOut += sprintf( pOut, "%%!FontType1-1.0: %s 001.003\n", rEmitter.maSubsetName);
	// emit TOPDICT
#if 0 // improve PS Type1 caching?
	nOfs += sprintf( &aT1Str[nOfs],
		"FontDirectory/%s known{/%s findfont dup/UniqueID known{dup\n"
		"/UniqueID get %d eq exch/FontType get 1 eq and}{pop false}ifelse\n"
		"{save true}{false}ifelse}\n{false}ifelse\n",
			pFamilyName, pFamilyName, nUniqueId);
#endif
	pOut += sprintf( pOut,
		"11 dict begin\n"	// TODO: dynamic entry count for TOPDICT
		"/FontType 1 def\n"
		"/PaintType 0 def\n");
	pOut += sprintf( pOut, "/FontName /%s def\n", rEmitter.maSubsetName);
	pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
	// emit FontMatrix
	if( maFontMatrix.size() == 6)
		rEmitter.emitValVector( "/FontMatrix [", "]readonly def\n", maFontMatrix);
	else // emit default FontMatrix if needed
		pOut += sprintf( pOut, "/FontMatrix [0.001 0 0 0.001 0 0]readonly def\n");
	// emit FontBBox
	if( maFontBBox.size() == 4)
		rEmitter.emitValVector( "/FontBBox {", "}readonly def\n", maFontBBox);
	else // emit default FontBBox if needed
		pOut += sprintf( pOut, "/FontBBox {0 0 999 999}readonly def\n");
	// emit FONTINFO into TOPDICT
	pOut += sprintf( pOut,
		"/FontInfo 2 dict dup begin\n"	// TODO: check fontinfo entry count
		" /FullName (%s) readonly def\n"
		" /FamilyName (%s) readonly def\n"
		"end readonly def\n",
			pFullName, pFamilyName);
#if 0	// TODO: use an standard Type1 encoding if possible
	pOut += sprintf( pOut,
		"/Encoding StandardEncoding def\n");
#else
	pOut += sprintf( pOut,
		"/Encoding 256 array\n"
		"0 1 255 {1 index exch /.notdef put} for\n");
	for( int i = 1; (i < nGlyphCount) && (i < 256); ++i) {
		const char* pGlyphName = getGlyphName( pReqGlyphIds[i]);
		pOut += sprintf( pOut, "dup %d /%s put\n", pReqEncoding[i], pGlyphName);
	}
	pOut += sprintf( pOut, "readonly def\n");
#endif
	pOut += sprintf( pOut,
		// TODO: more topdict entries
		"currentdict end\n"
		"currentfile eexec\n");

	// emit PFB header
	rEmitter.emitAllRaw();
	if( rEmitter.mbPfbSubset) {
		// update PFB header segment
		const int nPfbHeaderLen = rEmitter.tellPos() - 6;
		rEmitter.updateLen( 2, nPfbHeaderLen);
	
		// prepare start of eexec segment
		rEmitter.emitRawData( "\x80\x02\x00\x00\x00\x00", 6);	// segment start
	}
	const int nEExecSegTell = rEmitter.tellPos();

	// which always starts with a privdict
	// count the privdict entries
	int nPrivEntryCount = 9;
#if !defined(IGNORE_HINTS)
	// emit blue hints only if non-default values
	nPrivEntryCount += !mpCffLocal->maOtherBlues.empty();
	nPrivEntryCount += !mpCffLocal->maFamilyBlues.empty();
	nPrivEntryCount += !mpCffLocal->maFamilyOtherBlues.empty();
	nPrivEntryCount += (mpCffLocal->mfBlueScale != 0.0);
	nPrivEntryCount += (mpCffLocal->mfBlueShift != 0.0);
	nPrivEntryCount += (mpCffLocal->mfBlueFuzz != 0.0);
	// emit stem hints only if non-default values
	nPrivEntryCount += (mpCffLocal->maStemStdHW != 0);
	nPrivEntryCount += (mpCffLocal->maStemStdVW != 0);
	nPrivEntryCount += !mpCffLocal->maStemSnapH.empty();
	nPrivEntryCount += !mpCffLocal->maStemSnapV.empty();
	// emit other hints only if non-default values
	nPrivEntryCount += (mpCffLocal->mfExpFactor != 0.0);
	nPrivEntryCount += (mpCffLocal->mnLangGroup != 0);
	nPrivEntryCount += (mpCffLocal->mnLangGroup == 1);
	nPrivEntryCount += (mpCffLocal->mbForceBold != false);
#endif // IGNORE_HINTS
	// emit the privdict header
	pOut += sprintf( pOut,
		"\110\104\125 "
		"dup\n/Private %d dict dup begin\n"
		"/RD{string currentfile exch readstring pop}executeonly def\n"
		"/ND{noaccess def}executeonly def\n"
		"/NP{noaccess put}executeonly def\n"
		"/MinFeature{16 16}ND\n"
		"/password 5839 def\n",		// TODO: mnRDCryptSeed?
			nPrivEntryCount);

#if defined(IGNORE_HINTS)
	pOut += sprintf( pOut, "/BlueValues []ND\n");	// BlueValues are mandatory
#else
	// emit blue hint related privdict entries
	if( !mpCffLocal->maBlueValues.empty())
		rEmitter.emitValVector( "/BlueValues [", "]ND\n", mpCffLocal->maBlueValues);
	else
		pOut += sprintf( pOut, "/BlueValues []ND\n"); // default to empty BlueValues
	rEmitter.emitValVector( "/OtherBlues [", "]ND\n", mpCffLocal->maOtherBlues);
	rEmitter.emitValVector( "/FamilyBlues [", "]ND\n", mpCffLocal->maFamilyBlues);
	rEmitter.emitValVector( "/FamilyOtherBlues [", "]ND\n", mpCffLocal->maFamilyOtherBlues);

	if( mpCffLocal->mfBlueScale) {
		pOut += sprintf( pOut, "/BlueScale ");
		pOut += dbl2str( pOut, mpCffLocal->mfBlueScale, 6);
		pOut += sprintf( pOut, " def\n");
	}
	if( mpCffLocal->mfBlueShift) {	// default BlueShift==7
		pOut += sprintf( pOut, "/BlueShift ");
		pOut += dbl2str( pOut, mpCffLocal->mfBlueShift);
		pOut += sprintf( pOut, " def\n");
	}
	if( mpCffLocal->mfBlueFuzz) {		// default BlueFuzz==1
		pOut += sprintf( pOut, "/BlueFuzz ");
		pOut += dbl2str( pOut, mpCffLocal->mfBlueFuzz);
		pOut += sprintf( pOut, " def\n");
	}

	// emit stem hint related privdict entries
	if( mpCffLocal->maStemStdHW) {
		pOut += sprintf( pOut, "/StdHW [");
		pOut += dbl2str( pOut, mpCffLocal->maStemStdHW);
		pOut += sprintf( pOut, "] def\n");
	}
	if( mpCffLocal->maStemStdVW) {
		pOut += sprintf( pOut, "/StdVW [");
		pOut += dbl2str( pOut, mpCffLocal->maStemStdVW);
		pOut += sprintf( pOut, "] def\n");
	}
	rEmitter.emitValVector( "/StemSnapH [", "]ND\n", mpCffLocal->maStemSnapH);
	rEmitter.emitValVector( "/StemSnapV [", "]ND\n", mpCffLocal->maStemSnapV);

	// emit other hints
	if( mpCffLocal->mbForceBold)
		pOut += sprintf( pOut, "/ForceBold true def\n");
	if( mpCffLocal->mnLangGroup != 0)
		pOut += sprintf( pOut, "/LanguageGroup %d def\n", mpCffLocal->mnLangGroup);
	if( mpCffLocal->mnLangGroup == 1) // compatibility with ancient printers
		pOut += sprintf( pOut, "/RndStemUp false def\n");
	if( mpCffLocal->mfExpFactor) {
		pOut += sprintf( pOut, "/ExpansionFactor ");
		pOut += dbl2str( pOut, mpCffLocal->mfExpFactor);
		pOut += sprintf( pOut, " def\n");
	}
#endif // IGNORE_HINTS

	// emit remaining privdict entries
	pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
	// TODO?: more privdict entries?

	static const char aOtherSubrs[] =
		"/OtherSubrs\n"
		"% Dummy code for faking flex hints\n"
		"[ {} {} {} {systemdict /internaldict known not {pop 3}\n"
		"{1183615869 systemdict /internaldict get exec\n"
		"dup /startlock known\n"
		"{/startlock get exec}\n"
		"{dup /strtlck known\n"
		"{/strtlck get exec}\n"
		"{pop 3}\nifelse}\nifelse}\nifelse\n} executeonly\n"
		"] ND\n";
	memcpy( pOut, aOtherSubrs, sizeof(aOtherSubrs)-1);
	pOut += sizeof(aOtherSubrs)-1;

	// emit used GlobalSubr charstrings
	// these are the just the default subrs
	// TODO: do we need them as the flex hints are resolved differently?
	static const char aSubrs[] =
		"/Subrs 5 array\n"
		"dup 0 15 RD \x5F\x3D\x6B\xAC\x3C\xBD\x74\x3D\x3E\x17\xA0\x86\x58\x08\x85 NP\n"
		"dup 1 9 RD \x5F\x3D\x6B\xD8\xA6\xB5\x68\xB6\xA2 NP\n"
		"dup 2 9 RD \x5F\x3D\x6B\xAC\x39\x46\xB9\x43\xF9 NP\n"
		"dup 3 5 RD \x5F\x3D\x6B\xAC\xB9 NP\n"
		"dup 4 12 RD \x5F\x3D\x6B\xAC\x3E\x5D\x48\x54\x62\x76\x39\x03 NP\n"
		"ND\n";
	memcpy( pOut, aSubrs, sizeof(aSubrs)-1);
	pOut += sizeof(aSubrs)-1;

	// TODO: emit more GlobalSubr charstrings?
	// TODO: emit used LocalSubr charstrings?

	// emit the CharStrings for the requested glyphs
	pOut += sprintf( pOut,
		"2 index /CharStrings %d dict dup begin\n", nGlyphCount);
	rEmitter.emitAllCrypted();
	for( int i = 0; i < nGlyphCount; ++i) {
		const int nCffGlyphId = pReqGlyphIds[i];
		assert( (nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount));
		// get privdict context matching to the glyph
		const int nFDSelect = getFDSelect( nCffGlyphId);
		if( nFDSelect < 0)
			continue;
		mpCffLocal = &maCffLocal[ nFDSelect];
		// convert the Type2op charstring to its Type1op counterpart
		const int nT2Len = seekIndexData( mnCharStrBase, nCffGlyphId);
		assert( nT2Len > 0);
		U8 aType1Ops[ MAX_T1OPS_SIZE]; // TODO: dynamic allocation
		const int nT1Len = convert2Type1Ops( mpCffLocal, mpReadPtr, nT2Len, aType1Ops);
		// get the glyph name
		const char* pGlyphName = getGlyphName( nCffGlyphId);
		// emit the encrypted Type1op charstring
		pOut += sprintf( pOut, "/%s %d RD ", pGlyphName, nT1Len);
		memcpy( pOut, aType1Ops, nT1Len);
		pOut += nT1Len;
		pOut += sprintf( pOut, " ND\n");
		rEmitter.emitAllCrypted();
		// provide individual glyphwidths if requested
		if( pGlyphWidths ) {
			ValType aCharWidth = getCharWidth();
			if( maFontMatrix.size() >= 4)
				aCharWidth *= 1000.0F * maFontMatrix[0];
			pGlyphWidths[i] = static_cast<GlyphWidth>(aCharWidth);
		}
	}
	pOut += sprintf( pOut, "end end\nreadonly put\nput\n");
	pOut += sprintf( pOut, "dup/FontName get exch definefont pop\n");
	pOut += sprintf( pOut, "mark currentfile closefile\n");
	rEmitter.emitAllCrypted();

	// mark stop of eexec encryption
 	if( rEmitter.mbPfbSubset) {
		const int nEExecLen = rEmitter.tellPos() - nEExecSegTell;
		rEmitter.updateLen( nEExecSegTell-4, nEExecLen);
 	}
 
	// create PFB footer
	static const char aPfxFooter[] = "\x80\x01\x14\x02\x00\x00\n" // TODO: check segment len
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"0000000000000000000000000000000000000000000000000000000000000000\n"
		"cleartomark\n"
		"\x80\x03";
 	if( rEmitter.mbPfbSubset)
		rEmitter.emitRawData( aPfxFooter, sizeof(aPfxFooter)-1);
	else
		rEmitter.emitRawData( aPfxFooter+6, sizeof(aPfxFooter)-9);

	// provide details to the subset requesters, TODO: move into own method?
	// note: Top and Bottom are flipped between Type1 and VCL
	// note: the rest of VCL expects the details below to be scaled like for an emUnits==1000 font
	ValType fXFactor = 1.0;
	ValType fYFactor = 1.0;
	if( maFontMatrix.size() >= 4) {
		fXFactor = 1000.0F * maFontMatrix[0];
		fYFactor = 1000.0F * maFontMatrix[3];
	}
	rFSInfo.m_aFontBBox = Rectangle( Point( static_cast<long>(maFontBBox[0] * fXFactor),
										static_cast<long>(maFontBBox[1] * fYFactor) ),
									Point( static_cast<long>(maFontBBox[2] * fXFactor),
										static_cast<long>(maFontBBox[3] * fYFactor) ) );
	// PDF-Spec says the values below mean the ink bounds!
	// TODO: use better approximations for these ink bounds
	rFSInfo.m_nAscent  = +rFSInfo.m_aFontBBox.Bottom();	// for capital letters
	rFSInfo.m_nDescent = -rFSInfo.m_aFontBBox.Top();	// for all letters
	rFSInfo.m_nCapHeight = rFSInfo.m_nAscent;			// for top-flat capital letters

	rFSInfo.m_nFontType = rEmitter.mbPfbSubset ? FontSubsetInfo::TYPE1_PFB : FontSubsetInfo::TYPE1_PFA;
	rFSInfo.m_aPSName	= String( rEmitter.maSubsetName, RTL_TEXTENCODING_UTF8 );

	return true;
}

// ====================================================================

bool FontSubsetInfo::CreateFontSubsetFromCff( GlyphWidth* pOutGlyphWidths )
{
	CffSubsetterContext aCff( mpInFontBytes, mnInByteLength);
	aCff.initialCffRead();

	// emit Type1 subset from the CFF input
	// TODO: also support CFF->CFF subsetting (when PDF-export and PS-printing need it)
	const bool bPfbSubset = (0 != (mnReqFontTypeMask & FontSubsetInfo::TYPE1_PFB));
	Type1Emitter aType1Emitter( mpOutFile, bPfbSubset);
	aType1Emitter.setSubsetName( mpReqFontName);
	bool bRC = aCff.emitAsType1( aType1Emitter,
		mpReqGlyphIds, mpReqEncodedIds,
		pOutGlyphWidths, mnReqGlyphCount, *this);
	return bRC;
}

// ====================================================================