LCOV - code coverage report
Current view: top level - vcl/source/fontsubset - cff.cxx (source / functions) Hit Total Coverage
Test: commit e02a6cb2c3e2b23b203b422e4e0680877f232636 Lines: 0 1020 0.0 %
Date: 2014-04-14 Functions: 0 53 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <cstdio>
      21             : #include <cstring>
      22             : #include <assert.h>
      23             : 
      24             : #include "fontsubset.hxx"
      25             : 
      26             : #include <vcl/strhelper.hxx>
      27             : 
      28             : //#define IGNORE_HINTS
      29             : 
      30             : typedef unsigned char U8;
      31             : typedef unsigned short U16;
      32             : typedef long long S64;
      33             : 
      34             : typedef sal_Int32 GlyphWidth;
      35             : 
      36             : typedef float RealType;
      37             : typedef RealType ValType;
      38             : #include <vector>
      39             : typedef std::vector<ValType> ValVector;
      40             : 
      41             : static const char* pStringIds[] = {
      42             : /*0*/   ".notdef",      "space",            "exclam",           "quotedbl",
      43             :     "numbersign",       "dollar",           "percent",          "ampersand",
      44             :     "quoteright",       "parenleft",        "parenright",       "asterisk",
      45             :     "plus",             "comma",            "hyphen",           "period",
      46             : /*16*/  "slash",        "zero",             "one",              "two",
      47             :     "three",            "four",             "five",             "six",
      48             :     "seven",            "eight",            "nine",             "colon",
      49             :     "semicolon",        "less",             "equal",            "greater",
      50             : /*32*/  "question",     "at",               "A",                "B",
      51             :     "C",                "D",                "E",                "F",
      52             :     "G",                "H",                "I",                "J",
      53             :     "K",                "L",                "M",                "N",
      54             : /*48*/  "O",            "P",                "Q",                "R",
      55             :     "S",                "T",                "U",                "V",
      56             :     "W",                "X",                "Y",                "Z",
      57             :     "bracketleft",      "backslash",        "bracketright",     "asciicircum",
      58             : /*64*/  "underscore",   "quoteleft",        "a",                "b",
      59             :     "c",                "d",                "e",                "f",
      60             :     "g",                "h",                "i",                "j",
      61             :     "k",                "l",                "m",                "n",
      62             : /*80*/  "o",            "p",                "q",                "r",
      63             :     "s",                "t",                "u",                "v",
      64             :     "w",                "x",                "y",                "z",
      65             :     "braceleft",        "bar",              "braceright",       "asciitilde",
      66             : /*96*/  "exclamdown",   "cent",             "sterlin",          "fraction",
      67             :     "yen",              "florin",           "section",          "currency",
      68             :     "quotesingle",      "quotedblleft",     "guillemotleft",    "guilsinglleft",
      69             :     "guilsinglright",   "fi",               "fl",               "endash",
      70             : /*112*/ "dagger",       "daggerdbl",        "periodcentered",   "paragraph",
      71             :     "bullet",           "quotesinglbase",   "quotedblbase",     "quotedblright",
      72             :     "guillemotright",   "ellipsis",         "perthousand",      "questiondown",
      73             :     "grave",            "acute",            "circumflex",       "tilde",
      74             : /*128*/ "macron",       "breve",            "dotaccent",        "dieresis",
      75             :     "ring",             "cedilla",          "hungarumlaut",     "ogonek",
      76             :     "caron",            "emdash",           "AE",               "ordfeminine",
      77             :     "Lslash",           "Oslash",           "OE",               "ordmasculine",
      78             : /*144*/ "ae",           "dotlessi",         "lslash",           "oslash",
      79             :     "oe",               "germandbls",       "onesuperior",      "logicalnot",
      80             :     "mu",               "trademark",        "Eth",              "onehalf",
      81             :     "plusminus",        "Thorn",            "onequarter",       "divide",
      82             : /*160*/ "brokenbar",    "degree",           "thorn",            "threequarters",
      83             :     "twosuperior",      "registered",       "minus",            "eth",
      84             :     "multiply",         "threesuperior",    "copyright",        "Aacute",
      85             :     "Acircumflex",      "Adieresis",        "Agrave",           "Aring",
      86             : /*176*/ "Atilde",       "Ccedilla",         "Eacute",           "Ecircumflex",
      87             :     "Edieresis",        "Egrave",           "Iacute",           "Icircumflex",
      88             :     "Idieresis",        "Igrave",           "Ntilde",           "Oacute",
      89             :     "Ocircumflex",      "Odieresis",        "Ograve",           "Otilde",
      90             : /*192*/ "Scaron",       "Uacute",           "Ucircumflex",      "Udieresis",
      91             :     "Ugrave",           "Yacute",           "Ydieresis",        "Zcaron",
      92             :     "aacute",           "acircumflex",      "adieresis",        "agrave",
      93             :     "aring",            "atilde",           "ccedilla",         "eacute",
      94             : /*208*/ "ecircumflex",  "edieresis",        "egrave",           "iacute",
      95             :     "icircumflex",      "idieresis",        "igrave",           "ntilde",
      96             :     "oacute",           "ocircumflex",      "odieresis",        "ograve",
      97             :     "otilde",           "scaron",           "uacute",           "ucircumflex",
      98             : /*224*/ "udieresis",    "ugrave",           "yacute",           "ydieresis",
      99             :     "zcaron",           "exclamsmall",      "Hungarumlautsmall","dollaroldstyle",
     100             :     "dollarsuperior",   "ampersandsmall",   "Acutesmall",       "parenleftsuperior",
     101             :     "parenrightsuperior","twodotenleader",  "onedotenleader",   "zerooldstyle",
     102             : /*240*/ "oneoldstyle",  "twooldstyle",      "threeoldstyle",    "fouroldstyle",
     103             :     "fiveoldstyle",     "sixoldstyle",      "sevenoldstyle",    "eightoldstyle",
     104             :     "nineoldstile",     "commasuperior",    "threequartersemdash","periodsuperior",
     105             :     "questionsmall",    "asuperior",        "bsuperior",        "centsuperior",
     106             : /*256*/ "dsuperior",    "esuperior",        "isuperior",        "lsuperior",
     107             :     "msuperior",        "nsuperior",        "osuperior",        "rsuperior",
     108             :     "ssuperior",        "tsuperior",        "ff",               "ffi",
     109             :     "ffl",              "parenleftinferior","parenrightinferior","Circumflexsmall",
     110             : /*272*/ "hyphensuperior","Gravesmall",      "Asmall",           "Bsmall",
     111             :     "Csmall",           "Dsmall",           "Esmall",           "Fsmall",
     112             :     "Gsmall",           "Hsmall",           "Ismall",           "Jsmall",
     113             :     "Ksmall",           "Lsmall",           "Msmall",           "Nsmall",
     114             : /*288*/ "Osmall",       "Psmall",           "Qsmall",           "Rsmall",
     115             :     "Ssmall",           "Tsmall",           "Usmall",           "Vsmall",
     116             :     "Wsmall",           "Xsmall",           "Ysmall",           "Zsmall",
     117             :     "colonmonetary",    "onefitted",        "rupia",            "Tildesmall",
     118             : /*304*/ "exclamdownsmall","centoldstyle",   "Lslashsmall",      "Scaronsmall",
     119             :     "Zcaronsmall",      "Dieresissmall",    "Brevesmall",       "Caronsmall",
     120             :     "Dotaccentsmall",   "Macronsmall",      "figuredash",       "hypheninferior",
     121             :     "Ogoneksmall",      "Ringsmall",        "Cedillasmall",     "questiondownsmall",
     122             : /*320*/ "oneeight",     "threeeights",      "fiveeights",       "seveneights",
     123             :     "onethird",         "twothirds",        "zerosuperior",     "foursuperior",
     124             :     "fivesuperior",     "sixsuperior",      "sevensuperior",    "eightsuperior",
     125             :     "ninesuperior",     "zeroinferior",     "oneinferior",      "twoinferior",
     126             : /*336*/ "threeinferior","fourinferior",     "fiveinferior",     "sixinferior",
     127             :     "seveninferior",    "eightinferior",    "nineinferior",     "centinferior",
     128             :     "dollarinferior",   "periodinferior",   "commainferior",    "Agravesmall",
     129             :     "Aacutesmall",      "Acircumflexsmall", "Atildesmall",      "Adieresissmall",
     130             : /*352*/ "Aringsmall",   "AEsmall",          "Ccedillasmall",    "Egravesmall",
     131             :     "Eacutesmall",      "Ecircumflexsmall", "Edieresissmall",   "Igravesmall",
     132             :     "Iacutesmall",      "Icircumflexsmall", "Idieresissmall",   "Ethsmall",
     133             :     "Ntildesmall",      "Ogravesmall",      "Oacutesmall",      "Ocircumflexsmall",
     134             : /*368*/ "Otildesmall",  "Odieressissmall",  "OEsmall",          "Oslashsmall",
     135             :     "Ugravesmall",      "Uacutesmall",      "Ucircumflexsmall", "Udieresissmall",
     136             :     "Yacutesmall",      "Thornsmall",       "Ydieresissmall",   "001.000",
     137             :     "001.001",          "001.002",          "001.003",          "Black",
     138             : /*384*/ "Bold",         "Book",             "Light",            "Medium",
     139             :     "Regular",          "Roman",            "Semibold"
     140             : };
     141             : 
     142             : // TOP DICT keywords (also covers PRIV DICT keywords)
     143             : static const char* pDictOps[] = {
     144             :     "sVersion",         "sNotice",              "sFullName",        "sFamilyName",
     145             :     "sWeight",          "aFontBBox",            "dBlueValues",      "dOtherBlues",
     146             :     "dFamilyBlues",     "dFamilyOtherBlues",    "nStdHW",           "nStdVW",
     147             :     "xESC",             "nUniqueID",            "aXUID",            "nCharset",
     148             :     "nEncoding",        "nCharStrings",         "PPrivate",         "nSubrs",
     149             :     "nDefaultWidthX",   "nNominalWidthX",       NULL,               NULL,
     150             :     NULL,               NULL,                   NULL,               NULL,
     151             :     "shortint",         "longint",              "BCD",              NULL
     152             : };
     153             : 
     154             : // TOP DICT escapes (also covers PRIV DICT escapes)
     155             : static const char* pDictEscs[] = {
     156             :     "sCopyright",           "bIsFixedPitch",    "nItalicAngle",     "nUnderlinePosition",
     157             :     "nUnderlineThickness",  "nPaintType",       "tCharstringType",  "aFontMatrix",
     158             :     "nStrokeWidth",         "nBlueScale",       "nBlueShift",       "nBlueFuzz",
     159             :     "dStemSnapH",           "dStemSnapV",       "bForceBold",       NULL,
     160             :     NULL,                   "nLanguageGroup",   "nExpansionFactor", "nInitialRandomSeed",
     161             :     "nSyntheticBase",       "sPostScript",      "sBaseFontName",    "dBaseFontBlend",
     162             :     NULL,                   NULL,               NULL,               NULL,
     163             :     NULL,                   NULL,               "rROS",             "nCIDFontVersion",
     164             :     "nCIDFontRevision",     "nCIDFontType",     "nCIDCount",        "nUIDBase",
     165             :     "nFDArray",             "nFDSelect",        "sFontName"
     166             : };
     167             : 
     168             : static const char* pType1Ops[] = {
     169             :     NULL,               "2hstem",           NULL,               "2vstem",
     170             :     "1vmoveto",         "Arlineto",         "1hlineto",         "1vlineto",
     171             :     "Crrcurveto",       "0closepath",       "Lcallsubr",        "0return",
     172             :     "xT1ESC",           "2hsbw",            "0endchar",         NULL,
     173             :     NULL,               NULL,               NULL,               NULL,
     174             :     NULL,               "2rmoveto",         "1hmoveto",         NULL,
     175             :     NULL,               NULL,               NULL,               NULL,
     176             :     NULL,               NULL,               "4vhcurveto",       "4hvcurveto"
     177             : };
     178             : 
     179             : static const char* pT1EscOps[] = {
     180             :     "0dotsection",      "6vstem3",          "6hstem3",          NULL,
     181             :     NULL,               NULL,               "5seac",            "4sbw",
     182             :     NULL,               "1abs",             "2add",             "2sub",
     183             :     "2div",             NULL,               NULL,               NULL,
     184             :     "Gcallothersubr",   "1pop",             NULL,               NULL,
     185             :     NULL,               NULL,               NULL,               NULL,
     186             :     NULL,               NULL,               NULL,               NULL,
     187             :     NULL,               NULL,               NULL,               NULL,
     188             :     NULL,               "2setcurrentpoint"
     189             : };
     190             : 
     191             : struct TYPE1OP
     192             : {
     193             :     enum OPS
     194             :     {
     195             :         HSTEM=1,        VSTEM=3,        VMOVETO=4,      RLINETO=5,
     196             :         HLINETO=6,      VLINETO=7,      RCURVETO=8,     CLOSEPATH=9,
     197             :         CALLSUBR=10,    RETURN=11,      T1ESC=12,       HSBW=13,
     198             :         ENDCHAR=14,     RMOVETO=21,     HMOVETO=22,     VHCURVETO=30,
     199             :         HVCURVETO=31
     200             :     };
     201             : 
     202             :     enum ESCS
     203             :     {
     204             :         DOTSECTION=0,   VSTEM3=1,           HSTEM3=2,   SEAC=6,
     205             :         SBW=7,          ABS=9,              ADD=10,     SUB=11,
     206             :         DIV=12,         CALLOTHERSUBR=16,   POP=17,     SETCURRENTPOINT=33
     207             :     };
     208             : };
     209             : 
     210             : static const char* pType2Ops[] = {
     211             :     NULL,           "hhstem",       NULL,           "vvstem",
     212             :     "mvmoveto",     "Arlineto",     "Ehlineto",     "Evlineto",
     213             :     "Crrcurveto",   NULL,           "Lcallsubr",    "Xreturn",
     214             :     "xT2ESC",       NULL,           "eendchar",     NULL,
     215             :     NULL,           NULL,           "Hhstemhm",     "Khintmask",
     216             :     "Kcntrmask",    "Mrmoveto",     "mhmoveto",     "Vvstemhm",
     217             :     ".rcurveline",  ".rlinecurve",  ".vvcurveto",   ".hhcurveto",
     218             :     ".shortint",    "Gcallgsubr",   ".vhcurveto",   ".hvcurveto"
     219             : };
     220             : 
     221             : static const char* pT2EscOps[] = {
     222             :     NULL,       NULL,       NULL,       "2and",
     223             :     "2or",      "1not",     NULL,       NULL,
     224             :     NULL,       "1abs",     "2add",     "2sub",
     225             :     "2div",     NULL,       "1neg",     "2eq",
     226             :     NULL,       NULL,       "1drop",    NULL,
     227             :     "1put",     "1get",     "4ifelse",  "0random",
     228             :     "2mul",     NULL,       "1sqrt",    "1dup",
     229             :     "2exch",    "Iindex",   "Rroll",    NULL,
     230             :     NULL,       NULL,       "7hflex",   "Fflex",
     231             :     "9hflex1",  "fflex1"
     232             : };
     233             : 
     234             : struct TYPE2OP
     235             : {
     236             :     enum OPS
     237             :     {
     238             :         HSTEM=1,        VSTEM=3,        VMOVETO=4,      RLINETO=5,
     239             :         HLINETO=6,      VLINETO=7,      RCURVETO=8,     CALLSUBR=10,
     240             :         RETURN=11,      T2ESC=12,       ENDCHAR=14,     HSTEMHM=18,
     241             :         HINTMASK=19,    CNTRMASK=20,    RMOVETO=21,     HMOVETO=22,
     242             :         VSTEMHM=23,     RCURVELINE=24,  RLINECURVE=25,  VVCURVETO=26,
     243             :         HHCURVETO=27,   SHORTINT=28,    CALLGSUBR=29,   VHCURVETO=30,
     244             :         HVCURVETO=31
     245             :     };
     246             : 
     247             :     enum ESCS
     248             :     {
     249             :         AND=3,      OR=4,       NOT=5,      ABS=9,
     250             :         ADD=10,     SUB=11,     DIV=12,     NEG=14,
     251             :         EQ=15,      DROP=18,    PUT=20,     GET=21,
     252             :         IFELSE=22,  RANDOM=23,  MUL=24,     SQRT=26,
     253             :         DUP=27,     EXCH=28,    INDEX=29,   ROLL=30,
     254             :         HFLEX=34,   FLEX=35,    HFLEX1=36,  FLEX1=37
     255             :     };
     256             : };
     257             : 
     258           0 : struct CffGlobal
     259             : {
     260             :     explicit CffGlobal();
     261             : 
     262             :     int     mnNameIdxBase;
     263             :     int     mnNameIdxCount;
     264             :     int     mnStringIdxBase;
     265             :     int     mnStringIdxCount;
     266             :     bool    mbCIDFont;
     267             :     int     mnCharStrBase;
     268             :     int     mnCharStrCount;
     269             :     int     mnEncodingBase;
     270             :     int     mnCharsetBase;
     271             :     int     mnGlobalSubrBase;
     272             :     int     mnGlobalSubrCount;
     273             :     int     mnGlobalSubrBias;
     274             :     int     mnFDSelectBase;
     275             :     int     mnFontDictBase;
     276             :     int     mnFDAryCount;
     277             : 
     278             :     ValVector   maFontBBox;
     279             :     ValVector   maFontMatrix;
     280             : 
     281             :     int     mnFontNameSID;
     282             :     int     mnFullNameSID;
     283             :     int     mnFamilyNameSID;
     284             : };
     285             : 
     286           0 : struct CffLocal
     287             : {
     288             :     explicit CffLocal();
     289             : 
     290             :     int     mnPrivDictBase;
     291             :     int     mnPrivDictSize;
     292             :     int     mnLocalSubrOffs;
     293             :     int     mnLocalSubrBase;
     294             :     int     mnLocalSubrCount;
     295             :     int     mnLocalSubrBias;
     296             : 
     297             :     ValType maNominalWidth;
     298             :     ValType maDefaultWidth;
     299             : 
     300             :     // ATM hinting related values
     301             :     ValType     maStemStdHW;
     302             :     ValType     maStemStdVW;
     303             :     ValVector   maStemSnapH;
     304             :     ValVector   maStemSnapV;
     305             :     ValVector   maBlueValues;
     306             :     ValVector   maOtherBlues;
     307             :     ValVector   maFamilyBlues;
     308             :     ValVector   maFamilyOtherBlues;
     309             :     RealType    mfBlueScale;
     310             :     RealType    mfBlueShift;
     311             :     RealType    mfBlueFuzz;
     312             :     RealType    mfExpFactor;
     313             :     int         mnLangGroup;
     314             :     bool        mbForceBold;
     315             : };
     316             : 
     317             : class CffSubsetterContext
     318             : :   private CffGlobal
     319             : {
     320             : public:
     321             :     static const int NMAXSTACK = 48;    // see CFF.appendixB
     322             :     static const int NMAXHINTS = 2*96;  // see CFF.appendixB
     323             :     static const int NMAXTRANS = 32;    // see CFF.appendixB
     324             : public:
     325             :     explicit CffSubsetterContext( const U8* pBasePtr, int nBaseLen);
     326             :     ~CffSubsetterContext( void);
     327             : 
     328             :     void    initialCffRead( void);
     329             :     bool    emitAsType1( class Type1Emitter&,
     330             :                 const sal_GlyphId* pGlyphIds, const U8* pEncoding,
     331             :                 GlyphWidth* pGlyphWidths, int nGlyphCount, FontSubsetInfo& );
     332             : 
     333             :     // used by charstring converter
     334             :     void    setCharStringType( int);
     335             : protected:
     336             :     int     convert2Type1Ops( CffLocal*, const U8* pType2Ops, int nType2Len, U8* pType1Ops);
     337             : private:
     338             :     void    convertOneTypeOp( void);
     339             :     void    convertOneTypeEsc( void);
     340             :     void    callType2Subr( bool bGlobal, int nSubrNumber);
     341           0 :     long    getReadOfs( void) const { return (long)(mpReadPtr - mpBasePtr);}
     342             : 
     343             :     const U8* mpBasePtr;
     344             :     const U8* mpBaseEnd;
     345             : 
     346             :     const U8* mpReadPtr;
     347             :     const U8* mpReadEnd;
     348             : 
     349             :     U8*     mpWritePtr;
     350             :     bool    mbSawError;
     351             :     bool    mbNeedClose;
     352             :     bool    mbIgnoreHints;
     353             :     long    mnCntrMask;
     354             : 
     355             : private:
     356             :     int     seekIndexData( int nIndexBase, int nDataIndex);
     357             :     void    seekIndexEnd( int nIndexBase);
     358             : 
     359             : private:
     360             :     const char**    mpCharStringOps;
     361             :     const char**    mpCharStringEscs;
     362             : 
     363             :     CffLocal    maCffLocal[16];
     364             :     CffLocal*   mpCffLocal;
     365             : 
     366             :     void        readDictOp( void);
     367             :     RealType    readRealVal( void);
     368             :     const char* getString( int nStringID);
     369             :     int         getFDSelect( int nGlyphIndex) const;
     370             :     int         getGlyphSID( int nGlyphIndex) const;
     371             :     const char* getGlyphName( int nGlyphIndex);
     372             : 
     373             :     void    read2push( void);
     374             :     void    writeType1Val( ValType);
     375             :     void    writeTypeOp( int nTypeOp);
     376             :     void    writeTypeEsc( int nTypeOp);
     377             :     void    writeCurveTo( int nStackPos, int nIX1, int nIY1, int nIX2, int nIY2, int nIX3, int nIY3);
     378             :     void    pop2MultiWrite( int nArgsPerTypo, int nTypeOp, int nTypeXor=0);
     379             :     void    popAll2Write( int nTypeOp);
     380             : 
     381             : public: // TODO: is public really needed?
     382             :     // accessing the value stack
     383             :     // TODO: add more checks
     384           0 :     void    push( ValType nVal) { mnValStack[ mnStackIdx++] = nVal;}
     385           0 :     ValType popVal( void) { return ((mnStackIdx>0) ? mnValStack[ --mnStackIdx] : 0);}
     386           0 :     ValType getVal( int nIndex) const { return mnValStack[ nIndex];}
     387             :     int     popInt( void);
     388           0 :     int     size( void) const { return mnStackIdx;}
     389           0 :     void    clear( void) { mnStackIdx = 0;}
     390             : 
     391             :     // accessing the charstring hints
     392             :     void    addHints( bool bVerticalHints);
     393             : 
     394             :     // accessing other charstring specifics
     395           0 :     bool    hasCharWidth( void) const { return (maCharWidth > 0);}
     396           0 :     ValType getCharWidth( void) const { return maCharWidth;}
     397           0 :     void    setNominalWidth( ValType aWidth) { mpCffLocal->maNominalWidth = aWidth;}
     398           0 :     void    setDefaultWidth( ValType aWidth) { mpCffLocal->maDefaultWidth = aWidth;}
     399             :     void    updateWidth( bool bUseFirstVal);
     400             : 
     401             : private:
     402             :     // typeop exceution context
     403             :     int mnStackIdx;
     404             :     ValType mnValStack[ NMAXSTACK+4];
     405             :     ValType mnTransVals[ NMAXTRANS];
     406             : 
     407             :     int mnHintSize;
     408             :     int mnHorzHintSize;
     409             :     ValType mnHintStack[ NMAXHINTS];
     410             : 
     411             :     ValType maCharWidth;
     412             : };
     413             : 
     414           0 : CffSubsetterContext::CffSubsetterContext( const U8* pBasePtr, int nBaseLen)
     415             : :   mpBasePtr( pBasePtr)
     416           0 : ,   mpBaseEnd( pBasePtr+nBaseLen)
     417             : ,   mnStackIdx(0)
     418             : ,   mnHintSize(0)
     419             : ,   mnHorzHintSize(0)
     420           0 : ,   maCharWidth(-1)
     421             : {
     422             : //  setCharStringType( 1);
     423             :     // TODO: new CffLocal[ mnFDAryCount];
     424           0 :     mpCffLocal = &maCffLocal[0];
     425           0 : }
     426             : 
     427           0 : CffSubsetterContext::~CffSubsetterContext( void)
     428             : {
     429             :     // TODO: delete[] maCffLocal;
     430           0 : }
     431             : 
     432           0 : inline int CffSubsetterContext::popInt( void)
     433             : {
     434           0 :     const ValType aVal = popVal();
     435           0 :     const int nInt = static_cast<int>(aVal);
     436             :     assert( nInt == aVal);
     437           0 :     return nInt;
     438             : }
     439             : 
     440           0 : inline void CffSubsetterContext::updateWidth( bool bUseFirstVal)
     441             : {
     442             : #if 1 // TODO: is this still needed?
     443             :     // the first value is not a hint but the charwidth
     444           0 :     if( hasCharWidth())
     445           0 :         return;
     446             : #endif
     447           0 :     if( bUseFirstVal) {
     448           0 :         maCharWidth = mpCffLocal->maNominalWidth + mnValStack[0];
     449             :         // remove bottom stack entry
     450           0 :         --mnStackIdx;
     451           0 :         for( int i = 0; i < mnStackIdx; ++i)
     452           0 :             mnValStack[ i] = mnValStack[ i+1];
     453             :     } else {
     454           0 :         maCharWidth = mpCffLocal->maDefaultWidth;
     455             :     }
     456             : }
     457             : 
     458           0 : void CffSubsetterContext::addHints( bool bVerticalHints)
     459             : {
     460             :     // the first charstring value may a charwidth instead of a charwidth
     461           0 :     updateWidth( (mnStackIdx & 1) != 0);
     462             :     // return early (e.g. no implicit hints for hintmask)
     463           0 :     if( !mnStackIdx)
     464           0 :         return;
     465             : 
     466             :     // copy the remaining values to the hint arrays
     467             :     // assert( (mnStackIdx & 1) == 0); // depends on called subrs
     468           0 :     if( mnStackIdx & 1) --mnStackIdx;//#######
     469             :     // TODO: if( !bSubr) assert( mnStackIdx >= 2);
     470             : 
     471             :     assert( (mnHintSize + mnStackIdx) <= 2*NMAXHINTS);
     472             : 
     473             : #ifdef IGNORE_HINTS
     474             :     mnHintSize += mnStackIdx;
     475             : #else
     476           0 :     ValType nHintOfs = 0;
     477           0 :     for( int i = 0; i < mnStackIdx; ++i) {
     478           0 :         nHintOfs += mnValStack[ i ];
     479           0 :         mnHintStack[ mnHintSize++] = nHintOfs;
     480             :     }
     481             : #endif // IGNORE_HINTS
     482           0 :     if( !bVerticalHints)
     483           0 :         mnHorzHintSize = mnHintSize;
     484             : 
     485             :     // clear all values from the stack
     486           0 :     mnStackIdx = 0;
     487             : }
     488             : 
     489           0 : void CffSubsetterContext::setCharStringType( int nVal)
     490             : {
     491           0 :     switch( nVal) {
     492           0 :         case 1: mpCharStringOps=pType1Ops; mpCharStringEscs=pT1EscOps; break;
     493           0 :         case 2: mpCharStringOps=pType2Ops; mpCharStringEscs=pT2EscOps; break;
     494           0 :         default: fprintf( stderr, "Unknown CharstringType=%d\n",nVal); break;
     495             :     }
     496           0 : }
     497             : 
     498           0 : void CffSubsetterContext::readDictOp( void)
     499             : {
     500           0 :     ValType nVal = 0;
     501           0 :     const U8 c = *mpReadPtr;
     502           0 :     if( c <= 21 ) {
     503           0 :         int nOpId = *(mpReadPtr++);
     504           0 :         const char* pCmdName = 0;
     505           0 :         if( nOpId != 12)
     506           0 :             pCmdName = pDictOps[nOpId];
     507             :         else {
     508           0 :             const U8 nExtId = *(mpReadPtr++);
     509           0 :             if (nExtId < 39)
     510           0 :                pCmdName = pDictEscs[nExtId];
     511           0 :             nOpId = 900 + nExtId;
     512             :         }
     513             : 
     514           0 :         if (!pCmdName)  // skip reserved operators
     515           0 :             return;
     516             : 
     517             :         //TODO: if( nStackIdx > 0)
     518           0 :         int nInt = 0;
     519           0 :         switch( *pCmdName) {
     520           0 :         default: fprintf( stderr, "unsupported DictOp.type=\'%c\'\n", *pCmdName); break;
     521             :         case 'b':   // bool
     522           0 :             nInt = popInt();
     523           0 :             switch( nOpId) {
     524           0 :             case 915: mpCffLocal->mbForceBold = nInt; break;    // "ForceBold"
     525           0 :             default: break; // TODO: handle more boolean dictops?
     526             :             }
     527           0 :             break;
     528             :         case 'n':   // dict-op number
     529           0 :             nVal = popVal();
     530           0 :             nInt = static_cast<int>(nVal);
     531           0 :             switch( nOpId) {
     532           0 :             case  10: mpCffLocal->maStemStdHW = nVal; break;    // "StdHW"
     533           0 :             case  11: mpCffLocal->maStemStdVW = nVal; break;    // "StdVW"
     534           0 :             case  15: mnCharsetBase = nInt; break;              // "charset"
     535           0 :             case  16: mnEncodingBase = nInt; break;             // "nEncoding"
     536           0 :             case  17: mnCharStrBase = nInt; break;              // "nCharStrings"
     537           0 :             case  19: mpCffLocal->mnLocalSubrOffs = nInt; break;// "nSubrs"
     538           0 :             case  20: setDefaultWidth( nVal ); break;           // "defaultWidthX"
     539           0 :             case  21: setNominalWidth( nVal ); break;           // "nominalWidthX"
     540           0 :             case 909: mpCffLocal->mfBlueScale = nVal; break;    // "BlueScale"
     541           0 :             case 910: mpCffLocal->mfBlueShift = nVal; break;    // "BlueShift"
     542           0 :             case 911: mpCffLocal->mfBlueFuzz = nVal; break;     // "BlueFuzz"
     543           0 :             case 912: mpCffLocal->mfExpFactor = nVal; break;    // "ExpansionFactor"
     544           0 :             case 917: mpCffLocal->mnLangGroup = nInt; break;    // "LanguageGroup"
     545           0 :             case 936: mnFontDictBase = nInt; break;             // "nFDArray"
     546           0 :             case 937: mnFDSelectBase = nInt; break;             // "nFDSelect"
     547           0 :             default: break; // TODO: handle more numeric dictops?
     548             :             }
     549           0 :             break;
     550             :         case 'a': { // array
     551           0 :             switch( nOpId) {
     552           0 :             case   5: maFontBBox.clear(); break;     // "FontBBox"
     553           0 :             case 907: maFontMatrix.clear(); break; // "FontMatrix"
     554           0 :             default: break; // TODO: reset other arrays?
     555             :             }
     556           0 :             for( int i = 0; i < size(); ++i ) {
     557           0 :                 nVal = getVal(i);
     558           0 :                 switch( nOpId) {
     559           0 :                 case   5: maFontBBox.push_back( nVal); break;     // "FontBBox"
     560           0 :                 case 907: maFontMatrix.push_back( nVal); break; // "FontMatrix"
     561           0 :                 default: break; // TODO: handle more array dictops?
     562             :                 }
     563             :             }
     564           0 :             clear();
     565           0 :             } break;
     566             :         case 'd': { // delta array
     567           0 :             nVal = 0;
     568           0 :             for( int i = 0; i < size(); ++i ) {
     569           0 :                 nVal += getVal(i);
     570           0 :                 switch( nOpId) {
     571           0 :                 case   6: mpCffLocal->maBlueValues.push_back( nVal); break;     // "BlueValues"
     572           0 :                 case   7: mpCffLocal->maOtherBlues.push_back( nVal); break;     // "OtherBlues"
     573           0 :                 case   8: mpCffLocal->maFamilyBlues.push_back( nVal); break;    // "FamilyBlues"
     574           0 :                 case   9: mpCffLocal->maFamilyOtherBlues.push_back( nVal); break;// "FamilyOtherBlues"
     575           0 :                 case 912: mpCffLocal->maStemSnapH.push_back( nVal); break;      // "StemSnapH"
     576           0 :                 case 913: mpCffLocal->maStemSnapV.push_back( nVal); break;      // "StemSnapV"
     577           0 :                 default: break; // TODO: handle more delta-array dictops?
     578             :                 }
     579             :             }
     580           0 :             clear();
     581           0 :             } break;
     582             :         case 's':   // stringid (SID)
     583           0 :             nInt = popInt();
     584           0 :             switch( nOpId ) {
     585           0 :             case   2: mnFullNameSID = nInt; break;      // "FullName"
     586           0 :             case   3: mnFamilyNameSID = nInt; break;    // "FamilyName"
     587           0 :             case 938: mnFontNameSID = nInt; break;      // "FontName"
     588           0 :             default: break; // TODO: handle more string dictops?
     589             :             }
     590           0 :             break;
     591             :         case 'P':   // private dict
     592           0 :             mpCffLocal->mnPrivDictBase = popInt();
     593           0 :             mpCffLocal->mnPrivDictSize = popInt();
     594           0 :             break;
     595             :         case 'r': { // ROS operands
     596           0 :             int nSid1 = popInt();
     597           0 :             int nSid2 = popInt();
     598             :             (void)nSid1; // TODO: use
     599             :             (void)nSid2; // TODO: use
     600           0 :             nVal = popVal();
     601           0 :             mbCIDFont = true;
     602           0 :             } break;
     603             :         case 't':   // CharstringType
     604           0 :             nInt = popInt();
     605           0 :             setCharStringType( nInt );
     606           0 :             break;
     607             :         }
     608             : 
     609           0 :         return;
     610             :     }
     611             : 
     612           0 :     if( (c >= 32) || (c == 28) ) {
     613             : //      --mpReadPtr;
     614           0 :         read2push();
     615           0 :     } else if( c == 29 ) {      // longint
     616           0 :         ++mpReadPtr;            // skip 29
     617           0 :         int nS32 = mpReadPtr[0] << 24;
     618           0 :         nS32 += mpReadPtr[1] << 16;
     619           0 :         nS32 += mpReadPtr[2] << 8;
     620           0 :         nS32 += mpReadPtr[3] << 0;
     621             :         if( (sizeof(nS32) != 4) && (nS32 & (1U<<31)))
     622             :             nS32 |= (~0U) << 31;    // assuming 2s complement
     623           0 :         mpReadPtr += 4;
     624           0 :         nVal = static_cast<ValType>(nS32);
     625           0 :         push( nVal );
     626           0 :     } else if( c == 30) {       // real number
     627           0 :         ++mpReadPtr; // skip 30
     628           0 :         const RealType fReal = readRealVal();
     629             :         // push value onto stack
     630           0 :         nVal = fReal;
     631           0 :         push( nVal);
     632             :     }
     633             : }
     634             : 
     635           0 : void CffSubsetterContext::read2push()
     636             : {
     637           0 :     ValType aVal = 0;
     638             : 
     639           0 :     const U8*& p = mpReadPtr;
     640           0 :     const U8 c = *p;
     641           0 :     if( c == 28 ) {
     642           0 :         short nS16 = (p[1] << 8) + p[2];
     643             :         if( (sizeof(nS16) != 2) && (nS16 & (1<<15)))
     644             :             nS16 |= (~0U) << 15;    // assuming 2s complement
     645           0 :         aVal = nS16;
     646           0 :         p += 3;
     647           0 :     } else if( c <= 246 ) {     // -107..+107
     648           0 :         aVal = static_cast<ValType>(p[0] - 139);
     649           0 :         p += 1;
     650           0 :     } else if( c <= 250 ) {     // +108..+1131
     651           0 :         aVal = static_cast<ValType>(((p[0] << 8) + p[1]) - 63124);
     652           0 :         p += 2;
     653           0 :     } else if( c <= 254 ) {     // -108..-1131
     654           0 :         aVal = static_cast<ValType>(64148 - ((p[0] << 8) + p[1]));
     655           0 :         p += 2;
     656             :     } else /*if( c == 255)*/ {  // Fixed16.16
     657           0 :         int nS32 = (p[1] << 24) + (p[2] << 16) + (p[3] << 8) + p[4];
     658           0 :         if( (sizeof(nS32) != 2) && (nS32 & (1U<<31)))
     659           0 :             nS32 |= (~0U) << 31;    // assuming 2s complement
     660           0 :         aVal = static_cast<ValType>(nS32 * (1.0 / 0x10000));
     661           0 :         p += 5;
     662             :     }
     663             : 
     664           0 :     push( aVal);
     665           0 : }
     666             : 
     667           0 : void CffSubsetterContext::writeType1Val( ValType aVal)
     668             : {
     669           0 :     U8* pOut = mpWritePtr;
     670             : 
     671           0 :     int nInt = static_cast<int>(aVal);
     672             :     static const int nOutCharstrType = 1;
     673             :     if( (nInt != aVal) && (nOutCharstrType == 2)) {
     674             :         // numtype==255 means int32 for Type1, but 16.16 for Type2 charstrings!!!
     675             :         *(pOut++) = 255;                            // Fixed 16.16
     676             :         *(pOut++) = static_cast<U8>(nInt >> 8);
     677             :         *(pOut++) = static_cast<U8>(nInt);
     678             :         nInt = static_cast<int>(aVal * 0x10000) & 0xFFFF;
     679             :         *(pOut++) = static_cast<U8>(nInt >> 8);
     680             :         *(pOut++) = static_cast<U8>(nInt);
     681           0 :     } else if( (nInt >= -107) && (nInt <= +107)) {
     682           0 :         *(pOut++) = static_cast<U8>(nInt + 139);    // -107..+107
     683           0 :     } else if( (nInt >= -1131) && (nInt <= +1131)) {
     684           0 :         if( nInt >= 0)
     685           0 :             nInt += 63124;                          // +108..+1131
     686             :         else
     687           0 :             nInt = 64148 - nInt;                    // -108..-1131
     688           0 :         *(pOut++) = static_cast<U8>(nInt >> 8);
     689           0 :         *(pOut++) = static_cast<U8>(nInt);
     690             :     } else if( nOutCharstrType == 1) {
     691             :         // numtype==255 means int32 for Type1, but 16.16 for Type2 charstrings!!!
     692           0 :         *(pOut++) = 255;
     693           0 :         *(pOut++) = static_cast<U8>(nInt >> 24);
     694           0 :         *(pOut++) = static_cast<U8>(nInt >> 16);
     695           0 :         *(pOut++) = static_cast<U8>(nInt >> 8);
     696           0 :         *(pOut++) = static_cast<U8>(nInt);
     697             :     }
     698             : 
     699           0 :     mpWritePtr = pOut;
     700           0 : }
     701             : 
     702           0 : inline void CffSubsetterContext::writeTypeOp( int nTypeOp)
     703             : {
     704           0 :     *(mpWritePtr++) = static_cast<U8>(nTypeOp);
     705           0 : }
     706             : 
     707           0 : inline void CffSubsetterContext::writeTypeEsc( int nTypeEsc)
     708             : {
     709           0 :     *(mpWritePtr++) = TYPE1OP::T1ESC;
     710           0 :     *(mpWritePtr++) = static_cast<U8>(nTypeEsc);
     711           0 : }
     712             : 
     713           0 : void CffSubsetterContext::pop2MultiWrite( int nArgsPerTypo, int nTypeOp, int nTypeXor)
     714             : {
     715           0 :     for( int i = 0; i < mnStackIdx;) {
     716           0 :         for( int j = 0; j < nArgsPerTypo; ++j) {
     717           0 :             const ValType aVal = mnValStack[i+j];
     718           0 :             writeType1Val( aVal);
     719             :         }
     720           0 :         i += nArgsPerTypo;
     721           0 :         writeTypeOp( nTypeOp);
     722           0 :         nTypeOp ^= nTypeXor;    // for toggling vlineto/hlineto
     723             :     }
     724           0 :     clear();
     725           0 : }
     726             : 
     727           0 : void CffSubsetterContext::popAll2Write( int nTypeOp)
     728             : {
     729             :     // pop in reverse order, then write
     730           0 :     for( int i = 0; i < mnStackIdx; ++i) {
     731           0 :         const ValType aVal = mnValStack[i];
     732           0 :         writeType1Val( aVal);
     733             :     }
     734           0 :     clear();
     735           0 :     writeTypeOp( nTypeOp);
     736           0 : }
     737             : 
     738           0 : void CffSubsetterContext::writeCurveTo( int nStackPos,
     739             :     int nIX1, int nIY1, int nIX2, int nIY2, int nIX3, int nIY3)
     740             : {
     741             :     // get the values from the stack
     742           0 :     const ValType nDX1 = nIX1 ? mnValStack[ nStackPos+nIX1 ] : 0;
     743           0 :     const ValType nDY1 = nIY1 ? mnValStack[ nStackPos+nIY1 ] : 0;
     744           0 :     const ValType nDX2 = nIX2 ? mnValStack[ nStackPos+nIX2 ] : 0;
     745           0 :     const ValType nDY2 = nIY2 ? mnValStack[ nStackPos+nIY2 ] : 0;
     746           0 :     const ValType nDX3 = nIX3 ? mnValStack[ nStackPos+nIX3 ] : 0;
     747           0 :     const ValType nDY3 = nIY3 ? mnValStack[ nStackPos+nIY3 ] : 0;
     748             : 
     749             :     // emit the curveto operator and operands
     750             :     // TODO: determine the most efficient curveto operator
     751             :     // TODO: depending on type1op or type2op target
     752           0 :     writeType1Val( nDX1 );
     753           0 :     writeType1Val( nDY1 );
     754           0 :     writeType1Val( nDX2 );
     755           0 :     writeType1Val( nDY2 );
     756           0 :     writeType1Val( nDX3 );
     757           0 :     writeType1Val( nDY3 );
     758           0 :     writeTypeOp( TYPE1OP::RCURVETO );
     759           0 : }
     760             : 
     761           0 : void CffSubsetterContext::convertOneTypeOp( void)
     762             : {
     763           0 :     const int nType2Op = *(mpReadPtr++);
     764             : 
     765             :     int i, nInt; // prevent WAE for declarations inside switch cases
     766             :     // convert each T2op
     767           0 :     switch( nType2Op) {
     768             :     case TYPE2OP::T2ESC:
     769           0 :         convertOneTypeEsc();
     770           0 :         break;
     771             :     case TYPE2OP::HSTEM:
     772             :     case TYPE2OP::VSTEM:
     773           0 :         addHints( nType2Op == TYPE2OP::VSTEM );
     774             : #ifndef IGNORE_HINTS
     775           0 :         for( i = 0; i < mnHintSize; i+=2 ) {
     776           0 :             writeType1Val( mnHintStack[i]);
     777           0 :             writeType1Val( mnHintStack[i+1] - mnHintStack[i]);
     778           0 :             writeTypeOp( nType2Op );
     779             :         }
     780             : #endif // IGNORE_HINTS
     781           0 :         break;
     782             :     case TYPE2OP::HSTEMHM:
     783             :     case TYPE2OP::VSTEMHM:
     784           0 :         addHints( nType2Op == TYPE2OP::VSTEMHM);
     785           0 :         break;
     786             :     case TYPE2OP::CNTRMASK:
     787             :         // TODO: replace cntrmask with vstem3/hstem3
     788           0 :         addHints( true);
     789             : #ifdef IGNORE_HINTS
     790             :         mpReadPtr += (mnHintSize + 15) / 16;
     791             :         mbIgnoreHints = true;
     792             : #else
     793             :         {
     794           0 :         U8 nMaskBit = 0;
     795           0 :         U8 nMaskByte = 0;
     796           0 :         for( i = 0; i < mnHintSize; i+=2, nMaskBit>>=1) {
     797           0 :             if( !nMaskBit) {
     798           0 :                 nMaskByte = *(mpReadPtr++);
     799           0 :                 nMaskBit = 0x80;
     800             :             }
     801           0 :             if( !(nMaskByte & nMaskBit))
     802           0 :                 continue;
     803           0 :             if( i >= 8*(int)sizeof(mnCntrMask))
     804           0 :                 mbIgnoreHints = true;
     805           0 :             if( mbIgnoreHints)
     806           0 :                 continue;
     807           0 :             mnCntrMask |= (1U << i);
     808             :         }
     809             :         }
     810             : #endif
     811           0 :         break;
     812             :     case TYPE2OP::HINTMASK:
     813           0 :         addHints( true);
     814             : #ifdef IGNORE_HINTS
     815             :         mpReadPtr += (mnHintSize + 15) / 16;
     816             : #else
     817             :         {
     818           0 :         long nHintMask = 0;
     819           0 :         int nCntrBits[2] = {0,0};
     820           0 :         U8 nMaskBit = 0;
     821           0 :         U8 nMaskByte = 0;
     822           0 :         for( i = 0; i < mnHintSize; i+=2, nMaskBit>>=1) {
     823           0 :             if( !nMaskBit) {
     824           0 :                 nMaskByte = *(mpReadPtr++);
     825           0 :                 nMaskBit = 0x80;
     826             :             }
     827           0 :             if( !(nMaskByte & nMaskBit))
     828           0 :                 continue;
     829           0 :             if( i >= 8*(int)sizeof(nHintMask))
     830           0 :                 mbIgnoreHints = true;
     831           0 :             if( mbIgnoreHints)
     832           0 :                 continue;
     833           0 :             nHintMask |= (1U << i);
     834           0 :             nCntrBits[ i < mnHorzHintSize] += (mnCntrMask >> i) & 1;
     835             :         }
     836             : 
     837           0 :         mbIgnoreHints |= (nCntrBits[0] && (nCntrBits[0] != 3));
     838           0 :         mbIgnoreHints |= (nCntrBits[1] && (nCntrBits[1] != 3));
     839           0 :         if( mbIgnoreHints)
     840           0 :             break;
     841             : 
     842           0 :         for( i = 0; i < mnHintSize; i+=2) {
     843           0 :             if( !(nHintMask & (1U << i)))
     844           0 :                 continue;
     845           0 :             writeType1Val( mnHintStack[i]);
     846           0 :             writeType1Val( mnHintStack[i+1] - mnHintStack[i]);
     847           0 :             const bool bHorz = (i < mnHorzHintSize);
     848           0 :             if( !nCntrBits[ bHorz])
     849           0 :                 writeTypeOp( bHorz ? TYPE1OP::HSTEM : TYPE1OP::VSTEM);
     850           0 :             else if( !--nCntrBits[ bHorz])
     851           0 :                 writeTypeEsc( bHorz ? TYPE1OP::HSTEM3 : TYPE1OP::VSTEM3);
     852             :         }
     853             :         }
     854             : #endif
     855           0 :         break;
     856             :     case TYPE2OP::CALLSUBR:
     857             :     case TYPE2OP::CALLGSUBR:
     858             :         {
     859           0 :         nInt = popInt();
     860           0 :         const bool bGlobal = (nType2Op == TYPE2OP::CALLGSUBR);
     861           0 :         callType2Subr( bGlobal, nInt);
     862             :         }
     863           0 :         break;
     864             :     case TYPE2OP::RETURN:
     865             :         // TODO: check that we are in a subroutine
     866           0 :         return;
     867             :     case TYPE2OP::VMOVETO:
     868             :     case TYPE2OP::HMOVETO:
     869           0 :         if( mbNeedClose)
     870           0 :             writeTypeOp( TYPE1OP::CLOSEPATH);
     871             :         else
     872           0 :             updateWidth( size() > 1);
     873           0 :         mbNeedClose = true;
     874           0 :         pop2MultiWrite( 1, nType2Op);
     875           0 :         break;
     876             :     case TYPE2OP::VLINETO:
     877             :     case TYPE2OP::HLINETO:
     878             :         pop2MultiWrite( 1, nType2Op,
     879           0 :             TYPE1OP::VLINETO ^ TYPE1OP::HLINETO);
     880           0 :         break;
     881             :     case TYPE2OP::RMOVETO:
     882             :         // TODO: convert rmoveto to vlineto/hlineto if possible
     883           0 :         if( mbNeedClose)
     884           0 :             writeTypeOp( TYPE1OP::CLOSEPATH);
     885             :         else
     886           0 :             updateWidth( size() > 2);
     887           0 :         mbNeedClose = true;
     888           0 :         pop2MultiWrite( 2, nType2Op);
     889           0 :         break;
     890             :     case TYPE2OP::RLINETO:
     891             :         // TODO: convert rlineto to vlineto/hlineto if possible
     892           0 :         pop2MultiWrite( 2, nType2Op);
     893           0 :         break;
     894             :     case TYPE2OP::RCURVETO:
     895             :         // TODO: convert rcurveto to vh/hv/hh/vv-curveto if possible
     896           0 :         pop2MultiWrite( 6, nType2Op);
     897           0 :         break;
     898             :     case TYPE2OP::RCURVELINE:
     899           0 :         i = 0;
     900           0 :         while( (i += 6) <= mnStackIdx)
     901           0 :             writeCurveTo( i, -6, -5, -4, -3, -2, -1 );
     902           0 :         i -= 6;
     903           0 :         while( (i += 2) <= mnStackIdx) {
     904           0 :             writeType1Val( mnValStack[i-2]);
     905           0 :             writeType1Val( mnValStack[i-1]);
     906           0 :             writeTypeOp( TYPE2OP::RLINETO);
     907             :         }
     908           0 :         clear();
     909           0 :         break;
     910             :     case TYPE2OP::RLINECURVE:
     911           0 :         i = 0;
     912           0 :         while( (i += 2) <= mnStackIdx-6) {
     913           0 :             writeType1Val( mnValStack[i-2]);
     914           0 :             writeType1Val( mnValStack[i-1]);
     915           0 :             writeTypeOp( TYPE2OP::RLINETO);
     916             :         }
     917           0 :         i -= 2;
     918           0 :         while( (i += 6) <= mnStackIdx)
     919           0 :             writeCurveTo( i, -6, -5, -4, -3, -2, -1 );
     920           0 :         clear();
     921           0 :         break;
     922             :     case TYPE2OP::VHCURVETO:
     923             :     case TYPE2OP::HVCURVETO:
     924             :         {
     925           0 :         bool bVert = (nType2Op == TYPE2OP::VHCURVETO);
     926           0 :         i = 0;
     927           0 :         nInt = 0;
     928           0 :         if( mnStackIdx & 1 )
     929           0 :             nInt = static_cast<int>(mnValStack[ --mnStackIdx ]);
     930           0 :         while( (i += 4) <= mnStackIdx) {
     931             :             // TODO: use writeCurveTo()
     932           0 :             if( bVert ) writeType1Val( 0 );
     933           0 :             writeType1Val( mnValStack[i-4] );
     934           0 :             if( !bVert ) writeType1Val( 0);
     935           0 :             writeType1Val( mnValStack[i-3] );
     936           0 :             writeType1Val( mnValStack[i-2] );
     937           0 :             if( !bVert ) writeType1Val( static_cast<ValType>((i==mnStackIdx) ? nInt : 0) );
     938           0 :             writeType1Val( mnValStack[i-1] );
     939           0 :             if( bVert ) writeType1Val( static_cast<ValType>((i==mnStackIdx) ? nInt : 0) );
     940           0 :             bVert = !bVert;
     941           0 :             writeTypeOp( TYPE2OP::RCURVETO);
     942             :         }
     943             :         }
     944           0 :         clear();
     945           0 :         break;
     946             :     case TYPE2OP::HHCURVETO:
     947           0 :         i = (mnStackIdx & 1);
     948           0 :         while( (i += 4) <= mnStackIdx) {
     949           0 :             if( i != 5)
     950           0 :                 writeCurveTo( i, -4,  0, -3, -2, -1, 0);
     951             :             else
     952           0 :                 writeCurveTo( i, -4, -5, -3, -2, -1, 0);
     953             :         }
     954           0 :         clear();
     955           0 :         break;
     956             :     case TYPE2OP::VVCURVETO:
     957           0 :         i = (mnStackIdx & 1);
     958           0 :         while( (i += 4) <= mnStackIdx) {
     959           0 :             if( i != 5)
     960           0 :                 writeCurveTo( i,  0, -4, -3, -2, 0, -1);
     961             :             else
     962           0 :                 writeCurveTo( i, -5, -4, -3, -2, 0, -1);
     963             :         }
     964           0 :         clear();
     965           0 :         break;
     966             :     case TYPE2OP::ENDCHAR:
     967           0 :         if( mbNeedClose)
     968           0 :             writeTypeOp( TYPE1OP::CLOSEPATH);
     969             :         else
     970           0 :             updateWidth( size() >= 1);
     971             :         // mbNeedClose = true;
     972           0 :         writeTypeOp( TYPE1OP::ENDCHAR);
     973           0 :         break;
     974             :     default:
     975           0 :         if( ((nType2Op >= 32) && (nType2Op <= 255)) || (nType2Op == 28)) {
     976           0 :             --mpReadPtr;
     977           0 :             read2push();
     978             :         } else {
     979           0 :             popAll2Write( nType2Op);
     980             :             assert( false); // TODO?
     981             :         }
     982           0 :         break;
     983             :     }
     984             : }
     985             : 
     986           0 : void CffSubsetterContext::convertOneTypeEsc( void)
     987             : {
     988           0 :     const int nType2Esc = *(mpReadPtr++);
     989           0 :     ValType* pTop = &mnValStack[ mnStackIdx-1];
     990             :     // convert each T2op
     991           0 :     switch( nType2Esc) {
     992             :     case TYPE2OP::AND:
     993             :         assert( mnStackIdx >= 2 );
     994           0 :         pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) & static_cast<int>(pTop[-1]));
     995           0 :         --mnStackIdx;
     996           0 :         break;
     997             :     case TYPE2OP::OR:
     998             :         assert( mnStackIdx >= 2 );
     999           0 :         pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) | static_cast<int>(pTop[-1]));
    1000           0 :         --mnStackIdx;
    1001           0 :         break;
    1002             :     case TYPE2OP::NOT:
    1003             :         assert( mnStackIdx >= 1 );
    1004           0 :         pTop[0] = ValType(pTop[0] == 0);
    1005           0 :         break;
    1006             :     case TYPE2OP::ABS:
    1007             :         assert( mnStackIdx >= 1 );
    1008           0 :         if( pTop[0] >= 0)
    1009           0 :             break;
    1010             :         // fall through
    1011             :     case TYPE2OP::NEG:
    1012             :         assert( mnStackIdx >= 1 );
    1013           0 :         pTop[0] = -pTop[0];
    1014           0 :         break;
    1015             :     case TYPE2OP::ADD:
    1016             :         assert( mnStackIdx >= 2 );
    1017           0 :         pTop[0] += pTop[-1];
    1018           0 :         --mnStackIdx;
    1019           0 :         break;
    1020             :     case TYPE2OP::SUB:
    1021             :         assert( mnStackIdx >= 2 );
    1022           0 :         pTop[0] -= pTop[-1];
    1023           0 :         --mnStackIdx;
    1024           0 :         break;
    1025             :     case TYPE2OP::MUL:
    1026             :         assert( mnStackIdx >= 2 );
    1027           0 :         if( pTop[-1])
    1028           0 :             pTop[0] *= pTop[-1];
    1029           0 :         --mnStackIdx;
    1030           0 :         break;
    1031             :     case TYPE2OP::DIV:
    1032             :         assert( mnStackIdx >= 2 );
    1033           0 :         if( pTop[-1])
    1034           0 :             pTop[0] /= pTop[-1];
    1035           0 :         --mnStackIdx;
    1036           0 :         break;
    1037             :     case TYPE2OP::EQ:
    1038             :         assert( mnStackIdx >= 2 );
    1039           0 :         pTop[0] = ValType(pTop[0] == pTop[-1]);
    1040           0 :         --mnStackIdx;
    1041           0 :         break;
    1042             :     case TYPE2OP::DROP:
    1043             :         assert( mnStackIdx >= 1 );
    1044           0 :         --mnStackIdx;
    1045           0 :         break;
    1046             :     case TYPE2OP::PUT: {
    1047             :         assert( mnStackIdx >= 2 );
    1048           0 :         const int nIdx = static_cast<int>(pTop[0]);
    1049             :         assert( nIdx >= 0 );
    1050             :         assert( nIdx < NMAXTRANS );
    1051           0 :         mnTransVals[ nIdx] = pTop[-1];
    1052           0 :         mnStackIdx -= 2;
    1053           0 :         break;
    1054             :         }
    1055             :     case TYPE2OP::GET: {
    1056             :         assert( mnStackIdx >= 1 );
    1057           0 :         const int nIdx = static_cast<int>(pTop[0]);
    1058             :         assert( nIdx >= 0 );
    1059             :         assert( nIdx < NMAXTRANS );
    1060           0 :         pTop[0] = mnTransVals[ nIdx ];
    1061           0 :         break;
    1062             :         }
    1063             :     case TYPE2OP::IFELSE: {
    1064             :         assert( mnStackIdx >= 4 );
    1065           0 :         if( pTop[-1] > pTop[0] )
    1066           0 :             pTop[-3] = pTop[-2];
    1067           0 :         mnStackIdx -= 3;
    1068           0 :         break;
    1069             :         }
    1070             :     case TYPE2OP::RANDOM:
    1071           0 :         pTop[+1] = 1234; // TODO
    1072           0 :         ++mnStackIdx;
    1073           0 :         break;
    1074             :     case TYPE2OP::SQRT:
    1075             :         // TODO: implement
    1076           0 :         break;
    1077             :     case TYPE2OP::DUP:
    1078             :         assert( mnStackIdx >= 1 );
    1079           0 :         pTop[+1] = pTop[0];
    1080           0 :         ++mnStackIdx;
    1081           0 :         break;
    1082             :     case TYPE2OP::EXCH: {
    1083             :         assert( mnStackIdx >= 2 );
    1084           0 :         const ValType nVal = pTop[0];
    1085           0 :         pTop[0] = pTop[-1];
    1086           0 :         pTop[-1] = nVal;
    1087           0 :         break;
    1088             :         }
    1089             :     case TYPE2OP::INDEX: {
    1090             :         assert( mnStackIdx >= 1 );
    1091           0 :         const int nVal = static_cast<int>(pTop[0]);
    1092             :         assert( nVal >= 0 );
    1093             :         assert( nVal < mnStackIdx-1 );
    1094           0 :         pTop[0] = pTop[-1-nVal];
    1095           0 :         break;
    1096             :         }
    1097             :     case TYPE2OP::ROLL: {
    1098             :         assert( mnStackIdx >= 1 );
    1099           0 :         const int nNum = static_cast<int>(pTop[0]);
    1100             :         assert( nNum >= 0);
    1101             :         assert( nNum < mnStackIdx-2 );
    1102             :         (void)nNum; // TODO: implement
    1103           0 :         const int nOfs = static_cast<int>(pTop[-1]);
    1104           0 :         mnStackIdx -= 2;
    1105             :         (void)nOfs;// TODO: implement
    1106           0 :         break;
    1107             :         }
    1108             :     case TYPE2OP::HFLEX1: {
    1109             :             assert( mnStackIdx == 9);
    1110             : 
    1111           0 :             writeCurveTo( mnStackIdx, -9, -8, -7, -6, -5,  0);
    1112           0 :             writeCurveTo( mnStackIdx, -4,  0, -3, -2, -1,  0);
    1113             :         // TODO: emulate hflex1 using othersubr call
    1114             : 
    1115           0 :             mnStackIdx -= 9;
    1116             :         }
    1117           0 :         break;
    1118             :     case TYPE2OP::HFLEX: {
    1119             :             assert( mnStackIdx == 7);
    1120           0 :             ValType* pX = &mnValStack[ mnStackIdx];
    1121             : 
    1122           0 :             pX[+1] = -pX[-5]; // temp: +dy5==-dy2
    1123           0 :             writeCurveTo( mnStackIdx, -7,  0, -6, -5, -4,  0);
    1124           0 :             writeCurveTo( mnStackIdx, -3,  0, -2, +1, -1,  0);
    1125             :         // TODO: emulate hflex using othersubr call
    1126             : 
    1127           0 :             mnStackIdx -= 7;
    1128             :         }
    1129           0 :         break;
    1130             :     case TYPE2OP::FLEX: {
    1131             :             assert( mnStackIdx == 13 );
    1132           0 :             writeCurveTo( mnStackIdx, -13, -12, -11, -10, -9, -8 );
    1133           0 :             writeCurveTo( mnStackIdx,  -7,  -6,  -5,  -4, -3, -2 );
    1134           0 :             const ValType nFlexDepth =  mnValStack[ mnStackIdx-1 ];
    1135             :             (void)nFlexDepth; // ignoring nFlexDepth
    1136           0 :             mnStackIdx -= 13;
    1137             :         }
    1138           0 :         break;
    1139             :     case TYPE2OP::FLEX1: {
    1140             :             assert( mnStackIdx == 11 );
    1141             :             // write the first part of the flex1-hinted curve
    1142           0 :             writeCurveTo( mnStackIdx, -11, -10, -9, -8, -7, -6 );
    1143             : 
    1144             :             // determine if nD6 is horizontal or vertical
    1145           0 :             const int i = mnStackIdx;
    1146           0 :             ValType nDeltaX = mnValStack[i-11] + mnValStack[i-9] + mnValStack[i-7] + mnValStack[i-5] + mnValStack[i-3];
    1147           0 :             if( nDeltaX < 0 ) nDeltaX = -nDeltaX;
    1148           0 :             ValType nDeltaY = mnValStack[i-10] + mnValStack[i-8] + mnValStack[i-6] + mnValStack[i-4] + mnValStack[i-2];
    1149           0 :             if( nDeltaY < 0 ) nDeltaY = -nDeltaY;
    1150           0 :             const bool bVertD6 = (nDeltaY > nDeltaX);
    1151             : 
    1152             :             // write the second part of the flex1-hinted curve
    1153           0 :             if( !bVertD6 )
    1154           0 :                 writeCurveTo( mnStackIdx, -5, -4, -3, -2, -1, 0);
    1155             :             else
    1156           0 :                 writeCurveTo( mnStackIdx, -5, -4, -3, -2, 0, -1);
    1157           0 :             mnStackIdx -= 11;
    1158             :         }
    1159           0 :         break;
    1160             :     default:
    1161           0 :         fprintf( stderr,"unhandled type2esc %d\n", nType2Esc);
    1162             :         assert( false);
    1163           0 :         break;
    1164             :     }
    1165           0 : }
    1166             : 
    1167           0 : void CffSubsetterContext::callType2Subr( bool bGlobal, int nSubrNumber)
    1168             : {
    1169           0 :     const U8* const pOldReadPtr = mpReadPtr;
    1170           0 :     const U8* const pOldReadEnd = mpReadEnd;
    1171             : 
    1172           0 :     if( bGlobal ) {
    1173           0 :         nSubrNumber += mnGlobalSubrBias;
    1174           0 :         seekIndexData( mnGlobalSubrBase, nSubrNumber);
    1175             :     } else {
    1176           0 :         nSubrNumber += mpCffLocal->mnLocalSubrBias;
    1177           0 :         seekIndexData( mpCffLocal->mnLocalSubrBase, nSubrNumber);
    1178             :     }
    1179             : 
    1180           0 :     while( mpReadPtr < mpReadEnd)
    1181           0 :         convertOneTypeOp();
    1182             : 
    1183           0 :     mpReadPtr = pOldReadPtr;
    1184           0 :     mpReadEnd = pOldReadEnd;
    1185           0 : }
    1186             : 
    1187             : static const int MAX_T1OPS_SIZE = 81920; // TODO: use dynamic value
    1188             : 
    1189           0 : int CffSubsetterContext::convert2Type1Ops( CffLocal* pCffLocal, const U8* const pT2Ops, int nT2Len, U8* const pT1Ops)
    1190             : {
    1191           0 :     mpCffLocal = pCffLocal;
    1192             : 
    1193             :     // prepare the charstring conversion
    1194           0 :     mpWritePtr = pT1Ops;
    1195             : #if 1   // TODO: update caller
    1196             :     U8 aType1Ops[ MAX_T1OPS_SIZE];
    1197           0 :     if( !pT1Ops)
    1198           0 :         mpWritePtr = aType1Ops;
    1199           0 :     *const_cast<U8**>(&pT1Ops) = mpWritePtr;
    1200             : #else
    1201             :     assert( pT1Ops);
    1202             : #endif
    1203             : 
    1204             :     // prepend random seed for T1crypt
    1205           0 :     *(mpWritePtr++) = 0x48;
    1206           0 :     *(mpWritePtr++) = 0x44;
    1207           0 :     *(mpWritePtr++) = 0x55;
    1208           0 :     *(mpWritePtr++) = ' ';
    1209             : #if 1 // convert the Type2 charstring to Type1
    1210           0 :     mpReadPtr = pT2Ops;
    1211           0 :     mpReadEnd = pT2Ops + nT2Len;
    1212             :     // prepend "hsbw" or "sbw"
    1213             :     // TODO: only emit hsbw when charwidth is known
    1214             :     // TODO: remove charwidth from T2 stack
    1215           0 :     writeType1Val( 0); // TODO: aSubsetterContext.getLeftSideBearing();
    1216           0 :     writeType1Val( 1000/*###getCharWidth()###*/);
    1217           0 :     writeTypeOp( TYPE1OP::HSBW);
    1218           0 : mbSawError = false;
    1219           0 : mbNeedClose = false;
    1220           0 : mbIgnoreHints = false;
    1221           0 : mnHintSize=mnHorzHintSize=mnStackIdx=0; maCharWidth=-1;//#######
    1222           0 : mnCntrMask = 0;
    1223           0 :     while( mpReadPtr < mpReadEnd)
    1224           0 :         convertOneTypeOp();
    1225             : //  if( bActivePath)
    1226             : //      writeTypeOp( TYPE1OP::CLOSEPATH);
    1227             : //  if( bSubRoutine)
    1228             : //      writeTypeOp( TYPE1OP::RETURN);
    1229           0 : if( mbSawError) {
    1230           0 :     mpWritePtr = pT1Ops+4;
    1231             :      // create an "idiotproof" charstring
    1232           0 :     writeType1Val( 0);
    1233           0 :     writeType1Val( 800);
    1234           0 :     writeTypeOp( TYPE1OP::HSBW);
    1235           0 :     writeType1Val( 50);
    1236           0 :     writeTypeOp( TYPE1OP::HMOVETO);
    1237           0 :     writeType1Val( 650);
    1238           0 :     writeType1Val( 100);
    1239           0 :     writeTypeOp( TYPE1OP::RLINETO);
    1240           0 :     writeType1Val( -350);
    1241           0 :     writeType1Val( 700);
    1242           0 :     writeTypeOp( TYPE1OP::RLINETO);
    1243           0 :     writeTypeOp( TYPE1OP::CLOSEPATH);
    1244           0 :     writeTypeOp( TYPE1OP::ENDCHAR);
    1245             : }
    1246             : #else // useful for manually encoding charstrings
    1247             :     mpWritePtr = pT1Ops;
    1248             :     mpWritePtr += sprintf( (char*)mpWritePtr, "OOo_\x8b\x8c\x0c\x10\x0b");
    1249             : #endif
    1250           0 :     const int nType1Len = mpWritePtr - pT1Ops;
    1251             : 
    1252             :     // encrypt the Type1 charstring
    1253           0 :     int nRDCryptR = 4330; // TODO: mnRDCryptSeed;
    1254           0 :     for( U8* p = pT1Ops; p < mpWritePtr; ++p) {
    1255           0 :         *p ^= (nRDCryptR >> 8);
    1256           0 :         nRDCryptR = (*(U8*)p + nRDCryptR) * 52845 + 22719;
    1257             :     }
    1258             : 
    1259           0 :     return nType1Len;
    1260             : }
    1261             : 
    1262           0 : RealType CffSubsetterContext::readRealVal()
    1263             : {
    1264             :     // TODO: more thorough number validity test
    1265           0 :     bool bComma = false;
    1266           0 :     int nExpVal = 0;
    1267           0 :     int nExpSign = 0;
    1268           0 :     S64 nNumber = 0;
    1269           0 :     RealType fReal = +1.0;
    1270             :     for(;;){
    1271           0 :         const U8 c = *(mpReadPtr++); // read nibbles
    1272             :         // parse high nibble
    1273           0 :         const U8 nH = c >> 4U;
    1274           0 :         if( nH <= 9) {
    1275           0 :             nNumber = nNumber * 10 + nH;
    1276           0 :             --nExpVal;
    1277           0 :         } else if( nH == 10) {  // comma
    1278           0 :             nExpVal = 0;
    1279           0 :             bComma = true;
    1280           0 :         } else if( nH == 11) {  // +exp
    1281           0 :             fReal *= nNumber;
    1282           0 :             nExpSign = +1;
    1283           0 :             nNumber = 0;
    1284           0 :         } else if( nH == 12) {  // -exp
    1285           0 :             fReal *= nNumber;
    1286           0 :             nExpSign = -1;
    1287           0 :             nNumber = 0;
    1288           0 :         } else if( nH == 13) {  // reserved
    1289             :             // TODO: ignore or error?
    1290           0 :         } else if( nH == 14)    // minus
    1291           0 :             fReal = -fReal;
    1292           0 :         else if( nH == 15)  // end
    1293           0 :             break;
    1294             :         // parse low nibble
    1295           0 :         const U8 nL = c & 0x0F;
    1296           0 :         if( nL <= 9) {
    1297           0 :             nNumber = nNumber * 10 + nL;
    1298           0 :             --nExpVal;
    1299           0 :         } else if( nL == 10) {  // comma
    1300           0 :             nExpVal = 0;
    1301           0 :             bComma = true;
    1302           0 :         } else if( nL == 11) {  // +exp
    1303           0 :             fReal *= nNumber;
    1304           0 :             nNumber = 0;
    1305           0 :             nExpSign = +1;
    1306           0 :         } else if( nL == 12) {  // -exp
    1307           0 :             fReal *= nNumber;
    1308           0 :             nNumber = 0;
    1309           0 :             nExpSign = -1;
    1310           0 :         } else if( nL == 13) {  // reserved
    1311             :             // TODO: ignore or error?
    1312           0 :         } else if( nL == 14)    // minus
    1313           0 :             fReal = -fReal;
    1314           0 :         else if( nL == 15)  // end
    1315           0 :             break;
    1316           0 :     }
    1317             : 
    1318             :     // merge exponents
    1319           0 :     if( !bComma)
    1320           0 :         nExpVal = 0;
    1321           0 :     if( !nExpSign) { fReal *= nNumber;}
    1322           0 :     else if( nExpSign > 0) { nExpVal += static_cast<int>(nNumber);}
    1323           0 :     else if( nExpSign < 0) { nExpVal -= static_cast<int>(nNumber);}
    1324             : 
    1325             :     // apply exponents
    1326           0 :     if( !nExpVal) { /*nothing to apply*/}
    1327           0 :     else if( nExpVal > 0) { while( --nExpVal >= 0) fReal *= 10.0;}
    1328           0 :     else if( nExpVal < 0) { while( ++nExpVal <= 0) fReal /= 10.0;}
    1329           0 :     return fReal;
    1330             : }
    1331             : 
    1332             : // prepare to access an element inside a CFF/CID index table
    1333           0 : int CffSubsetterContext::seekIndexData( int nIndexBase, int nDataIndex)
    1334             : {
    1335             :     assert( (nIndexBase > 0) && (mpBasePtr + nIndexBase + 3 <= mpBaseEnd));
    1336           0 :     if( nDataIndex < 0)
    1337           0 :         return -1;
    1338           0 :     mpReadPtr = mpBasePtr + nIndexBase;
    1339           0 :     const int nDataCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1340           0 :     if( nDataIndex >= nDataCount)
    1341           0 :         return -1;
    1342           0 :     const int nDataOfsSz = mpReadPtr[2];
    1343           0 :     mpReadPtr += 3 + (nDataOfsSz * nDataIndex);
    1344           0 :     int nOfs1 = 0;
    1345           0 :     switch( nDataOfsSz) {
    1346           0 :         default: fprintf( stderr, "\tINVALID nDataOfsSz=%d\n\n", nDataOfsSz); return -1;
    1347           0 :         case 1: nOfs1 = mpReadPtr[0]; break;
    1348           0 :         case 2: nOfs1 = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
    1349           0 :         case 3: nOfs1 = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2]; break;
    1350           0 :         case 4: nOfs1 = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
    1351             :     }
    1352           0 :     mpReadPtr += nDataOfsSz;
    1353             : 
    1354           0 :     int nOfs2 = 0;
    1355           0 :     switch( nDataOfsSz) {
    1356           0 :         case 1: nOfs2 = mpReadPtr[0]; break;
    1357           0 :         case 2: nOfs2 = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
    1358           0 :         case 3: nOfs2 = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2]; break;
    1359           0 :         case 4: nOfs2 = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
    1360             :     }
    1361             : 
    1362           0 :     mpReadPtr = mpBasePtr + (nIndexBase + 2) + nDataOfsSz * (nDataCount + 1) + nOfs1;
    1363           0 :     mpReadEnd = mpReadPtr + (nOfs2 - nOfs1);
    1364             :     assert( nOfs1 >= 0);
    1365             :     assert( nOfs2 >= nOfs1);
    1366             :     assert( mpReadPtr <= mpBaseEnd);
    1367             :     assert( mpReadEnd <= mpBaseEnd);
    1368           0 :     return (nOfs2 - nOfs1);
    1369             : }
    1370             : 
    1371             : // skip over a CFF/CID index table
    1372           0 : void CffSubsetterContext::seekIndexEnd( int nIndexBase)
    1373             : {
    1374             :     assert( (nIndexBase > 0) && (mpBasePtr + nIndexBase + 3 <= mpBaseEnd));
    1375           0 :     mpReadPtr = mpBasePtr + nIndexBase;
    1376           0 :     const int nDataCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1377           0 :     const int nDataOfsSz = mpReadPtr[2];
    1378           0 :     mpReadPtr += 3 + nDataOfsSz * nDataCount;
    1379             :     assert( mpReadPtr <= mpBaseEnd);
    1380           0 :     int nEndOfs = 0;
    1381           0 :     switch( nDataOfsSz) {
    1382           0 :         default: fprintf( stderr, "\tINVALID nDataOfsSz=%d\n\n", nDataOfsSz); return;
    1383           0 :         case 1: nEndOfs = mpReadPtr[0]; break;
    1384           0 :         case 2: nEndOfs = (mpReadPtr[0]<<8) + mpReadPtr[1]; break;
    1385           0 :         case 3: nEndOfs = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2];break;
    1386           0 :         case 4: nEndOfs = (mpReadPtr[0]<<24) + (mpReadPtr[1]<<16) + (mpReadPtr[2]<<8) + mpReadPtr[3]; break;
    1387             :     }
    1388           0 :     mpReadPtr += nDataOfsSz;
    1389           0 :     mpReadPtr += nEndOfs - 1;
    1390           0 :     mpReadEnd = mpBaseEnd;
    1391             :     assert( nEndOfs >= 0);
    1392             :     assert( mpReadEnd <= mpBaseEnd);
    1393             : }
    1394             : 
    1395             : // initialize FONTDICT specific values
    1396           0 : CffLocal::CffLocal( void)
    1397             : :   mnPrivDictBase( 0)
    1398             : ,   mnPrivDictSize( 0)
    1399             : ,   mnLocalSubrOffs( 0)
    1400             : ,   mnLocalSubrBase( 0)
    1401             : ,   mnLocalSubrCount( 0)
    1402             : ,   mnLocalSubrBias( 0)
    1403             : ,   maNominalWidth( 0)
    1404             : ,   maDefaultWidth( 0)
    1405             : ,   maStemStdHW( 0)
    1406             : ,   maStemStdVW( 0)
    1407             : ,   mfBlueScale( 0.0)
    1408             : ,   mfBlueShift( 0.0)
    1409             : ,   mfBlueFuzz( 0.0)
    1410             : ,   mfExpFactor( 0.0)
    1411             : ,   mnLangGroup( 0)
    1412           0 : ,   mbForceBold( false)
    1413             : {
    1414           0 :     maStemSnapH.clear();
    1415           0 :     maStemSnapV.clear();
    1416           0 :     maBlueValues.clear();
    1417           0 :     maOtherBlues.clear();
    1418           0 :     maFamilyBlues.clear();
    1419           0 :     maFamilyOtherBlues.clear();
    1420           0 : }
    1421             : 
    1422           0 : CffGlobal::CffGlobal( void)
    1423             : :   mnNameIdxBase( 0)
    1424             : ,   mnNameIdxCount( 0)
    1425             : ,   mnStringIdxBase( 0)
    1426             : ,   mnStringIdxCount( 0)
    1427             : ,   mbCIDFont( false)
    1428             : ,   mnCharStrBase( 0)
    1429             : ,   mnCharStrCount( 0)
    1430             : ,   mnEncodingBase( 0)
    1431             : ,   mnCharsetBase( 0)
    1432             : ,   mnGlobalSubrBase( 0)
    1433             : ,   mnGlobalSubrCount( 0)
    1434             : ,   mnGlobalSubrBias( 0)
    1435             : ,   mnFDSelectBase( 0)
    1436             : ,   mnFontDictBase( 0)
    1437             : ,   mnFDAryCount( 1)
    1438             : ,   mnFontNameSID( 0)
    1439             : ,   mnFullNameSID( 0)
    1440           0 : ,   mnFamilyNameSID( 0)
    1441             : {
    1442           0 :     maFontBBox.clear();
    1443             :     // TODO; maFontMatrix.clear();
    1444           0 : }
    1445             : 
    1446           0 : void CffSubsetterContext::initialCffRead( void)
    1447             : {
    1448             :     // get the CFFHeader
    1449           0 :     mpReadPtr = mpBasePtr;
    1450           0 :     const U8 nVerMajor = *(mpReadPtr++);
    1451           0 :     const U8 nVerMinor = *(mpReadPtr++);
    1452           0 :     const U8 nHeaderSize = *(mpReadPtr++);
    1453           0 :     const U8 nOffsetSize = *(mpReadPtr++);
    1454             :     // TODO: is the version number useful for anything else?
    1455             :     assert( (nVerMajor == 1) && (nVerMinor == 0));
    1456             :     (void)(nVerMajor + nVerMinor + nOffsetSize); // avoid compiler warnings
    1457             : 
    1458             :     // prepare access to the NameIndex
    1459           0 :     mnNameIdxBase = nHeaderSize;
    1460           0 :     mpReadPtr = mpBasePtr + nHeaderSize;
    1461           0 :     mnNameIdxCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1462           0 :     seekIndexEnd( mnNameIdxBase);
    1463             : 
    1464             :     // get the TopDict index
    1465           0 :     const long nTopDictBase = getReadOfs();
    1466           0 :     const int nTopDictCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1467           0 :     if( nTopDictCount) {
    1468           0 :         for( int i = 0; i < nTopDictCount; ++i) {
    1469           0 :             seekIndexData( nTopDictBase, i);
    1470           0 :             while( mpReadPtr < mpReadEnd)
    1471           0 :                 readDictOp();
    1472             :             assert( mpReadPtr == mpReadEnd);
    1473             :         }
    1474             :     }
    1475             : 
    1476             :     // prepare access to the String index
    1477           0 :     mnStringIdxBase =  getReadOfs();
    1478           0 :     mnStringIdxCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1479           0 :     seekIndexEnd( mnStringIdxBase);
    1480             : 
    1481             :     // prepare access to the GlobalSubr index
    1482           0 :     mnGlobalSubrBase =  getReadOfs();
    1483           0 :     mnGlobalSubrCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1484           0 :     mnGlobalSubrBias = (mnGlobalSubrCount<1240)?107:(mnGlobalSubrCount<33900)?1131:32768;
    1485             :     // skip past the last GlobalSubr entry
    1486             : //  seekIndexEnd( mnGlobalSubrBase);
    1487             : 
    1488             :     // get/skip the Encodings (we got mnEncodingBase from TOPDICT)
    1489             : //  seekEncodingsEnd( mnEncodingBase);
    1490             :     // get/skip the Charsets (we got mnCharsetBase from TOPDICT)
    1491             : //  seekCharsetsEnd( mnCharStrBase);
    1492             :     // get/skip FDSelect (CID only) data
    1493             : 
    1494             :     // prepare access to the CharStrings index (we got the base from TOPDICT)
    1495           0 :     mpReadPtr = mpBasePtr + mnCharStrBase;
    1496           0 :     mnCharStrCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1497             : //  seekIndexEnd( mnCharStrBase);
    1498             : 
    1499             :     // read the FDArray index (CID only)
    1500           0 :     if( mbCIDFont) {
    1501             : //      assert( mnFontDictBase == tellRel());
    1502           0 :         mpReadPtr = mpBasePtr + mnFontDictBase;
    1503           0 :         mnFDAryCount = (mpReadPtr[0]<<8) + mpReadPtr[1];
    1504             :         assert( mnFDAryCount < (int)(sizeof(maCffLocal)/sizeof(*maCffLocal)));
    1505             : 
    1506             :         // read FDArray details to get access to the PRIVDICTs
    1507           0 :         for( int i = 0; i < mnFDAryCount; ++i) {
    1508           0 :             mpCffLocal = &maCffLocal[i];
    1509           0 :             seekIndexData( mnFontDictBase, i);
    1510           0 :             while( mpReadPtr < mpReadEnd)
    1511           0 :                 readDictOp();
    1512             :             assert( mpReadPtr == mpReadEnd);
    1513             :         }
    1514             :     }
    1515             : 
    1516           0 :     for( int i = 0; i < mnFDAryCount; ++i) {
    1517           0 :         mpCffLocal = &maCffLocal[i];
    1518             : 
    1519             :         // get the PrivateDict index
    1520             :         // (we got mnPrivDictSize and mnPrivDictBase from TOPDICT or FDArray)
    1521           0 :         if( mpCffLocal->mnPrivDictSize != 0) {
    1522             :             assert( mpCffLocal->mnPrivDictSize > 0);
    1523             :             // get the PrivDict data
    1524           0 :             mpReadPtr = mpBasePtr + mpCffLocal->mnPrivDictBase;
    1525           0 :             mpReadEnd = mpReadPtr + mpCffLocal->mnPrivDictSize;
    1526             :             assert( mpReadEnd <= mpBaseEnd);
    1527             :             // read PrivDict details
    1528           0 :             while( mpReadPtr < mpReadEnd)
    1529           0 :                 readDictOp();
    1530             :         }
    1531             : 
    1532             :         // prepare access to the LocalSubrs (we got mnLocalSubrOffs from PRIVDICT)
    1533           0 :         if( mpCffLocal->mnLocalSubrOffs) {
    1534             :             // read LocalSubrs summary
    1535           0 :             mpCffLocal->mnLocalSubrBase = mpCffLocal->mnPrivDictBase + mpCffLocal->mnLocalSubrOffs;
    1536           0 :             mpReadPtr = mpBasePtr + mpCffLocal->mnLocalSubrBase;
    1537           0 :             const int nSubrCount = (mpReadPtr[0] << 8) + mpReadPtr[1];
    1538           0 :             mpCffLocal->mnLocalSubrCount = nSubrCount;
    1539           0 :             mpCffLocal->mnLocalSubrBias = (nSubrCount<1240)?107:(nSubrCount<33900)?1131:32768;
    1540             : //          seekIndexEnd( mpCffLocal->mnLocalSubrBase);
    1541             :         }
    1542             :     }
    1543             : 
    1544             :     // ignore the Notices info
    1545           0 : }
    1546             : 
    1547             : // get a cstring from a StringID
    1548           0 : const char* CffSubsetterContext::getString( int nStringID)
    1549             : {
    1550             :     // get a standard string if possible
    1551             :     const static int nStdStrings = sizeof(pStringIds)/sizeof(*pStringIds);
    1552           0 :     if( (nStringID >= 0) && (nStringID < nStdStrings))
    1553           0 :         return pStringIds[ nStringID];
    1554             : 
    1555             :     // else get the string from the StringIndex table
    1556           0 :     const U8* pReadPtr = mpReadPtr;
    1557           0 :     const U8* pReadEnd = mpReadEnd;
    1558           0 :     nStringID -= nStdStrings;
    1559           0 :     int nLen = seekIndexData( mnStringIdxBase, nStringID);
    1560             :     // assert( nLen >= 0);
    1561             :     // TODO: just return the undecorated name
    1562             :     // TODO: get rid of static char buffer
    1563             :     static char aNameBuf[ 2560];
    1564           0 :     if( nLen < 0) {
    1565           0 :         sprintf( aNameBuf, "name[%d].notfound!", nStringID);
    1566             :     } else {
    1567           0 :         const int nMaxLen = sizeof(aNameBuf) - 1;
    1568           0 :         if( nLen >= nMaxLen)
    1569           0 :             nLen = nMaxLen;
    1570           0 :         for( int i = 0; i < nLen; ++i)
    1571           0 :             aNameBuf[i] = *(mpReadPtr++);
    1572           0 :         aNameBuf[ nLen] = '\0';
    1573             :     }
    1574           0 :     mpReadPtr = pReadPtr;
    1575           0 :     mpReadEnd = pReadEnd;
    1576           0 :     return aNameBuf;
    1577             : }
    1578             : 
    1579             : // access a CID's FDSelect table
    1580           0 : int CffSubsetterContext::getFDSelect( int nGlyphIndex) const
    1581             : {
    1582             :     assert( nGlyphIndex >= 0);
    1583             :     assert( nGlyphIndex < mnCharStrCount);
    1584           0 :     if( !mbCIDFont)
    1585           0 :         return 0;
    1586             : 
    1587           0 :     const U8* pReadPtr = mpBasePtr + mnFDSelectBase;
    1588           0 :     const U8 nFDSelFormat = *(pReadPtr++);
    1589           0 :     switch( nFDSelFormat) {
    1590             :         case 0: { // FDSELECT format 0
    1591           0 :                 pReadPtr += nGlyphIndex;
    1592           0 :                 const U8 nFDIdx = *(pReadPtr++);
    1593           0 :                 return nFDIdx;
    1594             :             } //break;
    1595             :         case 3: { // FDSELECT format 3
    1596           0 :                 const U16 nRangeCount = (pReadPtr[0]<<8) + pReadPtr[1];
    1597             :                 assert( nRangeCount > 0);
    1598             :                 assert( nRangeCount <= mnCharStrCount);
    1599           0 :                 U16 nPrev = (pReadPtr[2]<<8) + pReadPtr[3];
    1600             :                 assert( nPrev == 0);
    1601             :                 (void)nPrev;
    1602           0 :                 pReadPtr += 4;
    1603             :                 // TODO? binary search
    1604           0 :                 for( int i = 0; i < nRangeCount; ++i) {
    1605           0 :                     const U8 nFDIdx = pReadPtr[0];
    1606           0 :                     const U16 nNext = (pReadPtr[1]<<8) + pReadPtr[2];
    1607             :                     assert( nPrev < nNext);
    1608           0 :                     if( nGlyphIndex < nNext)
    1609           0 :                         return nFDIdx;
    1610           0 :                     pReadPtr += 3;
    1611           0 :                     nPrev = nNext;
    1612             :                 }
    1613           0 :             } break;
    1614             :         default:    // invalid FDselect format
    1615           0 :             fprintf( stderr, "invalid CFF.FdselType=%d\n", nFDSelFormat);
    1616           0 :             break;
    1617             :     }
    1618             : 
    1619             :     assert( false);
    1620           0 :     return -1;
    1621             : }
    1622             : 
    1623           0 : int CffSubsetterContext::getGlyphSID( int nGlyphIndex) const
    1624             : {
    1625           0 :     if( nGlyphIndex == 0)
    1626           0 :         return 0;       // ".notdef"
    1627             :     assert( nGlyphIndex >= 0);
    1628             :     assert( nGlyphIndex < mnCharStrCount);
    1629           0 :     if( (nGlyphIndex < 0) || (nGlyphIndex >= mnCharStrCount))
    1630           0 :         return -1;
    1631             : 
    1632             :     // get the SID/CID from the Charset table
    1633           0 :      const U8* pReadPtr = mpBasePtr + mnCharsetBase;
    1634           0 :     const U8 nCSetFormat = *(pReadPtr++);
    1635           0 :     int nGlyphsToSkip = nGlyphIndex - 1;
    1636           0 :     switch( nCSetFormat) {
    1637             :         case 0: // charset format 0
    1638           0 :             pReadPtr += 2 * nGlyphsToSkip;
    1639           0 :             nGlyphsToSkip = 0;
    1640           0 :             break;
    1641             :         case 1: // charset format 1
    1642           0 :             while( nGlyphsToSkip >= 0) {
    1643           0 :                 const int nLeft = pReadPtr[2];
    1644           0 :                 if( nGlyphsToSkip <= nLeft)
    1645           0 :                     break;
    1646           0 :                 nGlyphsToSkip -= nLeft + 1;
    1647           0 :                 pReadPtr += 3;
    1648             :             }
    1649           0 :             break;
    1650             :         case 2: // charset format 2
    1651           0 :             while( nGlyphsToSkip >= 0) {
    1652           0 :                 const int nLeft = (pReadPtr[2]<<8) + pReadPtr[3];
    1653           0 :                 if( nGlyphsToSkip <= nLeft)
    1654           0 :                     break;
    1655           0 :                 nGlyphsToSkip -= nLeft + 1;
    1656           0 :                 pReadPtr += 4;
    1657             :             }
    1658           0 :             break;
    1659             :         default:
    1660           0 :             fprintf( stderr, "ILLEGAL CFF-Charset format %d\n", nCSetFormat);
    1661           0 :             return -2;
    1662             :     }
    1663             : 
    1664           0 :     int nSID = (pReadPtr[0]<<8) + pReadPtr[1];
    1665           0 :     nSID += nGlyphsToSkip;
    1666             :     // NOTE: for CID-fonts the resulting SID is interpreted as CID
    1667           0 :     return nSID;
    1668             : }
    1669             : 
    1670             : // NOTE: the result becomes invalid with the next call to this method
    1671           0 : const char* CffSubsetterContext::getGlyphName( int nGlyphIndex)
    1672             : {
    1673             :     // the first glyph is always the .notdef glyph
    1674           0 :     const char* pGlyphName = ".notdef";
    1675           0 :     if( nGlyphIndex == 0)
    1676           0 :         return pGlyphName;
    1677             : 
    1678             :     // prepare a result buffer
    1679             :     // TODO: get rid of static buffer
    1680             :     static char aDefaultGlyphName[64];
    1681           0 :     pGlyphName = aDefaultGlyphName;
    1682             : 
    1683             :     // get the glyph specific name
    1684           0 :     const int nSID = getGlyphSID( nGlyphIndex);
    1685           0 :     if( nSID < 0)           // default glyph name
    1686           0 :         sprintf( aDefaultGlyphName, "gly%03d", nGlyphIndex);
    1687           0 :     else if( mbCIDFont)     // default glyph name in CIDs
    1688           0 :          sprintf( aDefaultGlyphName, "cid%03d", nSID);
    1689             :     else {                  // glyph name from string table
    1690           0 :         const char* pSidName = getString( nSID);
    1691             :         // check validity of glyph name
    1692           0 :         if( pSidName) {
    1693           0 :             const char* p = pSidName;
    1694           0 :             while( (*p >= '0') && (*p <= 'z')) ++p;
    1695           0 :             if( (p >= pSidName+1) && (*p == '\0'))
    1696           0 :                 pGlyphName = pSidName;
    1697             :         }
    1698             :         // if needed invent a fallback name
    1699           0 :         if( pGlyphName != pSidName)
    1700           0 :              sprintf( aDefaultGlyphName, "bad%03d", nSID);
    1701             :     }
    1702             : 
    1703           0 :      return pGlyphName;
    1704             : }
    1705             : 
    1706             : class Type1Emitter
    1707             : {
    1708             : public:
    1709             :     explicit    Type1Emitter( FILE* pOutFile, bool bPfbSubset = true);
    1710             :     /*virtual*/ ~Type1Emitter( void);
    1711             :     void        setSubsetName( const char* );
    1712             : 
    1713             :     size_t      emitRawData( const char* pData, size_t nLength) const;
    1714             :     void        emitAllRaw( void);
    1715             :     void        emitAllHex( void);
    1716             :     void        emitAllCrypted( void);
    1717             :     int         tellPos( void) const;
    1718             :     size_t      updateLen( int nTellPos, size_t nLength);
    1719             :     void        emitValVector( const char* pLineHead, const char* pLineTail, const ValVector&);
    1720             : private:
    1721             :     FILE*       mpFileOut;
    1722             :     bool        mbCloseOutfile;
    1723             :     char        maBuffer[MAX_T1OPS_SIZE];   // TODO: dynamic allocation
    1724             :     int         mnEECryptR;
    1725             : public:
    1726             :     char*       mpPtr;
    1727             : 
    1728             :     char        maSubsetName[256];
    1729             :     bool        mbPfbSubset;
    1730             :     int         mnHexLineCol;
    1731             : };
    1732             : 
    1733           0 : Type1Emitter::Type1Emitter( FILE* pOutFile, bool bPfbSubset)
    1734             : :   mpFileOut( pOutFile)
    1735             : ,   mbCloseOutfile( false)
    1736             : ,   mnEECryptR( 55665)  // default eexec seed, TODO: mnEECryptSeed
    1737             : ,   mpPtr( maBuffer)
    1738             : ,   mbPfbSubset( bPfbSubset)
    1739           0 : ,   mnHexLineCol( 0)
    1740             : {
    1741           0 :     maSubsetName[0] = '\0';
    1742           0 : }
    1743             : 
    1744           0 : Type1Emitter::~Type1Emitter( void)
    1745             : {
    1746           0 :     if( !mpFileOut)
    1747           0 :         return;
    1748           0 :     if( mbCloseOutfile )
    1749           0 :         fclose( mpFileOut);
    1750           0 :     mpFileOut = NULL;
    1751           0 : }
    1752             : 
    1753           0 : void Type1Emitter::setSubsetName( const char* pSubsetName)
    1754             : {
    1755           0 :     maSubsetName[0] = '\0';
    1756           0 :     if( pSubsetName)
    1757           0 :         strncpy( maSubsetName, pSubsetName, sizeof(maSubsetName));
    1758           0 :     maSubsetName[sizeof(maSubsetName)-1] = '\0';
    1759           0 : }
    1760             : 
    1761           0 : int Type1Emitter::tellPos( void) const
    1762             : {
    1763           0 :     int nTellPos = ftell( mpFileOut);
    1764           0 :     return nTellPos;
    1765             : }
    1766             : 
    1767           0 : size_t Type1Emitter::updateLen( int nTellPos, size_t nLength)
    1768             : {
    1769             :     // update PFB segment header length
    1770             :     U8 cData[4];
    1771           0 :     cData[0] = static_cast<U8>(nLength >>  0);
    1772           0 :     cData[1] = static_cast<U8>(nLength >>  8);
    1773           0 :     cData[2] = static_cast<U8>(nLength >> 16);
    1774           0 :     cData[3] = static_cast<U8>(nLength >> 24);
    1775           0 :     const long nCurrPos = ftell( mpFileOut);
    1776           0 :     fseek( mpFileOut, nTellPos, SEEK_SET);
    1777           0 :     size_t nWrote = fwrite( cData, 1, sizeof(cData), mpFileOut);
    1778           0 :     if( nCurrPos >= 0)
    1779           0 :         fseek( mpFileOut, nCurrPos, SEEK_SET);
    1780           0 :     return nWrote;
    1781             : }
    1782             : 
    1783           0 : inline size_t Type1Emitter::emitRawData(const char* pData, size_t nLength) const
    1784             : {
    1785           0 :     return fwrite( pData, 1, nLength, mpFileOut);
    1786             : }
    1787             : 
    1788           0 : inline void Type1Emitter::emitAllRaw( void)
    1789             : {
    1790             :     // writeout raw data
    1791             :     assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
    1792           0 :     emitRawData( maBuffer, mpPtr - maBuffer);
    1793             :     // reset the raw buffer
    1794           0 :     mpPtr = maBuffer;
    1795           0 : }
    1796             : 
    1797           0 : inline void Type1Emitter::emitAllHex( void)
    1798             : {
    1799             :     assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
    1800           0 :     for( const char* p = maBuffer; p < mpPtr;) {
    1801             :         // convert binary chunk to hex
    1802             :         char aHexBuf[0x4000];
    1803           0 :         char* pOut = aHexBuf;
    1804           0 :         while( (p < mpPtr) && (pOut < aHexBuf+sizeof(aHexBuf)-4)) {
    1805             :             // convert each byte to hex
    1806           0 :             char cNibble = (*p >> 4) & 0x0F;
    1807           0 :             cNibble += (cNibble < 10) ? '0' : 'A'-10;
    1808           0 :             *(pOut++) = cNibble;
    1809           0 :             cNibble = *(p++) & 0x0F;
    1810           0 :             cNibble += (cNibble < 10) ? '0' : 'A'-10;
    1811           0 :             *(pOut++) = cNibble;
    1812             :             // limit the line length
    1813           0 :             if( (++mnHexLineCol & 0x3F) == 0)
    1814           0 :                 *(pOut++) = '\n';
    1815             :         }
    1816             :         // writeout hex-converted chunk
    1817           0 :         emitRawData( aHexBuf, pOut-aHexBuf);
    1818             :     }
    1819             :     // reset the raw buffer
    1820           0 :     mpPtr = maBuffer;
    1821           0 : }
    1822             : 
    1823           0 : void Type1Emitter::emitAllCrypted( void)
    1824             : {
    1825             :     // apply t1crypt
    1826           0 :     for( char* p = maBuffer; p < mpPtr; ++p) {
    1827           0 :         *p ^= (mnEECryptR >> 8);
    1828           0 :         mnEECryptR = (*(U8*)p + mnEECryptR) * 52845 + 22719;
    1829             :     }
    1830             : 
    1831             :     // emit the t1crypt result
    1832           0 :     if( mbPfbSubset)
    1833           0 :         emitAllRaw();
    1834             :     else
    1835           0 :         emitAllHex();
    1836           0 : }
    1837             : 
    1838             : // #i110387# quick-and-dirty double->ascii conversion
    1839             : // needed because sprintf/ecvt/etc. alone are too localized (LC_NUMERIC)
    1840             : // also strip off trailing zeros in fraction while we are at it
    1841           0 : inline int dbl2str( char* pOut, double fVal, int nPrecision=6)
    1842             : {
    1843           0 :     const int nLen = psp::getValueOfDouble( pOut, fVal, nPrecision);
    1844           0 :     return nLen;
    1845             : }
    1846             : 
    1847           0 : void Type1Emitter::emitValVector( const char* pLineHead, const char* pLineTail,
    1848             :     const ValVector& rVector)
    1849             : {
    1850             :     // ignore empty vectors
    1851           0 :     if( rVector.empty())
    1852           0 :         return;
    1853             : 
    1854             :     // emit the line head
    1855           0 :     mpPtr += sprintf( mpPtr, "%s", pLineHead);
    1856             :     // emit the vector values
    1857           0 :     ValVector::value_type aVal = 0;
    1858           0 :     for( ValVector::const_iterator it = rVector.begin();;) {
    1859           0 :         aVal = *it;
    1860           0 :         if( ++it == rVector.end() )
    1861           0 :             break;
    1862           0 :         mpPtr += dbl2str( mpPtr, aVal);
    1863           0 :         *(mpPtr++) = ' ';
    1864           0 :     }
    1865             :     // emit the last value
    1866           0 :     mpPtr += dbl2str( mpPtr, aVal);
    1867             :     // emit the line tail
    1868           0 :     mpPtr += sprintf( mpPtr, "%s", pLineTail);
    1869             : }
    1870             : 
    1871           0 : bool CffSubsetterContext::emitAsType1( Type1Emitter& rEmitter,
    1872             :     const sal_GlyphId* pReqGlyphIds, const U8* pReqEncoding,
    1873             :     GlyphWidth* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rFSInfo)
    1874             : {
    1875             :     // prepare some fontdirectory details
    1876             :     static const int nUniqueIdBase = 4100000; // using private-interchange UniqueIds
    1877             :     static int nUniqueId = nUniqueIdBase;
    1878           0 :     ++nUniqueId;
    1879             : 
    1880           0 :     char* pFontName = rEmitter.maSubsetName;
    1881           0 :     if( !*pFontName ) {
    1882           0 :         if( mnFontNameSID) {
    1883             :             // get the fontname directly if available
    1884           0 :             strncpy( pFontName, getString( mnFontNameSID), sizeof(rEmitter.maSubsetName) - 1);
    1885           0 :             pFontName[sizeof(rEmitter.maSubsetName) - 1] = 0;
    1886           0 :         } else if( mnFullNameSID) {
    1887             :             // approximate fontname as fullname-whitespace
    1888           0 :             const char* pI = getString( mnFullNameSID);
    1889           0 :             char* pO = pFontName;
    1890           0 :             const char* pLimit = pFontName + sizeof(rEmitter.maSubsetName) - 1;
    1891           0 :             while( pO < pLimit) {
    1892           0 :                 const char c = *(pI++);
    1893           0 :                 if( c != ' ')
    1894           0 :                     *(pO++) = c;
    1895           0 :                 if( !c)
    1896           0 :                     break;
    1897             :             }
    1898           0 :             *pO = '\0';
    1899             :         } else {
    1900             :             // fallback name of last resort
    1901           0 :             strncpy( pFontName, "DummyName", sizeof(rEmitter.maSubsetName));
    1902             :         }
    1903             :     }
    1904           0 :     const char* pFullName = pFontName;
    1905           0 :     const char* pFamilyName = pFontName;
    1906             : 
    1907           0 :     char*& pOut = rEmitter.mpPtr; // convenience reference, TODO: cleanup
    1908             : 
    1909             :     // create a PFB+Type1 header
    1910           0 :     if( rEmitter.mbPfbSubset ) {
    1911             :         static const char aPfbHeader[] = "\x80\x01\x00\x00\x00\x00";
    1912           0 :         rEmitter.emitRawData( aPfbHeader, sizeof(aPfbHeader)-1);
    1913             :     }
    1914             : 
    1915           0 :     pOut += sprintf( pOut, "%%!FontType1-1.0: %s 001.003\n", rEmitter.maSubsetName);
    1916             :     // emit TOPDICT
    1917           0 :     pOut += sprintf( pOut,
    1918             :         "11 dict begin\n"   // TODO: dynamic entry count for TOPDICT
    1919             :         "/FontType 1 def\n"
    1920           0 :         "/PaintType 0 def\n");
    1921           0 :     pOut += sprintf( pOut, "/FontName /%s def\n", rEmitter.maSubsetName);
    1922           0 :     pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
    1923             :     // emit FontMatrix
    1924           0 :     if( maFontMatrix.size() == 6)
    1925           0 :         rEmitter.emitValVector( "/FontMatrix [", "]readonly def\n", maFontMatrix);
    1926             :     else // emit default FontMatrix if needed
    1927           0 :         pOut += sprintf( pOut, "/FontMatrix [0.001 0 0 0.001 0 0]readonly def\n");
    1928             :     // emit FontBBox
    1929           0 :     if( maFontBBox.size() == 4)
    1930           0 :         rEmitter.emitValVector( "/FontBBox {", "}readonly def\n", maFontBBox);
    1931             :     else // emit default FontBBox if needed
    1932           0 :         pOut += sprintf( pOut, "/FontBBox {0 0 999 999}readonly def\n");
    1933             :     // emit FONTINFO into TOPDICT
    1934           0 :     pOut += sprintf( pOut,
    1935             :         "/FontInfo 2 dict dup begin\n"  // TODO: check fontinfo entry count
    1936             :         " /FullName (%s) readonly def\n"
    1937             :         " /FamilyName (%s) readonly def\n"
    1938             :         "end readonly def\n",
    1939           0 :             pFullName, pFamilyName);
    1940             : 
    1941           0 :     pOut += sprintf( pOut,
    1942             :         "/Encoding 256 array\n"
    1943           0 :         "0 1 255 {1 index exch /.notdef put} for\n");
    1944           0 :     for( int i = 1; (i < nGlyphCount) && (i < 256); ++i) {
    1945           0 :         const char* pGlyphName = getGlyphName( pReqGlyphIds[i]);
    1946           0 :         pOut += sprintf( pOut, "dup %d /%s put\n", pReqEncoding[i], pGlyphName);
    1947             :     }
    1948           0 :     pOut += sprintf( pOut, "readonly def\n");
    1949           0 :     pOut += sprintf( pOut,
    1950             :         // TODO: more topdict entries
    1951             :         "currentdict end\n"
    1952           0 :         "currentfile eexec\n");
    1953             : 
    1954             :     // emit PFB header
    1955           0 :     rEmitter.emitAllRaw();
    1956           0 :     if( rEmitter.mbPfbSubset) {
    1957             :         // update PFB header segment
    1958           0 :         const int nPfbHeaderLen = rEmitter.tellPos() - 6;
    1959           0 :         rEmitter.updateLen( 2, nPfbHeaderLen);
    1960             : 
    1961             :         // prepare start of eexec segment
    1962           0 :         rEmitter.emitRawData( "\x80\x02\x00\x00\x00\x00", 6);   // segment start
    1963             :     }
    1964           0 :     const int nEExecSegTell = rEmitter.tellPos();
    1965             : 
    1966             :     // which always starts with a privdict
    1967             :     // count the privdict entries
    1968           0 :     int nPrivEntryCount = 9;
    1969             : #if !defined(IGNORE_HINTS)
    1970             :     // emit blue hints only if non-default values
    1971           0 :     nPrivEntryCount += int(!mpCffLocal->maOtherBlues.empty());
    1972           0 :     nPrivEntryCount += int(!mpCffLocal->maFamilyBlues.empty());
    1973           0 :     nPrivEntryCount += int(!mpCffLocal->maFamilyOtherBlues.empty());
    1974           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueScale != 0.0);
    1975           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueShift != 0.0);
    1976           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueFuzz != 0.0);
    1977             :     // emit stem hints only if non-default values
    1978           0 :     nPrivEntryCount += int(mpCffLocal->maStemStdHW != 0);
    1979           0 :     nPrivEntryCount += int(mpCffLocal->maStemStdVW != 0);
    1980           0 :     nPrivEntryCount += int(!mpCffLocal->maStemSnapH.empty());
    1981           0 :     nPrivEntryCount += int(!mpCffLocal->maStemSnapV.empty());
    1982             :     // emit other hints only if non-default values
    1983           0 :     nPrivEntryCount += int(mpCffLocal->mfExpFactor != 0.0);
    1984           0 :     nPrivEntryCount += int(mpCffLocal->mnLangGroup != 0);
    1985           0 :     nPrivEntryCount += int(mpCffLocal->mnLangGroup == 1);
    1986           0 :     nPrivEntryCount += int(mpCffLocal->mbForceBold != false);
    1987             : #endif // IGNORE_HINTS
    1988             :     // emit the privdict header
    1989           0 :     pOut += sprintf( pOut,
    1990             :         "\110\104\125 "
    1991             :         "dup\n/Private %d dict dup begin\n"
    1992             :         "/RD{string currentfile exch readstring pop}executeonly def\n"
    1993             :         "/ND{noaccess def}executeonly def\n"
    1994             :         "/NP{noaccess put}executeonly def\n"
    1995             :         "/MinFeature{16 16}ND\n"
    1996             :         "/password 5839 def\n",     // TODO: mnRDCryptSeed?
    1997           0 :             nPrivEntryCount);
    1998             : 
    1999             : #if defined(IGNORE_HINTS)
    2000             :     pOut += sprintf( pOut, "/BlueValues []ND\n");   // BlueValues are mandatory
    2001             : #else
    2002             :     // emit blue hint related privdict entries
    2003           0 :     if( !mpCffLocal->maBlueValues.empty())
    2004           0 :         rEmitter.emitValVector( "/BlueValues [", "]ND\n", mpCffLocal->maBlueValues);
    2005             :     else
    2006           0 :         pOut += sprintf( pOut, "/BlueValues []ND\n"); // default to empty BlueValues
    2007           0 :     rEmitter.emitValVector( "/OtherBlues [", "]ND\n", mpCffLocal->maOtherBlues);
    2008           0 :     rEmitter.emitValVector( "/FamilyBlues [", "]ND\n", mpCffLocal->maFamilyBlues);
    2009           0 :     rEmitter.emitValVector( "/FamilyOtherBlues [", "]ND\n", mpCffLocal->maFamilyOtherBlues);
    2010             : 
    2011           0 :     if( mpCffLocal->mfBlueScale) {
    2012           0 :         pOut += sprintf( pOut, "/BlueScale ");
    2013           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueScale, 6);
    2014           0 :         pOut += sprintf( pOut, " def\n");
    2015             :     }
    2016           0 :     if( mpCffLocal->mfBlueShift) {  // default BlueShift==7
    2017           0 :         pOut += sprintf( pOut, "/BlueShift ");
    2018           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueShift);
    2019           0 :         pOut += sprintf( pOut, " def\n");
    2020             :     }
    2021           0 :     if( mpCffLocal->mfBlueFuzz) {       // default BlueFuzz==1
    2022           0 :         pOut += sprintf( pOut, "/BlueFuzz ");
    2023           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueFuzz);
    2024           0 :         pOut += sprintf( pOut, " def\n");
    2025             :     }
    2026             : 
    2027             :     // emit stem hint related privdict entries
    2028           0 :     if( mpCffLocal->maStemStdHW) {
    2029           0 :         pOut += sprintf( pOut, "/StdHW [");
    2030           0 :         pOut += dbl2str( pOut, mpCffLocal->maStemStdHW);
    2031           0 :         pOut += sprintf( pOut, "] def\n");
    2032             :     }
    2033           0 :     if( mpCffLocal->maStemStdVW) {
    2034           0 :         pOut += sprintf( pOut, "/StdVW [");
    2035           0 :         pOut += dbl2str( pOut, mpCffLocal->maStemStdVW);
    2036           0 :         pOut += sprintf( pOut, "] def\n");
    2037             :     }
    2038           0 :     rEmitter.emitValVector( "/StemSnapH [", "]ND\n", mpCffLocal->maStemSnapH);
    2039           0 :     rEmitter.emitValVector( "/StemSnapV [", "]ND\n", mpCffLocal->maStemSnapV);
    2040             : 
    2041             :     // emit other hints
    2042           0 :     if( mpCffLocal->mbForceBold)
    2043           0 :         pOut += sprintf( pOut, "/ForceBold true def\n");
    2044           0 :     if( mpCffLocal->mnLangGroup != 0)
    2045           0 :         pOut += sprintf( pOut, "/LanguageGroup %d def\n", mpCffLocal->mnLangGroup);
    2046           0 :     if( mpCffLocal->mnLangGroup == 1) // compatibility with ancient printers
    2047           0 :         pOut += sprintf( pOut, "/RndStemUp false def\n");
    2048           0 :     if( mpCffLocal->mfExpFactor) {
    2049           0 :         pOut += sprintf( pOut, "/ExpansionFactor ");
    2050           0 :         pOut += dbl2str( pOut, mpCffLocal->mfExpFactor);
    2051           0 :         pOut += sprintf( pOut, " def\n");
    2052             :     }
    2053             : #endif // IGNORE_HINTS
    2054             : 
    2055             :     // emit remaining privdict entries
    2056           0 :     pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
    2057             :     // TODO?: more privdict entries?
    2058             : 
    2059             :     static const char aOtherSubrs[] =
    2060             :         "/OtherSubrs\n"
    2061             :         "% Dummy code for faking flex hints\n"
    2062             :         "[ {} {} {} {systemdict /internaldict known not {pop 3}\n"
    2063             :         "{1183615869 systemdict /internaldict get exec\n"
    2064             :         "dup /startlock known\n"
    2065             :         "{/startlock get exec}\n"
    2066             :         "{dup /strtlck known\n"
    2067             :         "{/strtlck get exec}\n"
    2068             :         "{pop 3}\nifelse}\nifelse}\nifelse\n} executeonly\n"
    2069             :         "] ND\n";
    2070           0 :     memcpy( pOut, aOtherSubrs, sizeof(aOtherSubrs)-1);
    2071           0 :     pOut += sizeof(aOtherSubrs)-1;
    2072             : 
    2073             :     // emit used GlobalSubr charstrings
    2074             :     // these are the just the default subrs
    2075             :     // TODO: do we need them as the flex hints are resolved differently?
    2076             :     static const char aSubrs[] =
    2077             :         "/Subrs 5 array\n"
    2078             :         "dup 0 15 RD \x5F\x3D\x6B\xAC\x3C\xBD\x74\x3D\x3E\x17\xA0\x86\x58\x08\x85 NP\n"
    2079             :         "dup 1 9 RD \x5F\x3D\x6B\xD8\xA6\xB5\x68\xB6\xA2 NP\n"
    2080             :         "dup 2 9 RD \x5F\x3D\x6B\xAC\x39\x46\xB9\x43\xF9 NP\n"
    2081             :         "dup 3 5 RD \x5F\x3D\x6B\xAC\xB9 NP\n"
    2082             :         "dup 4 12 RD \x5F\x3D\x6B\xAC\x3E\x5D\x48\x54\x62\x76\x39\x03 NP\n"
    2083             :         "ND\n";
    2084           0 :     memcpy( pOut, aSubrs, sizeof(aSubrs)-1);
    2085           0 :     pOut += sizeof(aSubrs)-1;
    2086             : 
    2087             :     // TODO: emit more GlobalSubr charstrings?
    2088             :     // TODO: emit used LocalSubr charstrings?
    2089             : 
    2090             :     // emit the CharStrings for the requested glyphs
    2091           0 :     pOut += sprintf( pOut,
    2092           0 :         "2 index /CharStrings %d dict dup begin\n", nGlyphCount);
    2093           0 :     rEmitter.emitAllCrypted();
    2094           0 :     for( int i = 0; i < nGlyphCount; ++i) {
    2095           0 :         const int nCffGlyphId = pReqGlyphIds[i];
    2096             :         assert( (nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount));
    2097             :         // get privdict context matching to the glyph
    2098           0 :         const int nFDSelect = getFDSelect( nCffGlyphId);
    2099           0 :         if( nFDSelect < 0)
    2100           0 :             continue;
    2101           0 :         mpCffLocal = &maCffLocal[ nFDSelect];
    2102             :         // convert the Type2op charstring to its Type1op counterpart
    2103           0 :         const int nT2Len = seekIndexData( mnCharStrBase, nCffGlyphId);
    2104             :         assert( nT2Len > 0);
    2105             :         U8 aType1Ops[ MAX_T1OPS_SIZE]; // TODO: dynamic allocation
    2106           0 :         const int nT1Len = convert2Type1Ops( mpCffLocal, mpReadPtr, nT2Len, aType1Ops);
    2107             :         // get the glyph name
    2108           0 :         const char* pGlyphName = getGlyphName( nCffGlyphId);
    2109             :         // emit the encrypted Type1op charstring
    2110           0 :         pOut += sprintf( pOut, "/%s %d RD ", pGlyphName, nT1Len);
    2111           0 :         memcpy( pOut, aType1Ops, nT1Len);
    2112           0 :         pOut += nT1Len;
    2113           0 :         pOut += sprintf( pOut, " ND\n");
    2114           0 :         rEmitter.emitAllCrypted();
    2115             :         // provide individual glyphwidths if requested
    2116           0 :         if( pGlyphWidths ) {
    2117           0 :             ValType aCharWidth = getCharWidth();
    2118           0 :             if( maFontMatrix.size() >= 4)
    2119           0 :                 aCharWidth *= 1000.0F * maFontMatrix[0];
    2120           0 :             pGlyphWidths[i] = static_cast<GlyphWidth>(aCharWidth);
    2121             :         }
    2122             :     }
    2123           0 :     pOut += sprintf( pOut, "end end\nreadonly put\nput\n");
    2124           0 :     pOut += sprintf( pOut, "dup/FontName get exch definefont pop\n");
    2125           0 :     pOut += sprintf( pOut, "mark currentfile closefile\n");
    2126           0 :     rEmitter.emitAllCrypted();
    2127             : 
    2128             :     // mark stop of eexec encryption
    2129           0 :      if( rEmitter.mbPfbSubset) {
    2130           0 :         const int nEExecLen = rEmitter.tellPos() - nEExecSegTell;
    2131           0 :         rEmitter.updateLen( nEExecSegTell-4, nEExecLen);
    2132             :      }
    2133             : 
    2134             :     // create PFB footer
    2135             :     static const char aPfxFooter[] = "\x80\x01\x14\x02\x00\x00\n" // TODO: check segment len
    2136             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2137             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2138             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2139             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2140             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2141             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2142             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2143             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2144             :         "cleartomark\n"
    2145             :         "\x80\x03";
    2146           0 :      if( rEmitter.mbPfbSubset)
    2147           0 :         rEmitter.emitRawData( aPfxFooter, sizeof(aPfxFooter)-1);
    2148             :     else
    2149           0 :         rEmitter.emitRawData( aPfxFooter+6, sizeof(aPfxFooter)-9);
    2150             : 
    2151             :     // provide details to the subset requesters, TODO: move into own method?
    2152             :     // note: Top and Bottom are flipped between Type1 and VCL
    2153             :     // note: the rest of VCL expects the details below to be scaled like for an emUnits==1000 font
    2154           0 :     ValType fXFactor = 1.0;
    2155           0 :     ValType fYFactor = 1.0;
    2156           0 :     if( maFontMatrix.size() >= 4) {
    2157           0 :         fXFactor = 1000.0F * maFontMatrix[0];
    2158           0 :         fYFactor = 1000.0F * maFontMatrix[3];
    2159             :     }
    2160           0 :     rFSInfo.m_aFontBBox = Rectangle( Point( static_cast<long>(maFontBBox[0] * fXFactor),
    2161           0 :                                         static_cast<long>(maFontBBox[1] * fYFactor) ),
    2162           0 :                                     Point( static_cast<long>(maFontBBox[2] * fXFactor),
    2163           0 :                                         static_cast<long>(maFontBBox[3] * fYFactor) ) );
    2164             :     // PDF-Spec says the values below mean the ink bounds!
    2165             :     // TODO: use better approximations for these ink bounds
    2166           0 :     rFSInfo.m_nAscent  = +rFSInfo.m_aFontBBox.Bottom(); // for capital letters
    2167           0 :     rFSInfo.m_nDescent = -rFSInfo.m_aFontBBox.Top();    // for all letters
    2168           0 :     rFSInfo.m_nCapHeight = rFSInfo.m_nAscent;           // for top-flat capital letters
    2169             : 
    2170           0 :     rFSInfo.m_nFontType = rEmitter.mbPfbSubset ? FontSubsetInfo::TYPE1_PFB : FontSubsetInfo::TYPE1_PFA;
    2171           0 :     rFSInfo.m_aPSName   = OUString( rEmitter.maSubsetName, strlen(rEmitter.maSubsetName), RTL_TEXTENCODING_UTF8 );
    2172             : 
    2173           0 :     return true;
    2174             : }
    2175             : 
    2176           0 : bool FontSubsetInfo::CreateFontSubsetFromCff( GlyphWidth* pOutGlyphWidths )
    2177             : {
    2178           0 :     CffSubsetterContext aCff( mpInFontBytes, mnInByteLength);
    2179           0 :     aCff.initialCffRead();
    2180             : 
    2181             :     // emit Type1 subset from the CFF input
    2182             :     // TODO: also support CFF->CFF subsetting (when PDF-export and PS-printing need it)
    2183           0 :     const bool bPfbSubset = (0 != (mnReqFontTypeMask & FontSubsetInfo::TYPE1_PFB));
    2184           0 :     Type1Emitter aType1Emitter( mpOutFile, bPfbSubset);
    2185           0 :     aType1Emitter.setSubsetName( mpReqFontName);
    2186             :     bool bRC = aCff.emitAsType1( aType1Emitter,
    2187             :         mpReqGlyphIds, mpReqEncodedIds,
    2188           0 :         pOutGlyphWidths, mnReqGlyphCount, *this);
    2189           0 :     return bRC;
    2190             : }
    2191             : 
    2192             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10