vcl/source/fontsubset/cff.cxx |  192 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 171 insertions(+), 21 deletions(-)

New commits:
commit 45318605384fac327a8b487e0132c4559d25a29c
Author:     Khaled Hosny <kha...@aliftype.com>
AuthorDate: Sat Oct 15 17:16:36 2022 +0200
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Sat Oct 15 22:39:31 2022 +0200

    tdf#123234: Fix subsetting CFF deprecated endchar
    
    Type 2 “endchar” operator can take 4 extra arguments and act like Type 1
    “seac” operator. Although this is documented as deprecated, the fonts in
    the bug report use it.
    
    Detect the presence of the extra arguments and output a “seac” operator
    for them.
    
    Change-Id: Ic24ec8fda586c82612f0cc13f2f31585d40ad9c4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139908
    Tested-by: Jenkins
    Reviewed-by: خالد حسني <kha...@aliftype.com>
    (cherry picked from commit 03ff7ee47c6b4e0dbf38a040825aaca53ce2ed28)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141396
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/vcl/source/fontsubset/cff.cxx b/vcl/source/fontsubset/cff.cxx
index d417c7fe1a42..ef756e2fa57f 100644
--- a/vcl/source/fontsubset/cff.cxx
+++ b/vcl/source/fontsubset/cff.cxx
@@ -162,6 +162,55 @@ static const char* pDictEscs[] = {
     "nFDArray",             "nFDSelect",        "sFontName"
 };
 
+static const char* pStandardEncoding[] = {
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", "space", "exclam", "quotedbl",
+    "numbersign", "dollar", "percent", "ampersand",
+    "quoteright", "parenleft", "parenright", "asterisk", "plus",
+    "comma", "hyphen", "period", "slash", "zero", "one", "two",
+    "three", "four", "five", "six", "seven", "eight", "nine",
+    "colon", "semicolon", "less", "equal", "greater",
+    "question", "at", "A", "B", "C", "D", "E", "F", "G", "H",
+    "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
+    "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
+    "bracketright", "asciicircum", "underscore", "quoteleft",
+    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
+    "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
+    "y", "z", "braceleft", "bar", "braceright", "asciitilde",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", "exclamdown",
+    "cent", "sterling", "fraction", "yen", "florin", "section",
+    "currency", "quotesingle", "quotedblleft", "guillemotleft",
+    "guilsinglleft", "guilsinglright", "fi", "fl", ".notdef",
+    "endash", "dagger", "daggerdbl", "periodcentered",
+    ".notdef", "paragraph", "bullet", "quotesinglbase",
+    "quotedblbase", "quotedblright", "guillemotright",
+    "ellipsis", "perthousand", ".notdef", "questiondown",
+    ".notdef", "grave", "acute", "circumflex", "tilde",
+    "macron", "breve", "dotaccent", "dieresis", ".notdef",
+    "ring", "cedilla", ".notdef", "hungarumlaut", "ogonek",
+    "caron", "emdash", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", ".notdef",
+    ".notdef", ".notdef", ".notdef", "AE", ".notdef",
+    "ordfeminine", ".notdef", ".notdef", ".notdef", ".notdef",
+    "Lslash", "Oslash", "OE", "ordmasculine", ".notdef",
+    ".notdef", ".notdef", ".notdef", ".notdef", "ae", ".notdef",
+    ".notdef", ".notdef", "dotlessi", ".notdef", ".notdef",
+    "lslash", "oslash", "oe", "germandbls", ".notdef",
+    ".notdef", ".notdef", ".notdef"
+};
+
 namespace {
 
 namespace TYPE1OP
@@ -261,6 +310,17 @@ struct CffLocal
     bool        mbForceBold;
 };
 
+const int MAX_T1OPS_SIZE = 81920; // TODO: use dynamic value
+
+struct CharString
+{
+    int nLen;
+    U8 aOps[MAX_T1OPS_SIZE];
+    int nCffGlyphId;
+    ValType aCharWidth;
+};
+
+
 class CffSubsetterContext
 :   private CffGlobal
 {
@@ -277,6 +337,8 @@ public:
                 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& );
 
 private:
+    void    convertCharStrings(const sal_GlyphId* pGlyphIds, int nGlyphCount,
+                std::vector<CharString>& rCharStrings);
     int     convert2Type1Ops( CffLocal*, const U8* pType2Ops, int nType2Len, 
U8* pType1Ops);
     void    convertOneTypeOp();
     void    convertOneTypeEsc();
