LCOV - code coverage report
Current view: top level - vcl/source/fontsubset - cff.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 0 1027 0.0 %
Date: 2015-06-13 12:38:46 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();
     327             : 
     328             :     bool    initialCffRead();
     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();
     339             :     void    convertOneTypeEsc();
     340             :     void    callType2Subr( bool bGlobal, int nSubrNumber);
     341           0 :     sal_Int32 getReadOfs() const { return (sal_Int32)(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             :     sal_Int32 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[256];
     364             :     CffLocal*   mpCffLocal;
     365             : 
     366             :     void        readDictOp();
     367             :     RealType    readRealVal();
     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();
     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() { return ((mnStackIdx>0) ? mnValStack[ --mnStackIdx] : 0);}
     386           0 :     ValType getVal( int nIndex) const { return mnValStack[ nIndex];}
     387             :     int     popInt();
     388           0 :     int     size() const { return mnStackIdx;}
     389           0 :     void    clear() { mnStackIdx = 0;}
     390             : 
     391             :     // accessing the charstring hints
     392             :     void    addHints( bool bVerticalHints);
     393             : 
     394             :     // accessing other charstring specifics
     395           0 :     bool    hasCharWidth() const { return (maCharWidth > 0);}
     396           0 :     ValType getCharWidth() 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             :     , mpReadPtr(NULL)
     418             :     , mpReadEnd(NULL)
     419             :     , mpWritePtr(NULL)
     420             :     , mbSawError(false)
     421             :     , mbNeedClose(false)
     422             :     , mbIgnoreHints(false)
     423             :     , mnCntrMask(0)
     424             :     , mpCharStringOps(NULL)
     425             :     , mpCharStringEscs(NULL)
     426             :     , mnStackIdx(0)
     427             :     , mnHintSize(0)
     428             :     , mnHorzHintSize(0)
     429           0 :     , maCharWidth(-1)
     430             : {
     431             : //  setCharStringType( 1);
     432             :     // TODO: new CffLocal[ mnFDAryCount];
     433           0 :     mpCffLocal = &maCffLocal[0];
     434           0 : }
     435             : 
     436           0 : CffSubsetterContext::~CffSubsetterContext()
     437             : {
     438             :     // TODO: delete[] maCffLocal;
     439           0 : }
     440             : 
     441           0 : inline int CffSubsetterContext::popInt()
     442             : {
     443           0 :     const ValType aVal = popVal();
     444           0 :     const int nInt = static_cast<int>(aVal);
     445             :     assert( nInt == aVal);
     446           0 :     return nInt;
     447             : }
     448             : 
     449           0 : inline void CffSubsetterContext::updateWidth( bool bUseFirstVal)
     450             : {
     451             : #if 1 // TODO: is this still needed?
     452             :     // the first value is not a hint but the charwidth
     453           0 :     if( hasCharWidth())
     454           0 :         return;
     455             : #endif
     456           0 :     if( bUseFirstVal) {
     457           0 :         maCharWidth = mpCffLocal->maNominalWidth + mnValStack[0];
     458             :         // remove bottom stack entry
     459           0 :         --mnStackIdx;
     460           0 :         for( int i = 0; i < mnStackIdx; ++i)
     461           0 :             mnValStack[ i] = mnValStack[ i+1];
     462             :     } else {
     463           0 :         maCharWidth = mpCffLocal->maDefaultWidth;
     464             :     }
     465             : }
     466             : 
     467           0 : void CffSubsetterContext::addHints( bool bVerticalHints)
     468             : {
     469             :     // the first charstring value may a charwidth instead of a charwidth
     470           0 :     updateWidth( (mnStackIdx & 1) != 0);
     471             :     // return early (e.g. no implicit hints for hintmask)
     472           0 :     if( !mnStackIdx)
     473           0 :         return;
     474             : 
     475             :     // copy the remaining values to the hint arrays
     476             :     // assert( (mnStackIdx & 1) == 0); // depends on called subrs
     477           0 :     if( mnStackIdx & 1) --mnStackIdx;//#######
     478             :     // TODO: if( !bSubr) assert( mnStackIdx >= 2);
     479             : 
     480             :     assert( (mnHintSize + mnStackIdx) <= 2*NMAXHINTS);
     481             : 
     482             : #ifdef IGNORE_HINTS
     483             :     mnHintSize += mnStackIdx;
     484             : #else
     485           0 :     ValType nHintOfs = 0;
     486           0 :     for( int i = 0; i < mnStackIdx; ++i) {
     487           0 :         nHintOfs += mnValStack[ i ];
     488           0 :         mnHintStack[ mnHintSize++] = nHintOfs;
     489             :     }
     490             : #endif // IGNORE_HINTS
     491           0 :     if( !bVerticalHints)
     492           0 :         mnHorzHintSize = mnHintSize;
     493             : 
     494             :     // clear all values from the stack
     495           0 :     mnStackIdx = 0;
     496             : }
     497             : 
     498           0 : void CffSubsetterContext::setCharStringType( int nVal)
     499             : {
     500           0 :     switch( nVal) {
     501           0 :         case 1: mpCharStringOps=pType1Ops; mpCharStringEscs=pT1EscOps; break;
     502           0 :         case 2: mpCharStringOps=pType2Ops; mpCharStringEscs=pT2EscOps; break;
     503           0 :         default: fprintf( stderr, "Unknown CharstringType=%d\n",nVal); break;
     504             :     }
     505           0 : }
     506             : 
     507           0 : void CffSubsetterContext::readDictOp()
     508             : {
     509           0 :     ValType nVal = 0;
     510           0 :     const U8 c = *mpReadPtr;
     511           0 :     if( c <= 21 ) {
     512           0 :         int nOpId = *(mpReadPtr++);
     513           0 :         const char* pCmdName = 0;
     514           0 :         if( nOpId != 12)
     515           0 :             pCmdName = pDictOps[nOpId];
     516             :         else {
     517           0 :             const U8 nExtId = *(mpReadPtr++);
     518           0 :             if (nExtId < 39)
     519           0 :                pCmdName = pDictEscs[nExtId];
     520           0 :             nOpId = 900 + nExtId;
     521             :         }
     522             : 
     523           0 :         if (!pCmdName)  // skip reserved operators
     524           0 :             return;
     525             : 
     526             :         //TODO: if( nStackIdx > 0)
     527           0 :         int nInt = 0;
     528           0 :         switch( *pCmdName) {
     529           0 :         default: fprintf( stderr, "unsupported DictOp.type=\'%c\'\n", *pCmdName); break;
     530             :         case 'b':   // bool
     531           0 :             nInt = popInt();
     532           0 :             switch( nOpId) {
     533           0 :             case 915: mpCffLocal->mbForceBold = nInt; break;    // "ForceBold"
     534           0 :             default: break; // TODO: handle more boolean dictops?
     535             :             }
     536           0 :             break;
     537             :         case 'n':   // dict-op number
     538           0 :             nVal = popVal();
     539           0 :             nInt = static_cast<int>(nVal);
     540           0 :             switch( nOpId) {
     541           0 :             case  10: mpCffLocal->maStemStdHW = nVal; break;    // "StdHW"
     542           0 :             case  11: mpCffLocal->maStemStdVW = nVal; break;    // "StdVW"
     543           0 :             case  15: mnCharsetBase = nInt; break;              // "charset"
     544           0 :             case  16: mnEncodingBase = nInt; break;             // "nEncoding"
     545           0 :             case  17: mnCharStrBase = nInt; break;              // "nCharStrings"
     546           0 :             case  19: mpCffLocal->mnLocalSubrOffs = nInt; break;// "nSubrs"
     547           0 :             case  20: setDefaultWidth( nVal ); break;           // "defaultWidthX"
     548           0 :             case  21: setNominalWidth( nVal ); break;           // "nominalWidthX"
     549           0 :             case 909: mpCffLocal->mfBlueScale = nVal; break;    // "BlueScale"
     550           0 :             case 910: mpCffLocal->mfBlueShift = nVal; break;    // "BlueShift"
     551           0 :             case 911: mpCffLocal->mfBlueFuzz = nVal; break;     // "BlueFuzz"
     552           0 :             case 912: mpCffLocal->mfExpFactor = nVal; break;    // "ExpansionFactor"
     553           0 :             case 917: mpCffLocal->mnLangGroup = nInt; break;    // "LanguageGroup"
     554           0 :             case 936: mnFontDictBase = nInt; break;             // "nFDArray"
     555           0 :             case 937: mnFDSelectBase = nInt; break;             // "nFDSelect"
     556           0 :             default: break; // TODO: handle more numeric dictops?
     557             :             }
     558           0 :             break;
     559             :         case 'a': { // array
     560           0 :             switch( nOpId) {
     561           0 :             case   5: maFontBBox.clear(); break;     // "FontBBox"
     562           0 :             case 907: maFontMatrix.clear(); break; // "FontMatrix"
     563           0 :             default: break; // TODO: reset other arrays?
     564             :             }
     565           0 :             for( int i = 0; i < size(); ++i ) {
     566           0 :                 nVal = getVal(i);
     567           0 :                 switch( nOpId) {
     568           0 :                 case   5: maFontBBox.push_back( nVal); break;     // "FontBBox"
     569           0 :                 case 907: maFontMatrix.push_back( nVal); break; // "FontMatrix"
     570           0 :                 default: break; // TODO: handle more array dictops?
     571             :                 }
     572             :             }
     573           0 :             clear();
     574           0 :             } break;
     575             :         case 'd': { // delta array
     576           0 :             nVal = 0;
     577           0 :             for( int i = 0; i < size(); ++i ) {
     578           0 :                 nVal += getVal(i);
     579           0 :                 switch( nOpId) {
     580           0 :                 case   6: mpCffLocal->maBlueValues.push_back( nVal); break;     // "BlueValues"
     581           0 :                 case   7: mpCffLocal->maOtherBlues.push_back( nVal); break;     // "OtherBlues"
     582           0 :                 case   8: mpCffLocal->maFamilyBlues.push_back( nVal); break;    // "FamilyBlues"
     583           0 :                 case   9: mpCffLocal->maFamilyOtherBlues.push_back( nVal); break;// "FamilyOtherBlues"
     584           0 :                 case 912: mpCffLocal->maStemSnapH.push_back( nVal); break;      // "StemSnapH"
     585           0 :                 case 913: mpCffLocal->maStemSnapV.push_back( nVal); break;      // "StemSnapV"
     586           0 :                 default: break; // TODO: handle more delta-array dictops?
     587             :                 }
     588             :             }
     589           0 :             clear();
     590           0 :             } break;
     591             :         case 's':   // stringid (SID)
     592           0 :             nInt = popInt();
     593           0 :             switch( nOpId ) {
     594           0 :             case   2: mnFullNameSID = nInt; break;      // "FullName"
     595           0 :             case   3: mnFamilyNameSID = nInt; break;    // "FamilyName"
     596           0 :             case 938: mnFontNameSID = nInt; break;      // "FontName"
     597           0 :             default: break; // TODO: handle more string dictops?
     598             :             }
     599           0 :             break;
     600             :         case 'P':   // private dict
     601           0 :             mpCffLocal->mnPrivDictBase = popInt();
     602           0 :             mpCffLocal->mnPrivDictSize = popInt();
     603           0 :             break;
     604             :         case 'r': { // ROS operands
     605           0 :             int nSid1 = popInt();
     606           0 :             int nSid2 = popInt();
     607             :             (void)nSid1; // TODO: use
     608             :             (void)nSid2; // TODO: use
     609           0 :             nVal = popVal();
     610           0 :             mbCIDFont = true;
     611           0 :             } break;
     612             :         case 't':   // CharstringType
     613           0 :             nInt = popInt();
     614           0 :             setCharStringType( nInt );
     615           0 :             break;
     616             :         }
     617             : 
     618           0 :         return;
     619             :     }
     620             : 
     621           0 :     if( (c >= 32) || (c == 28) ) {
     622             : //      --mpReadPtr;
     623           0 :         read2push();
     624           0 :     } else if( c == 29 ) {      // longint
     625           0 :         ++mpReadPtr;            // skip 29
     626           0 :         int nS32 = mpReadPtr[0] << 24;
     627           0 :         nS32 += mpReadPtr[1] << 16;
     628           0 :         nS32 += mpReadPtr[2] << 8;
     629           0 :         nS32 += mpReadPtr[3] << 0;
     630             :         if( (sizeof(nS32) != 4) && (nS32 & (1U<<31)))
     631             :             nS32 |= (~0U) << 31;    // assuming 2s complement
     632           0 :         mpReadPtr += 4;
     633           0 :         nVal = static_cast<ValType>(nS32);
     634           0 :         push( nVal );
     635           0 :     } else if( c == 30) {       // real number
     636           0 :         ++mpReadPtr; // skip 30
     637           0 :         const RealType fReal = readRealVal();
     638             :         // push value onto stack
     639           0 :         nVal = fReal;
     640           0 :         push( nVal);
     641             :     }
     642             : }
     643             : 
     644           0 : void CffSubsetterContext::read2push()
     645             : {
     646           0 :     ValType aVal = 0;
     647             : 
     648           0 :     const U8*& p = mpReadPtr;
     649           0 :     const U8 c = *p;
     650           0 :     if( c == 28 ) {
     651           0 :         short nS16 = (p[1] << 8) + p[2];
     652             :         if( (sizeof(nS16) != 2) && (nS16 & (1<<15)))
     653             :             nS16 |= (~0U) << 15;    // assuming 2s complement
     654           0 :         aVal = nS16;
     655           0 :         p += 3;
     656           0 :     } else if( c <= 246 ) {     // -107..+107
     657           0 :         aVal = static_cast<ValType>(p[0] - 139);
     658           0 :         p += 1;
     659           0 :     } else if( c <= 250 ) {     // +108..+1131
     660           0 :         aVal = static_cast<ValType>(((p[0] << 8) + p[1]) - 63124);
     661           0 :         p += 2;
     662           0 :     } else if( c <= 254 ) {     // -108..-1131
     663           0 :         aVal = static_cast<ValType>(64148 - ((p[0] << 8) + p[1]));
     664           0 :         p += 2;
     665             :     } else /*if( c == 255)*/ {  // Fixed16.16
     666           0 :         int nS32 = (p[1] << 24) + (p[2] << 16) + (p[3] << 8) + p[4];
     667           0 :         if( (sizeof(nS32) != 2) && (nS32 & (1U<<31)))
     668           0 :             nS32 |= (~0U) << 31;    // assuming 2s complement
     669           0 :         aVal = static_cast<ValType>(nS32 * (1.0 / 0x10000));
     670           0 :         p += 5;
     671             :     }
     672             : 
     673           0 :     push( aVal);
     674           0 : }
     675             : 
     676           0 : void CffSubsetterContext::writeType1Val( ValType aVal)
     677             : {
     678           0 :     U8* pOut = mpWritePtr;
     679             : 
     680           0 :     int nInt = static_cast<int>(aVal);
     681           0 :     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 {
     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()
     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 :         sal_Int32 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()
     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 = (*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()
    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()
    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 : bool CffSubsetterContext::initialCffRead()
    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 sal_Int32 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           0 :         if (static_cast<size_t>(mnFDAryCount) >= SAL_N_ELEMENTS(maCffLocal))
    1505             :         {
    1506             :             SAL_INFO("vcl.fonts", "CffSubsetterContext: too many CFF in font");
    1507           0 :             return false;
    1508             :         }
    1509             : 
    1510             :         // read FDArray details to get access to the PRIVDICTs
    1511           0 :         for( int i = 0; i < mnFDAryCount; ++i) {
    1512           0 :             mpCffLocal = &maCffLocal[i];
    1513           0 :             seekIndexData( mnFontDictBase, i);
    1514           0 :             while( mpReadPtr < mpReadEnd)
    1515           0 :                 readDictOp();
    1516             :             assert( mpReadPtr == mpReadEnd);
    1517             :         }
    1518             :     }
    1519             : 
    1520           0 :     for( int i = 0; i < mnFDAryCount; ++i) {
    1521           0 :         mpCffLocal = &maCffLocal[i];
    1522             : 
    1523             :         // get the PrivateDict index
    1524             :         // (we got mnPrivDictSize and mnPrivDictBase from TOPDICT or FDArray)
    1525           0 :         if( mpCffLocal->mnPrivDictSize != 0) {
    1526             :             assert( mpCffLocal->mnPrivDictSize > 0);
    1527             :             // get the PrivDict data
    1528           0 :             mpReadPtr = mpBasePtr + mpCffLocal->mnPrivDictBase;
    1529           0 :             mpReadEnd = mpReadPtr + mpCffLocal->mnPrivDictSize;
    1530             :             assert( mpReadEnd <= mpBaseEnd);
    1531             :             // read PrivDict details
    1532           0 :             while( mpReadPtr < mpReadEnd)
    1533           0 :                 readDictOp();
    1534             :         }
    1535             : 
    1536             :         // prepare access to the LocalSubrs (we got mnLocalSubrOffs from PRIVDICT)
    1537           0 :         if( mpCffLocal->mnLocalSubrOffs) {
    1538             :             // read LocalSubrs summary
    1539           0 :             mpCffLocal->mnLocalSubrBase = mpCffLocal->mnPrivDictBase + mpCffLocal->mnLocalSubrOffs;
    1540           0 :             mpReadPtr = mpBasePtr + mpCffLocal->mnLocalSubrBase;
    1541           0 :             const int nSubrCount = (mpReadPtr[0] << 8) + mpReadPtr[1];
    1542           0 :             mpCffLocal->mnLocalSubrCount = nSubrCount;
    1543           0 :             mpCffLocal->mnLocalSubrBias = (nSubrCount<1240)?107:(nSubrCount<33900)?1131:32768;
    1544             : //          seekIndexEnd( mpCffLocal->mnLocalSubrBase);
    1545             :         }
    1546             :     }
    1547             : 
    1548             :     // ignore the Notices info
    1549             : 
    1550           0 :     return true;
    1551             : }
    1552             : 
    1553             : // get a cstring from a StringID
    1554           0 : const char* CffSubsetterContext::getString( int nStringID)
    1555             : {
    1556             :     // get a standard string if possible
    1557             :     const static int nStdStrings = sizeof(pStringIds)/sizeof(*pStringIds);
    1558           0 :     if( (nStringID >= 0) && (nStringID < nStdStrings))
    1559           0 :         return pStringIds[ nStringID];
    1560             : 
    1561             :     // else get the string from the StringIndex table
    1562           0 :     const U8* pReadPtr = mpReadPtr;
    1563           0 :     const U8* pReadEnd = mpReadEnd;
    1564           0 :     nStringID -= nStdStrings;
    1565           0 :     int nLen = seekIndexData( mnStringIdxBase, nStringID);
    1566             :     // assert( nLen >= 0);
    1567             :     // TODO: just return the undecorated name
    1568             :     // TODO: get rid of static char buffer
    1569             :     static char aNameBuf[ 2560];
    1570           0 :     if( nLen < 0) {
    1571           0 :         sprintf( aNameBuf, "name[%d].notfound!", nStringID);
    1572             :     } else {
    1573           0 :         const int nMaxLen = sizeof(aNameBuf) - 1;
    1574           0 :         if( nLen >= nMaxLen)
    1575           0 :             nLen = nMaxLen;
    1576           0 :         for( int i = 0; i < nLen; ++i)
    1577           0 :             aNameBuf[i] = *(mpReadPtr++);
    1578           0 :         aNameBuf[ nLen] = '\0';
    1579             :     }
    1580           0 :     mpReadPtr = pReadPtr;
    1581           0 :     mpReadEnd = pReadEnd;
    1582           0 :     return aNameBuf;
    1583             : }
    1584             : 
    1585             : // access a CID's FDSelect table
    1586           0 : int CffSubsetterContext::getFDSelect( int nGlyphIndex) const
    1587             : {
    1588             :     assert( nGlyphIndex >= 0);
    1589             :     assert( nGlyphIndex < mnCharStrCount);
    1590           0 :     if( !mbCIDFont)
    1591           0 :         return 0;
    1592             : 
    1593           0 :     const U8* pReadPtr = mpBasePtr + mnFDSelectBase;
    1594           0 :     const U8 nFDSelFormat = *(pReadPtr++);
    1595           0 :     switch( nFDSelFormat) {
    1596             :         case 0: { // FDSELECT format 0
    1597           0 :                 pReadPtr += nGlyphIndex;
    1598           0 :                 const U8 nFDIdx = *(pReadPtr++);
    1599           0 :                 return nFDIdx;
    1600             :             } //break;
    1601             :         case 3: { // FDSELECT format 3
    1602           0 :                 const U16 nRangeCount = (pReadPtr[0]<<8) + pReadPtr[1];
    1603             :                 assert( nRangeCount > 0);
    1604             :                 assert( nRangeCount <= mnCharStrCount);
    1605           0 :                 U16 nPrev = (pReadPtr[2]<<8) + pReadPtr[3];
    1606             :                 assert( nPrev == 0);
    1607             :                 (void)nPrev;
    1608           0 :                 pReadPtr += 4;
    1609             :                 // TODO? binary search
    1610           0 :                 for( int i = 0; i < nRangeCount; ++i) {
    1611           0 :                     const U8 nFDIdx = pReadPtr[0];
    1612           0 :                     const U16 nNext = (pReadPtr[1]<<8) + pReadPtr[2];
    1613             :                     assert( nPrev < nNext);
    1614           0 :                     if( nGlyphIndex < nNext)
    1615           0 :                         return nFDIdx;
    1616           0 :                     pReadPtr += 3;
    1617           0 :                     nPrev = nNext;
    1618             :                 }
    1619           0 :             } break;
    1620             :         default:    // invalid FDselect format
    1621           0 :             fprintf( stderr, "invalid CFF.FdselType=%d\n", nFDSelFormat);
    1622           0 :             break;
    1623             :     }
    1624             : 
    1625             :     assert( false);
    1626           0 :     return -1;
    1627             : }
    1628             : 
    1629           0 : int CffSubsetterContext::getGlyphSID( int nGlyphIndex) const
    1630             : {
    1631           0 :     if( nGlyphIndex == 0)
    1632           0 :         return 0;       // ".notdef"
    1633             :     assert( nGlyphIndex >= 0);
    1634             :     assert( nGlyphIndex < mnCharStrCount);
    1635           0 :     if( (nGlyphIndex < 0) || (nGlyphIndex >= mnCharStrCount))
    1636           0 :         return -1;
    1637             : 
    1638             :     // get the SID/CID from the Charset table
    1639           0 :      const U8* pReadPtr = mpBasePtr + mnCharsetBase;
    1640           0 :     const U8 nCSetFormat = *(pReadPtr++);
    1641           0 :     int nGlyphsToSkip = nGlyphIndex - 1;
    1642           0 :     switch( nCSetFormat) {
    1643             :         case 0: // charset format 0
    1644           0 :             pReadPtr += 2 * nGlyphsToSkip;
    1645           0 :             nGlyphsToSkip = 0;
    1646           0 :             break;
    1647             :         case 1: // charset format 1
    1648           0 :             while( nGlyphsToSkip >= 0) {
    1649           0 :                 const int nLeft = pReadPtr[2];
    1650           0 :                 if( nGlyphsToSkip <= nLeft)
    1651           0 :                     break;
    1652           0 :                 nGlyphsToSkip -= nLeft + 1;
    1653           0 :                 pReadPtr += 3;
    1654             :             }
    1655           0 :             break;
    1656             :         case 2: // charset format 2
    1657           0 :             while( nGlyphsToSkip >= 0) {
    1658           0 :                 const int nLeft = (pReadPtr[2]<<8) + pReadPtr[3];
    1659           0 :                 if( nGlyphsToSkip <= nLeft)
    1660           0 :                     break;
    1661           0 :                 nGlyphsToSkip -= nLeft + 1;
    1662           0 :                 pReadPtr += 4;
    1663             :             }
    1664           0 :             break;
    1665             :         default:
    1666           0 :             fprintf( stderr, "ILLEGAL CFF-Charset format %d\n", nCSetFormat);
    1667           0 :             return -2;
    1668             :     }
    1669             : 
    1670           0 :     int nSID = (pReadPtr[0]<<8) + pReadPtr[1];
    1671           0 :     nSID += nGlyphsToSkip;
    1672             :     // NOTE: for CID-fonts the resulting SID is interpreted as CID
    1673           0 :     return nSID;
    1674             : }
    1675             : 
    1676             : // NOTE: the result becomes invalid with the next call to this method
    1677           0 : const char* CffSubsetterContext::getGlyphName( int nGlyphIndex)
    1678             : {
    1679             :     // the first glyph is always the .notdef glyph
    1680           0 :     const char* pGlyphName = ".notdef";
    1681           0 :     if( nGlyphIndex == 0)
    1682           0 :         return pGlyphName;
    1683             : 
    1684             :     // prepare a result buffer
    1685             :     // TODO: get rid of static buffer
    1686             :     static char aDefaultGlyphName[64];
    1687           0 :     pGlyphName = aDefaultGlyphName;
    1688             : 
    1689             :     // get the glyph specific name
    1690           0 :     const int nSID = getGlyphSID( nGlyphIndex);
    1691           0 :     if( nSID < 0)           // default glyph name
    1692           0 :         sprintf( aDefaultGlyphName, "gly%03d", nGlyphIndex);
    1693           0 :     else if( mbCIDFont)     // default glyph name in CIDs
    1694           0 :          sprintf( aDefaultGlyphName, "cid%03d", nSID);
    1695             :     else {                  // glyph name from string table
    1696           0 :         const char* pSidName = getString( nSID);
    1697             :         // check validity of glyph name
    1698           0 :         if( pSidName) {
    1699           0 :             const char* p = pSidName;
    1700           0 :             while( (*p >= '0') && (*p <= 'z')) ++p;
    1701           0 :             if( (p >= pSidName+1) && (*p == '\0'))
    1702           0 :                 pGlyphName = pSidName;
    1703             :         }
    1704             :         // if needed invent a fallback name
    1705           0 :         if( pGlyphName != pSidName)
    1706           0 :              sprintf( aDefaultGlyphName, "bad%03d", nSID);
    1707             :     }
    1708             : 
    1709           0 :      return pGlyphName;
    1710             : }
    1711             : 
    1712             : class Type1Emitter
    1713             : {
    1714             : public:
    1715             :     explicit    Type1Emitter( FILE* pOutFile, bool bPfbSubset = true);
    1716             :     ~Type1Emitter();
    1717             :     void        setSubsetName( const char* );
    1718             : 
    1719             :     size_t      emitRawData( const char* pData, size_t nLength) const;
    1720             :     void        emitAllRaw();
    1721             :     void        emitAllHex();
    1722             :     void        emitAllCrypted();
    1723             :     int         tellPos() const;
    1724             :     size_t      updateLen( int nTellPos, size_t nLength);
    1725             :     void        emitValVector( const char* pLineHead, const char* pLineTail, const ValVector&);
    1726             : private:
    1727             :     FILE*       mpFileOut;
    1728             :     bool        mbCloseOutfile;
    1729             :     char        maBuffer[MAX_T1OPS_SIZE];   // TODO: dynamic allocation
    1730             :     int         mnEECryptR;
    1731             : public:
    1732             :     char*       mpPtr;
    1733             : 
    1734             :     char        maSubsetName[256];
    1735             :     bool        mbPfbSubset;
    1736             :     int         mnHexLineCol;
    1737             : };
    1738             : 
    1739           0 : Type1Emitter::Type1Emitter( FILE* pOutFile, bool bPfbSubset)
    1740             : :   mpFileOut( pOutFile)
    1741             : ,   mbCloseOutfile( false)
    1742             : ,   mnEECryptR( 55665)  // default eexec seed, TODO: mnEECryptSeed
    1743             : ,   mpPtr( maBuffer)
    1744             : ,   mbPfbSubset( bPfbSubset)
    1745           0 : ,   mnHexLineCol( 0)
    1746             : {
    1747           0 :     maSubsetName[0] = '\0';
    1748           0 : }
    1749             : 
    1750           0 : Type1Emitter::~Type1Emitter()
    1751             : {
    1752           0 :     if( !mpFileOut)
    1753           0 :         return;
    1754           0 :     if( mbCloseOutfile )
    1755           0 :         fclose( mpFileOut);
    1756           0 :     mpFileOut = NULL;
    1757           0 : }
    1758             : 
    1759           0 : void Type1Emitter::setSubsetName( const char* pSubsetName)
    1760             : {
    1761           0 :     maSubsetName[0] = '\0';
    1762           0 :     if( pSubsetName)
    1763           0 :         strncpy( maSubsetName, pSubsetName, sizeof(maSubsetName));
    1764           0 :     maSubsetName[sizeof(maSubsetName)-1] = '\0';
    1765           0 : }
    1766             : 
    1767           0 : int Type1Emitter::tellPos() const
    1768             : {
    1769           0 :     int nTellPos = ftell( mpFileOut);
    1770           0 :     return nTellPos;
    1771             : }
    1772             : 
    1773           0 : size_t Type1Emitter::updateLen( int nTellPos, size_t nLength)
    1774             : {
    1775             :     // update PFB segment header length
    1776             :     U8 cData[4];
    1777           0 :     cData[0] = static_cast<U8>(nLength >>  0);
    1778           0 :     cData[1] = static_cast<U8>(nLength >>  8);
    1779           0 :     cData[2] = static_cast<U8>(nLength >> 16);
    1780           0 :     cData[3] = static_cast<U8>(nLength >> 24);
    1781           0 :     const long nCurrPos = ftell(mpFileOut);
    1782           0 :     if (nCurrPos < 0)
    1783           0 :         return 0;
    1784           0 :     if (fseek( mpFileOut, nTellPos, SEEK_SET) != 0)
    1785           0 :         return 0;
    1786           0 :     size_t nWrote = fwrite(cData, 1, sizeof(cData), mpFileOut);
    1787           0 :     if( nCurrPos >= 0)
    1788           0 :         (void)fseek(mpFileOut, nCurrPos, SEEK_SET);
    1789           0 :     return nWrote;
    1790             : }
    1791             : 
    1792           0 : inline size_t Type1Emitter::emitRawData(const char* pData, size_t nLength) const
    1793             : {
    1794           0 :     return fwrite( pData, 1, nLength, mpFileOut);
    1795             : }
    1796             : 
    1797           0 : inline void Type1Emitter::emitAllRaw()
    1798             : {
    1799             :     // writeout raw data
    1800             :     assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
    1801           0 :     emitRawData( maBuffer, mpPtr - maBuffer);
    1802             :     // reset the raw buffer
    1803           0 :     mpPtr = maBuffer;
    1804           0 : }
    1805             : 
    1806           0 : inline void Type1Emitter::emitAllHex()
    1807             : {
    1808             :     assert( (mpPtr - maBuffer) < (int)sizeof(maBuffer));
    1809           0 :     for( const char* p = maBuffer; p < mpPtr;) {
    1810             :         // convert binary chunk to hex
    1811             :         char aHexBuf[0x4000];
    1812           0 :         char* pOut = aHexBuf;
    1813           0 :         while( (p < mpPtr) && (pOut < aHexBuf+sizeof(aHexBuf)-4)) {
    1814             :             // convert each byte to hex
    1815           0 :             char cNibble = (*p >> 4) & 0x0F;
    1816           0 :             cNibble += (cNibble < 10) ? '0' : 'A'-10;
    1817           0 :             *(pOut++) = cNibble;
    1818           0 :             cNibble = *(p++) & 0x0F;
    1819           0 :             cNibble += (cNibble < 10) ? '0' : 'A'-10;
    1820           0 :             *(pOut++) = cNibble;
    1821             :             // limit the line length
    1822           0 :             if( (++mnHexLineCol & 0x3F) == 0)
    1823           0 :                 *(pOut++) = '\n';
    1824             :         }
    1825             :         // writeout hex-converted chunk
    1826           0 :         emitRawData( aHexBuf, pOut-aHexBuf);
    1827             :     }
    1828             :     // reset the raw buffer
    1829           0 :     mpPtr = maBuffer;
    1830           0 : }
    1831             : 
    1832           0 : void Type1Emitter::emitAllCrypted()
    1833             : {
    1834             :     // apply t1crypt
    1835           0 :     for( char* p = maBuffer; p < mpPtr; ++p) {
    1836           0 :         *p ^= (mnEECryptR >> 8);
    1837           0 :         mnEECryptR = (*reinterpret_cast<U8*>(p) + mnEECryptR) * 52845 + 22719;
    1838             :     }
    1839             : 
    1840             :     // emit the t1crypt result
    1841           0 :     if( mbPfbSubset)
    1842           0 :         emitAllRaw();
    1843             :     else
    1844           0 :         emitAllHex();
    1845           0 : }
    1846             : 
    1847             : // #i110387# quick-and-dirty double->ascii conversion
    1848             : // needed because sprintf/ecvt/etc. alone are too localized (LC_NUMERIC)
    1849             : // also strip off trailing zeros in fraction while we are at it
    1850           0 : inline int dbl2str( char* pOut, double fVal, int nPrecision=6)
    1851             : {
    1852           0 :     const int nLen = psp::getValueOfDouble( pOut, fVal, nPrecision);
    1853           0 :     return nLen;
    1854             : }
    1855             : 
    1856           0 : void Type1Emitter::emitValVector( const char* pLineHead, const char* pLineTail,
    1857             :     const ValVector& rVector)
    1858             : {
    1859             :     // ignore empty vectors
    1860           0 :     if( rVector.empty())
    1861           0 :         return;
    1862             : 
    1863             :     // emit the line head
    1864           0 :     mpPtr += sprintf( mpPtr, "%s", pLineHead);
    1865             :     // emit the vector values
    1866           0 :     ValVector::value_type aVal = 0;
    1867           0 :     for( ValVector::const_iterator it = rVector.begin();;) {
    1868           0 :         aVal = *it;
    1869           0 :         if( ++it == rVector.end() )
    1870           0 :             break;
    1871           0 :         mpPtr += dbl2str( mpPtr, aVal);
    1872           0 :         *(mpPtr++) = ' ';
    1873           0 :     }
    1874             :     // emit the last value
    1875           0 :     mpPtr += dbl2str( mpPtr, aVal);
    1876             :     // emit the line tail
    1877           0 :     mpPtr += sprintf( mpPtr, "%s", pLineTail);
    1878             : }
    1879             : 
    1880           0 : bool CffSubsetterContext::emitAsType1( Type1Emitter& rEmitter,
    1881             :     const sal_GlyphId* pReqGlyphIds, const U8* pReqEncoding,
    1882             :     GlyphWidth* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rFSInfo)
    1883             : {
    1884             :     // prepare some fontdirectory details
    1885             :     static const int nUniqueIdBase = 4100000; // using private-interchange UniqueIds
    1886             :     static int nUniqueId = nUniqueIdBase;
    1887           0 :     ++nUniqueId;
    1888             : 
    1889           0 :     char* pFontName = rEmitter.maSubsetName;
    1890           0 :     if( !*pFontName ) {
    1891           0 :         if( mnFontNameSID) {
    1892             :             // get the fontname directly if available
    1893           0 :             strncpy( pFontName, getString( mnFontNameSID), sizeof(rEmitter.maSubsetName) - 1);
    1894           0 :             pFontName[sizeof(rEmitter.maSubsetName) - 1] = 0;
    1895           0 :         } else if( mnFullNameSID) {
    1896             :             // approximate fontname as fullname-whitespace
    1897           0 :             const char* pI = getString( mnFullNameSID);
    1898           0 :             char* pO = pFontName;
    1899           0 :             const char* pLimit = pFontName + sizeof(rEmitter.maSubsetName) - 1;
    1900           0 :             while( pO < pLimit) {
    1901           0 :                 const char c = *(pI++);
    1902           0 :                 if( c != ' ')
    1903           0 :                     *(pO++) = c;
    1904           0 :                 if( !c)
    1905           0 :                     break;
    1906             :             }
    1907           0 :             *pO = '\0';
    1908             :         } else {
    1909             :             // fallback name of last resort
    1910           0 :             strncpy( pFontName, "DummyName", sizeof(rEmitter.maSubsetName));
    1911             :         }
    1912             :     }
    1913           0 :     const char* pFullName = pFontName;
    1914           0 :     const char* pFamilyName = pFontName;
    1915             : 
    1916           0 :     char*& pOut = rEmitter.mpPtr; // convenience reference, TODO: cleanup
    1917             : 
    1918             :     // create a PFB+Type1 header
    1919           0 :     if( rEmitter.mbPfbSubset ) {
    1920             :         static const char aPfbHeader[] = "\x80\x01\x00\x00\x00\x00";
    1921           0 :         rEmitter.emitRawData( aPfbHeader, sizeof(aPfbHeader)-1);
    1922             :     }
    1923             : 
    1924           0 :     pOut += sprintf( pOut, "%%!FontType1-1.0: %s 001.003\n", rEmitter.maSubsetName);
    1925             :     // emit TOPDICT
    1926           0 :     pOut += sprintf( pOut,
    1927             :         "11 dict begin\n"   // TODO: dynamic entry count for TOPDICT
    1928             :         "/FontType 1 def\n"
    1929           0 :         "/PaintType 0 def\n");
    1930           0 :     pOut += sprintf( pOut, "/FontName /%s def\n", rEmitter.maSubsetName);
    1931           0 :     pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
    1932             :     // emit FontMatrix
    1933           0 :     if( maFontMatrix.size() == 6)
    1934           0 :         rEmitter.emitValVector( "/FontMatrix [", "]readonly def\n", maFontMatrix);
    1935             :     else // emit default FontMatrix if needed
    1936           0 :         pOut += sprintf( pOut, "/FontMatrix [0.001 0 0 0.001 0 0]readonly def\n");
    1937             :     // emit FontBBox
    1938           0 :     if( maFontBBox.size() == 4)
    1939           0 :         rEmitter.emitValVector( "/FontBBox {", "}readonly def\n", maFontBBox);
    1940             :     else // emit default FontBBox if needed
    1941           0 :         pOut += sprintf( pOut, "/FontBBox {0 0 999 999}readonly def\n");
    1942             :     // emit FONTINFO into TOPDICT
    1943           0 :     pOut += sprintf( pOut,
    1944             :         "/FontInfo 2 dict dup begin\n"  // TODO: check fontinfo entry count
    1945             :         " /FullName (%s) readonly def\n"
    1946             :         " /FamilyName (%s) readonly def\n"
    1947             :         "end readonly def\n",
    1948           0 :             pFullName, pFamilyName);
    1949             : 
    1950           0 :     pOut += sprintf( pOut,
    1951             :         "/Encoding 256 array\n"
    1952           0 :         "0 1 255 {1 index exch /.notdef put} for\n");
    1953           0 :     for( int i = 1; (i < nGlyphCount) && (i < 256); ++i) {
    1954           0 :         const char* pGlyphName = getGlyphName( pReqGlyphIds[i]);
    1955           0 :         pOut += sprintf( pOut, "dup %d /%s put\n", pReqEncoding[i], pGlyphName);
    1956             :     }
    1957           0 :     pOut += sprintf( pOut, "readonly def\n");
    1958           0 :     pOut += sprintf( pOut,
    1959             :         // TODO: more topdict entries
    1960             :         "currentdict end\n"
    1961           0 :         "currentfile eexec\n");
    1962             : 
    1963             :     // emit PFB header
    1964           0 :     rEmitter.emitAllRaw();
    1965           0 :     if( rEmitter.mbPfbSubset) {
    1966             :         // update PFB header segment
    1967           0 :         const int nPfbHeaderLen = rEmitter.tellPos() - 6;
    1968           0 :         rEmitter.updateLen( 2, nPfbHeaderLen);
    1969             : 
    1970             :         // prepare start of eexec segment
    1971           0 :         rEmitter.emitRawData( "\x80\x02\x00\x00\x00\x00", 6);   // segment start
    1972             :     }
    1973           0 :     const int nEExecSegTell = rEmitter.tellPos();
    1974             : 
    1975             :     // which always starts with a privdict
    1976             :     // count the privdict entries
    1977           0 :     int nPrivEntryCount = 9;
    1978             : #if !defined(IGNORE_HINTS)
    1979             :     // emit blue hints only if non-default values
    1980           0 :     nPrivEntryCount += int(!mpCffLocal->maOtherBlues.empty());
    1981           0 :     nPrivEntryCount += int(!mpCffLocal->maFamilyBlues.empty());
    1982           0 :     nPrivEntryCount += int(!mpCffLocal->maFamilyOtherBlues.empty());
    1983           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueScale != 0.0);
    1984           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueShift != 0.0);
    1985           0 :     nPrivEntryCount += int(mpCffLocal->mfBlueFuzz != 0.0);
    1986             :     // emit stem hints only if non-default values
    1987           0 :     nPrivEntryCount += int(mpCffLocal->maStemStdHW != 0);
    1988           0 :     nPrivEntryCount += int(mpCffLocal->maStemStdVW != 0);
    1989           0 :     nPrivEntryCount += int(!mpCffLocal->maStemSnapH.empty());
    1990           0 :     nPrivEntryCount += int(!mpCffLocal->maStemSnapV.empty());
    1991             :     // emit other hints only if non-default values
    1992           0 :     nPrivEntryCount += int(mpCffLocal->mfExpFactor != 0.0);
    1993           0 :     nPrivEntryCount += int(mpCffLocal->mnLangGroup != 0);
    1994           0 :     nPrivEntryCount += int(mpCffLocal->mnLangGroup == 1);
    1995           0 :     nPrivEntryCount += int(mpCffLocal->mbForceBold);
    1996             : #endif // IGNORE_HINTS
    1997             :     // emit the privdict header
    1998           0 :     pOut += sprintf( pOut,
    1999             :         "\110\104\125 "
    2000             :         "dup\n/Private %d dict dup begin\n"
    2001             :         "/RD{string currentfile exch readstring pop}executeonly def\n"
    2002             :         "/ND{noaccess def}executeonly def\n"
    2003             :         "/NP{noaccess put}executeonly def\n"
    2004             :         "/MinFeature{16 16}ND\n"
    2005             :         "/password 5839 def\n",     // TODO: mnRDCryptSeed?
    2006           0 :             nPrivEntryCount);
    2007             : 
    2008             : #if defined(IGNORE_HINTS)
    2009             :     pOut += sprintf( pOut, "/BlueValues []ND\n");   // BlueValues are mandatory
    2010             : #else
    2011             :     // emit blue hint related privdict entries
    2012           0 :     if( !mpCffLocal->maBlueValues.empty())
    2013           0 :         rEmitter.emitValVector( "/BlueValues [", "]ND\n", mpCffLocal->maBlueValues);
    2014             :     else
    2015           0 :         pOut += sprintf( pOut, "/BlueValues []ND\n"); // default to empty BlueValues
    2016           0 :     rEmitter.emitValVector( "/OtherBlues [", "]ND\n", mpCffLocal->maOtherBlues);
    2017           0 :     rEmitter.emitValVector( "/FamilyBlues [", "]ND\n", mpCffLocal->maFamilyBlues);
    2018           0 :     rEmitter.emitValVector( "/FamilyOtherBlues [", "]ND\n", mpCffLocal->maFamilyOtherBlues);
    2019             : 
    2020           0 :     if( mpCffLocal->mfBlueScale) {
    2021           0 :         pOut += sprintf( pOut, "/BlueScale ");
    2022           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueScale, 6);
    2023           0 :         pOut += sprintf( pOut, " def\n");
    2024             :     }
    2025           0 :     if( mpCffLocal->mfBlueShift) {  // default BlueShift==7
    2026           0 :         pOut += sprintf( pOut, "/BlueShift ");
    2027           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueShift);
    2028           0 :         pOut += sprintf( pOut, " def\n");
    2029             :     }
    2030           0 :     if( mpCffLocal->mfBlueFuzz) {       // default BlueFuzz==1
    2031           0 :         pOut += sprintf( pOut, "/BlueFuzz ");
    2032           0 :         pOut += dbl2str( pOut, mpCffLocal->mfBlueFuzz);
    2033           0 :         pOut += sprintf( pOut, " def\n");
    2034             :     }
    2035             : 
    2036             :     // emit stem hint related privdict entries
    2037           0 :     if( mpCffLocal->maStemStdHW) {
    2038           0 :         pOut += sprintf( pOut, "/StdHW [");
    2039           0 :         pOut += dbl2str( pOut, mpCffLocal->maStemStdHW);
    2040           0 :         pOut += sprintf( pOut, "] def\n");
    2041             :     }
    2042           0 :     if( mpCffLocal->maStemStdVW) {
    2043           0 :         pOut += sprintf( pOut, "/StdVW [");
    2044           0 :         pOut += dbl2str( pOut, mpCffLocal->maStemStdVW);
    2045           0 :         pOut += sprintf( pOut, "] def\n");
    2046             :     }
    2047           0 :     rEmitter.emitValVector( "/StemSnapH [", "]ND\n", mpCffLocal->maStemSnapH);
    2048           0 :     rEmitter.emitValVector( "/StemSnapV [", "]ND\n", mpCffLocal->maStemSnapV);
    2049             : 
    2050             :     // emit other hints
    2051           0 :     if( mpCffLocal->mbForceBold)
    2052           0 :         pOut += sprintf( pOut, "/ForceBold true def\n");
    2053           0 :     if( mpCffLocal->mnLangGroup != 0)
    2054           0 :         pOut += sprintf( pOut, "/LanguageGroup %d def\n", mpCffLocal->mnLangGroup);
    2055           0 :     if( mpCffLocal->mnLangGroup == 1) // compatibility with ancient printers
    2056           0 :         pOut += sprintf( pOut, "/RndStemUp false def\n");
    2057           0 :     if( mpCffLocal->mfExpFactor) {
    2058           0 :         pOut += sprintf( pOut, "/ExpansionFactor ");
    2059           0 :         pOut += dbl2str( pOut, mpCffLocal->mfExpFactor);
    2060           0 :         pOut += sprintf( pOut, " def\n");
    2061             :     }
    2062             : #endif // IGNORE_HINTS
    2063             : 
    2064             :     // emit remaining privdict entries
    2065           0 :     pOut += sprintf( pOut, "/UniqueID %d def\n", nUniqueId);
    2066             :     // TODO?: more privdict entries?
    2067             : 
    2068             :     static const char aOtherSubrs[] =
    2069             :         "/OtherSubrs\n"
    2070             :         "% Dummy code for faking flex hints\n"
    2071             :         "[ {} {} {} {systemdict /internaldict known not {pop 3}\n"
    2072             :         "{1183615869 systemdict /internaldict get exec\n"
    2073             :         "dup /startlock known\n"
    2074             :         "{/startlock get exec}\n"
    2075             :         "{dup /strtlck known\n"
    2076             :         "{/strtlck get exec}\n"
    2077             :         "{pop 3}\nifelse}\nifelse}\nifelse\n} executeonly\n"
    2078             :         "] ND\n";
    2079           0 :     memcpy( pOut, aOtherSubrs, sizeof(aOtherSubrs)-1);
    2080           0 :     pOut += sizeof(aOtherSubrs)-1;
    2081             : 
    2082             :     // emit used GlobalSubr charstrings
    2083             :     // these are the just the default subrs
    2084             :     // TODO: do we need them as the flex hints are resolved differently?
    2085             :     static const char aSubrs[] =
    2086             :         "/Subrs 5 array\n"
    2087             :         "dup 0 15 RD \x5F\x3D\x6B\xAC\x3C\xBD\x74\x3D\x3E\x17\xA0\x86\x58\x08\x85 NP\n"
    2088             :         "dup 1 9 RD \x5F\x3D\x6B\xD8\xA6\xB5\x68\xB6\xA2 NP\n"
    2089             :         "dup 2 9 RD \x5F\x3D\x6B\xAC\x39\x46\xB9\x43\xF9 NP\n"
    2090             :         "dup 3 5 RD \x5F\x3D\x6B\xAC\xB9 NP\n"
    2091             :         "dup 4 12 RD \x5F\x3D\x6B\xAC\x3E\x5D\x48\x54\x62\x76\x39\x03 NP\n"
    2092             :         "ND\n";
    2093           0 :     memcpy( pOut, aSubrs, sizeof(aSubrs)-1);
    2094           0 :     pOut += sizeof(aSubrs)-1;
    2095             : 
    2096             :     // TODO: emit more GlobalSubr charstrings?
    2097             :     // TODO: emit used LocalSubr charstrings?
    2098             : 
    2099             :     // emit the CharStrings for the requested glyphs
    2100           0 :     pOut += sprintf( pOut,
    2101           0 :         "2 index /CharStrings %d dict dup begin\n", nGlyphCount);
    2102           0 :     rEmitter.emitAllCrypted();
    2103           0 :     for( int i = 0; i < nGlyphCount; ++i) {
    2104           0 :         const int nCffGlyphId = pReqGlyphIds[i];
    2105             :         assert( (nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount));
    2106             :         // get privdict context matching to the glyph
    2107           0 :         const int nFDSelect = getFDSelect( nCffGlyphId);
    2108           0 :         if( nFDSelect < 0)
    2109           0 :             continue;
    2110           0 :         mpCffLocal = &maCffLocal[ nFDSelect];
    2111             :         // convert the Type2op charstring to its Type1op counterpart
    2112           0 :         const int nT2Len = seekIndexData( mnCharStrBase, nCffGlyphId);
    2113             :         assert( nT2Len > 0);
    2114             :         U8 aType1Ops[ MAX_T1OPS_SIZE]; // TODO: dynamic allocation
    2115           0 :         const int nT1Len = convert2Type1Ops( mpCffLocal, mpReadPtr, nT2Len, aType1Ops);
    2116             :         // get the glyph name
    2117           0 :         const char* pGlyphName = getGlyphName( nCffGlyphId);
    2118             :         // emit the encrypted Type1op charstring
    2119           0 :         pOut += sprintf( pOut, "/%s %d RD ", pGlyphName, nT1Len);
    2120           0 :         memcpy( pOut, aType1Ops, nT1Len);
    2121           0 :         pOut += nT1Len;
    2122           0 :         pOut += sprintf( pOut, " ND\n");
    2123           0 :         rEmitter.emitAllCrypted();
    2124             :         // provide individual glyphwidths if requested
    2125           0 :         if( pGlyphWidths ) {
    2126           0 :             ValType aCharWidth = getCharWidth();
    2127           0 :             if( maFontMatrix.size() >= 4)
    2128           0 :                 aCharWidth *= 1000.0F * maFontMatrix[0];
    2129           0 :             pGlyphWidths[i] = static_cast<GlyphWidth>(aCharWidth);
    2130             :         }
    2131             :     }
    2132           0 :     pOut += sprintf( pOut, "end end\nreadonly put\nput\n");
    2133           0 :     pOut += sprintf( pOut, "dup/FontName get exch definefont pop\n");
    2134           0 :     pOut += sprintf( pOut, "mark currentfile closefile\n");
    2135           0 :     rEmitter.emitAllCrypted();
    2136             : 
    2137             :     // mark stop of eexec encryption
    2138           0 :      if( rEmitter.mbPfbSubset) {
    2139           0 :         const int nEExecLen = rEmitter.tellPos() - nEExecSegTell;
    2140           0 :         rEmitter.updateLen( nEExecSegTell-4, nEExecLen);
    2141             :      }
    2142             : 
    2143             :     // create PFB footer
    2144             :     static const char aPfxFooter[] = "\x80\x01\x14\x02\x00\x00\n" // TODO: check segment len
    2145             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2146             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2147             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2148             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2149             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2150             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2151             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2152             :         "0000000000000000000000000000000000000000000000000000000000000000\n"
    2153             :         "cleartomark\n"
    2154             :         "\x80\x03";
    2155           0 :      if( rEmitter.mbPfbSubset)
    2156           0 :         rEmitter.emitRawData( aPfxFooter, sizeof(aPfxFooter)-1);
    2157             :     else
    2158           0 :         rEmitter.emitRawData( aPfxFooter+6, sizeof(aPfxFooter)-9);
    2159             : 
    2160             :     // provide details to the subset requesters, TODO: move into own method?
    2161             :     // note: Top and Bottom are flipped between Type1 and VCL
    2162             :     // note: the rest of VCL expects the details below to be scaled like for an emUnits==1000 font
    2163           0 :     ValType fXFactor = 1.0;
    2164           0 :     ValType fYFactor = 1.0;
    2165           0 :     if( maFontMatrix.size() >= 4) {
    2166           0 :         fXFactor = 1000.0F * maFontMatrix[0];
    2167           0 :         fYFactor = 1000.0F * maFontMatrix[3];
    2168             :     }
    2169           0 :     rFSInfo.m_aFontBBox = Rectangle( Point( static_cast<sal_Int32>(maFontBBox[0] * fXFactor),
    2170           0 :                                         static_cast<sal_Int32>(maFontBBox[1] * fYFactor) ),
    2171           0 :                                     Point( static_cast<sal_Int32>(maFontBBox[2] * fXFactor),
    2172           0 :                                         static_cast<sal_Int32>(maFontBBox[3] * fYFactor) ) );
    2173             :     // PDF-Spec says the values below mean the ink bounds!
    2174             :     // TODO: use better approximations for these ink bounds
    2175           0 :     rFSInfo.m_nAscent  = +rFSInfo.m_aFontBBox.Bottom(); // for capital letters
    2176           0 :     rFSInfo.m_nDescent = -rFSInfo.m_aFontBBox.Top();    // for all letters
    2177           0 :     rFSInfo.m_nCapHeight = rFSInfo.m_nAscent;           // for top-flat capital letters
    2178             : 
    2179           0 :     rFSInfo.m_nFontType = rEmitter.mbPfbSubset ? FontSubsetInfo::TYPE1_PFB : FontSubsetInfo::TYPE1_PFA;
    2180           0 :     rFSInfo.m_aPSName   = OUString( rEmitter.maSubsetName, strlen(rEmitter.maSubsetName), RTL_TEXTENCODING_UTF8 );
    2181             : 
    2182           0 :     return true;
    2183             : }
    2184             : 
    2185           0 : bool FontSubsetInfo::CreateFontSubsetFromCff( GlyphWidth* pOutGlyphWidths )
    2186             : {
    2187           0 :     CffSubsetterContext aCff( mpInFontBytes, mnInByteLength);
    2188           0 :     bool bRC = aCff.initialCffRead();
    2189           0 :     if (!bRC)
    2190           0 :         return bRC;
    2191             : 
    2192             :     // emit Type1 subset from the CFF input
    2193             :     // TODO: also support CFF->CFF subsetting (when PDF-export and PS-printing need it)
    2194           0 :     const bool bPfbSubset = (0 != (mnReqFontTypeMask & FontSubsetInfo::TYPE1_PFB));
    2195           0 :     Type1Emitter aType1Emitter( mpOutFile, bPfbSubset);
    2196           0 :     aType1Emitter.setSubsetName( mpReqFontName);
    2197             :     bRC = aCff.emitAsType1( aType1Emitter,
    2198             :         mpReqGlyphIds, mpReqEncodedIds,
    2199           0 :         pOutGlyphWidths, mnReqGlyphCount, *this);
    2200           0 :     return bRC;
    2201             : }
    2202             : 
    2203             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11