@@ -306,6 +368,7 @@ private:
     int         getFDSelect( int nGlyphIndex) const;
     int         getGlyphSID( int nGlyphIndex) const;
     const char* getGlyphName( int nGlyphIndex);
+    bool        getBaseAccent(ValType aBase, ValType aAccent, int* nBase, int* 
nAccent);
 
     void    read2push();
     void    writeType1Val( ValType);
@@ -342,6 +405,9 @@ private:
     ValType mnHintStack[ NMAXHINTS];
 
     ValType maCharWidth;
+
+    bool    mbDoSeac;
+    std::vector<sal_GlyphId> maExtraGlyphIds;
 };
 
 }
@@ -362,6 +428,7 @@ CffSubsetterContext::CffSubsetterContext( const U8* 
pBasePtr, int nBaseLen)
     , mnHorzHintSize(0)
     , mnHintStack{}
     , maCharWidth(-1)
+    , mbDoSeac(true)
 {
 //  setCharStringType( 1);
     // TODO: new CffLocal[ mnFDAryCount];
@@ -888,6 +955,27 @@ void CffSubsetterContext::convertOneTypeOp()
         clear();
         break;
     case TYPE2OP::ENDCHAR:
+        if (size() >= 4 && mbDoSeac)
+        {
+            // Deprecated seac-like use of endchar (Adobe Technical Note #5177,
+            // Appendix C).
+            auto achar = popVal();
+            auto bchar = popVal();
+            auto ady = popVal();
+            auto adx = popVal();
+            int nBase, nAccent;
+            if (getBaseAccent(bchar, achar, &nBase, &nAccent))
+            {
+                maExtraGlyphIds.push_back(nBase);
+                maExtraGlyphIds.push_back(nAccent);
+                writeType1Val(0); // TODO accent sb
+                writeType1Val(adx);
+                writeType1Val(ady);
+                writeType1Val(bchar);
+                writeType1Val(achar);
+                writeTypeEsc(TYPE1OP::SEAC);
+            }
+        }
         if( mbNeedClose)
             writeTypeOp( TYPE1OP::CLOSEPATH);
         else
@@ -1106,8 +1194,6 @@ void CffSubsetterContext::callType2Subr( bool bGlobal, 
int nSubrNumber)
     mpReadEnd = pOldReadEnd;
 }
 
-const int MAX_T1OPS_SIZE = 81920; // TODO: use dynamic value
-
 int CffSubsetterContext::convert2Type1Ops( CffLocal* pCffLocal, const U8* 
const pT2Ops, int nT2Len, U8* const pT1Ops)
 {
     mpCffLocal = pCffLocal;
@@ -1605,6 +1691,28 @@ const char* CffSubsetterContext::getGlyphName( int 
nGlyphIndex)
     return pGlyphName;
 }
 
+bool CffSubsetterContext::getBaseAccent(ValType aBase, ValType aAccent, int* 
nBase, int* nAccent)
+{
+    bool bBase = false, bAccent = false;
+    for (int i = 0; i < mnCharStrCount; i++)
+    {
+        const char* pGlyphName = getGlyphName(i);
+        if (pGlyphName == pStandardEncoding[int(aBase)])
+        {
+            *nBase = i;
+            bBase = true;
+        }
+        if (pGlyphName == pStandardEncoding[int(aAccent)])
+        {
+            *nAccent = i;
+            bAccent = true;
+        }
+        if (bBase && bAccent)
+            return true;
+    }
+    return false;
+}
+
 namespace {
 
 class Type1Emitter
@@ -1772,6 +1880,47 @@ void Type1Emitter::emitValVector( const char* pLineHead, 
const char* pLineTail,
     mpPtr += sprintf( mpPtr, "%s", pLineTail);
 }
 
+void CffSubsetterContext::convertCharStrings(const sal_GlyphId* pGlyphIds, int 
nGlyphCount,
+                                             std::vector<CharString>& 
rCharStrings)
+{
+    // If we are doing extra glyphs used for seac operator, check for already
+    // converted glyphs.
+    bool bCheckDuplicates = !rCharStrings.empty();
+    rCharStrings.reserve(rCharStrings.size() + nGlyphCount);
+    for (int i = 0; i < nGlyphCount; ++i)
+    {
+        const int nCffGlyphId = pGlyphIds[i];
+        assert((nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount));
+
+        if (!bCheckDuplicates)
+        {
+            const auto& it
+                = std::find_if(rCharStrings.begin(), rCharStrings.end(),
+                               [&](const CharString& c) { return c.nCffGlyphId 
== nCffGlyphId; });
+            if (it != rCharStrings.end())
+                continue;
+        }
+
+        // get privdict context matching to the glyph
+        const int nFDSelect = getFDSelect(nCffGlyphId);
+        if (nFDSelect < 0)
+            continue;
+        mpCffLocal = &maCffLocal[nFDSelect];
+
+        // convert the Type2op charstring to its Type1op counterpart
+        const int nT2Len = seekIndexData(mnCharStrBase, nCffGlyphId);
+        assert(nT2Len > 0);
+
+        CharString aCharString;
+        const int nT1Len = convert2Type1Ops(mpCffLocal, mpReadPtr, nT2Len, 
aCharString.aOps);
+        aCharString.nLen = nT1Len;
+        aCharString.nCffGlyphId = nCffGlyphId;
+        aCharString.aCharWidth = maCharWidth;
+
+        rCharStrings.push_back(aCharString);
+    }
+}
+
 void CffSubsetterContext::emitAsType1( Type1Emitter& rEmitter,
     const sal_GlyphId* pReqGlyphIds, const U8* pReqEncoding,
     sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rFSInfo)
@@ -1986,33 +2135,34 @@ void CffSubsetterContext::emitAsType1( Type1Emitter& 
rEmitter,
     // TODO: emit used LocalSubr charstrings?
 
     // emit the CharStrings for the requested glyphs
+    std::vector<CharString> aCharStrings;
+    mbDoSeac = true;
+    convertCharStrings(pReqGlyphIds, nGlyphCount, aCharStrings);
+
+    // The previous convertCharStrings might collect extra glyphs used in seac
+    // operator, convert them as well
+    if (!maExtraGlyphIds.empty())
+    {
+        mbDoSeac = false;
+        convertCharStrings(maExtraGlyphIds.data(), maExtraGlyphIds.size(), 
aCharStrings);
+    }
     pOut += sprintf( pOut,
-        "2 index /CharStrings %d dict dup begin\n", nGlyphCount);
+        "2 index /CharStrings %zu dict dup begin\n", aCharStrings.size());
     rEmitter.emitAllCrypted();
-    for( int i = 0; i < nGlyphCount; ++i) {
-        const int nCffGlyphId = pReqGlyphIds[i];
-        assert( (nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount));
-        // get privdict context matching to the glyph
-        const int nFDSelect = getFDSelect( nCffGlyphId);
-        if( nFDSelect < 0)
-            continue;
-        mpCffLocal = &maCffLocal[ nFDSelect];
-        // convert the Type2op charstring to its Type1op counterpart
-        const int nT2Len = seekIndexData( mnCharStrBase, nCffGlyphId);
-        assert( nT2Len > 0);
-        U8 aType1Ops[ MAX_T1OPS_SIZE]; // TODO: dynamic allocation
-        const int nT1Len = convert2Type1Ops( mpCffLocal, mpReadPtr, nT2Len, 
aType1Ops);
+    for (size_t i = 0; i < aCharStrings.size(); i++)
+    {
+        const auto& rCharString = aCharStrings[i];
         // get the glyph name
-        const char* pGlyphName = getGlyphName( nCffGlyphId);
+        const char* pGlyphName = getGlyphName(rCharString.nCffGlyphId);
         // emit the encrypted Type1op charstring
-        pOut += sprintf( pOut, "/%s %d RD ", pGlyphName, nT1Len);
-        memcpy( pOut, aType1Ops, nT1Len);
-        pOut += nT1Len;
+        pOut += sprintf( pOut, "/%s %d RD ", pGlyphName, rCharString.nLen);
+        memcpy( pOut, rCharString.aOps, rCharString.nLen);
+        pOut += rCharString.nLen;
         pOut += sprintf( pOut, " ND\n");
         rEmitter.emitAllCrypted();
         // provide individual glyphwidths if requested
         if( pGlyphWidths ) {
-            ValType aCharWidth = maCharWidth;
+            ValType aCharWidth = rCharString.aCharWidth;
             if( maFontMatrix.size() >= 4)
                 aCharWidth *= 1000.0F * maFontMatrix[0];
             pGlyphWidths[i] = static_cast<sal_Int32>(aCharWidth);

Reply via email to