include/sal/log-areas.dox | 1 vcl/source/font/PhysicalFontFace.cxx | 53 ++++---- vcl/source/fontsubset/cff.cxx | 215 +++++++++++++++++++++-------------- 3 files changed, 161 insertions(+), 108 deletions(-)
New commits: commit 56050d1b891c11730c2bbda8e252947a2856da57 Author: Khaled Hosny <[email protected]> AuthorDate: Sun Mar 8 23:22:12 2026 +0200 Commit: Khaled Hosny <[email protected]> CommitDate: Mon Mar 9 11:21:24 2026 +0100 Shuffle code around to avoid creating a cmap table that we will not use In the CFF case we will use the bare CFF font, so no need to waste time creating a cmap table we will throw away any way. Change-Id: I5e2e6adb1743d82b1f4084da8570f1f65922cf86 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201229 Tested-by: Jenkins Reviewed-by: Khaled Hosny <[email protected]> diff --git a/vcl/source/font/PhysicalFontFace.cxx b/vcl/source/font/PhysicalFontFace.cxx index b98764270b7c..b0ed50a8797a 100644 --- a/vcl/source/font/PhysicalFontFace.cxx +++ b/vcl/source/font/PhysicalFontFace.cxx @@ -545,13 +545,16 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer, Point(XUnits(nUPEM, xMax), XUnits(nUPEM, yMax))); } - hb_blob_t* pSubsetBlob = nullptr; - comphelper::ScopeGuard aBuilderBlobGuard([&]() { hb_blob_destroy(pSubsetBlob); }); - - // HarfBuzz creates a Unicode cmap, but we need a fake cmap based on pEncoding, - // so we use face builder construct a new face based in the subset table, - // and create a new cmap table and add it to the new face. + hb_blob_t* pCFFBlob = hb_face_reference_table(pSubsetFace, HB_TAG('C', 'F', 'F', ' ')); + comphelper::ScopeGuard aCFFBlobGuard([&]() { hb_blob_destroy(pCFFBlob); }); + if (pCFFBlob == hb_blob_get_empty()) { + // This is not a font with CFF table, so we will create a TTF font subset. + rInfo.m_nFontType = FontType::SFNT_TTF; + + // HarfBuzz creates a Unicode cmap, but we need a fake cmap based on pEncoding, + // so we use face builder construct a new face based in the subset table, + // and create a new cmap table and add it to the new face. hb_face_t* pBuilderFace = hb_face_builder_create(); comphelper::ScopeGuard aBuilderFaceGuard([&]() { hb_face_destroy(pBuilderFace); }); unsigned int nSubsetTableCount = hb_face_get_table_tags(pSubsetFace, 0, nullptr, nullptr); @@ -597,12 +600,18 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer, hb_face_builder_add_table(pBuilderFace, HB_TAG('c', 'm', 'a', 'p'), pCmapBlob); hb_blob_destroy(pCmapBlob); - pSubsetBlob = hb_face_reference_blob(pBuilderFace); - } + hb_blob_t* pSubsetBlob = hb_face_reference_blob(pBuilderFace); + comphelper::ScopeGuard aBuilderBlobGuard([&]() { hb_blob_destroy(pSubsetBlob); }); - hb_blob_t* pCFFBlob = hb_face_reference_table(pSubsetFace, HB_TAG('C', 'F', 'F', ' ')); - comphelper::ScopeGuard aCFFBlobGuard([&]() { hb_blob_destroy(pCFFBlob); }); - if (pCFFBlob != hb_blob_get_empty()) + unsigned int nSubsetLength; + const char* pSubsetData = hb_blob_get_data(pSubsetBlob, &nSubsetLength); + if (!pSubsetData || !nSubsetLength) + return false; + + rOutBuffer.assign(reinterpret_cast<const sal_uInt8*>(pSubsetData), + reinterpret_cast<const sal_uInt8*>(pSubsetData) + nSubsetLength); + } + else { // Ideally we should be outputting a CFF (Type1C) font here, but I couldn’t get it to work. // So we oconvert it to Type1 font instead. @@ -611,24 +620,16 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer, rInfo.m_nFontType = FontType::TYPE1_PFB; unsigned int nCffLen; - const unsigned char* pCffData - = reinterpret_cast<const unsigned char*>(hb_blob_get_data(pCFFBlob, &nCffLen)); - - if (!ConvertCFFfontToType1(pCffData, nCffLen, rOutBuffer, rInfo)) + const char* pCffData = hb_blob_get_data(pCFFBlob, &nCffLen); + if (!pCffData || !nCffLen) return false; - } - else - { - rInfo.m_nFontType = FontType::SFNT_TTF; - unsigned int nSubsetLength; - const char* pSubsetData = nullptr; - pSubsetData = hb_blob_get_data(pSubsetBlob, &nSubsetLength); - if (!pSubsetData || !nSubsetLength) + if (!ConvertCFFfontToType1(reinterpret_cast<const unsigned char*>(pCffData), nCffLen, + rOutBuffer, rInfo)) + { + SAL_WARN("vcl.fonts.cff", "Failed to convert CFF data to Type 1 font"); return false; - - rOutBuffer.assign(reinterpret_cast<const sal_uInt8*>(pSubsetData), - reinterpret_cast<const sal_uInt8*>(pSubsetData) + nSubsetLength); + } } return true; commit 0a17b69e0784bcff70e759db4db26f486222e11f Author: Khaled Hosny <[email protected]> AuthorDate: Sun Mar 8 23:16:38 2026 +0200 Commit: Khaled Hosny <[email protected]> CommitDate: Mon Mar 9 11:21:15 2026 +0100 Assert less in CFF to Type 1 code The top-level function already returns a boolean, make many of the internal functions return false in case of failure and propagate that instead of asserting in debug builds and continuing with bad input in release builds. Change-Id: I00c5f9552eb114af1f406f5f1c2ead50fe6b97e4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201228 Tested-by: Jenkins Reviewed-by: Khaled Hosny <[email protected]> diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox index 36e7d8a71c7a..c63d7650e5fe 100644 --- a/include/sal/log-areas.dox +++ b/include/sal/log-areas.dox @@ -501,6 +501,7 @@ certain functionality. @li @c vcl.filter.webp @li @c vcl.fonts - font-specific code @li @c vcl.fonts.detail +@li @c vcl.fonts.cff - CFF font subsetting-specific code @li @c vcl.gdi - the GDI part of VCL, devices, bitmaps, etc. @li @c vcl.gdi.wndproc - Windows Procedure part of VCL @li @c vcl.gdi.fontmetric diff --git a/vcl/source/fontsubset/cff.cxx b/vcl/source/fontsubset/cff.cxx index c178a003c799..9ce598af4804 100644 --- a/vcl/source/fontsubset/cff.cxx +++ b/vcl/source/fontsubset/cff.cxx @@ -727,15 +727,15 @@ public: explicit CffContext( const U8* pBasePtr, int nBaseLen); bool initialCffRead(); - void emitAsType1(class Type1Emitter&, FontSubsetInfo&); + bool emitAsType1(class Type1Emitter&, FontSubsetInfo&); private: - void convertCharStrings(std::vector<CharString>& rCharStrings, + bool convertCharStrings(std::vector<CharString>& rCharStrings, int nGlyphCount, const sal_GlyphId* pGlyphIds = nullptr); int convert2Type1Ops( CffLocal*, const U8* pType2Ops, int nType2Len, U8* pType1Ops); - void convertOneTypeOp(); - void convertOneTypeEsc(); - void callType2Subr( bool bGlobal, int nSubrNumber); + bool convertOneTypeOp(); + bool convertOneTypeEsc(); + bool callType2Subr( bool bGlobal, int nSubrNumber); sal_Int32 getReadOfs() const { return static_cast<sal_Int32>(mpReadPtr - mpBasePtr);} const U8* mpBasePtr; @@ -902,7 +902,7 @@ void CffContext::readDictOp() //TODO: if( nStackIdx > 0) int nInt = 0; switch( *pCmdName) { - default: SAL_WARN("vcl.fonts", "unsupported DictOp.type='" << *pCmdName << "'."); break; + default: SAL_WARN("vcl.fonts.cff", "unsupported DictOp.type='" << *pCmdName << "'."); break; case 'b': // bool nInt = popInt(); switch( nOpId) { @@ -1152,7 +1152,7 @@ void CffContext::writeCurveTo( int nStackPos, writeTypeOp( TYPE1OP::RCURVETO ); } -void CffContext::convertOneTypeOp() +bool CffContext::convertOneTypeOp() { const int nType2Op = *(mpReadPtr++); @@ -1160,7 +1160,8 @@ void CffContext::convertOneTypeOp() // convert each T2op switch( nType2Op) { case TYPE2OP::T2ESC: - convertOneTypeEsc(); + if (!convertOneTypeEsc()) + return false; break; case TYPE2OP::HSTEM: case TYPE2OP::VSTEM: @@ -1242,12 +1243,13 @@ void CffContext::convertOneTypeOp() { nInt = popInt(); const bool bGlobal = (nType2Op == TYPE2OP::CALLGSUBR); - callType2Subr( bGlobal, nInt); + if (!callType2Subr( bGlobal, nInt)) + return false; } break; case TYPE2OP::RETURN: // TODO: check that we are in a subroutine - return; + return true; case TYPE2OP::VMOVETO: case TYPE2OP::HMOVETO: if( mbNeedClose) @@ -1382,91 +1384,107 @@ void CffContext::convertOneTypeOp() read2push(); } else { popAll2Write( nType2Op); - assert(false && "TODO?"); + SAL_WARN("vcl.fonts.cff", "unhandled type2op " << nType2Op); + return false; } break; } + return true; } -void CffContext::convertOneTypeEsc() +bool CffContext::convertOneTypeEsc() { const int nType2Esc = *(mpReadPtr++); ValType* pTop = &mnValStack[ mnStackIdx-1]; // convert each T2op switch( nType2Esc) { case TYPE2OP::AND: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) & static_cast<int>(pTop[-1])); --mnStackIdx; break; case TYPE2OP::OR: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; pTop[0] = static_cast<ValType>(static_cast<int>(pTop[0]) | static_cast<int>(pTop[-1])); --mnStackIdx; break; case TYPE2OP::NOT: - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; pTop[0] = ValType(pTop[0] == 0); break; case TYPE2OP::ABS: - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; if( pTop[0] >= 0) break; [[fallthrough]]; case TYPE2OP::NEG: - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; pTop[0] = -pTop[0]; break; case TYPE2OP::ADD: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; pTop[0] += pTop[-1]; --mnStackIdx; break; case TYPE2OP::SUB: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; pTop[0] -= pTop[-1]; --mnStackIdx; break; case TYPE2OP::MUL: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; if( pTop[-1]) pTop[0] *= pTop[-1]; --mnStackIdx; break; case TYPE2OP::DIV: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; if( pTop[-1]) pTop[0] /= pTop[-1]; --mnStackIdx; break; case TYPE2OP::EQ: - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; pTop[0] = ValType(pTop[0] == pTop[-1]); --mnStackIdx; break; case TYPE2OP::DROP: - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; --mnStackIdx; break; case TYPE2OP::PUT: { - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; const int nIdx = static_cast<int>(pTop[0]); - assert( nIdx >= 0 ); - assert( nIdx < NMAXTRANS ); + if ( nIdx < 0 || nIdx >= NMAXTRANS ) + return false; mnTransVals[ nIdx] = pTop[-1]; mnStackIdx -= 2; break; } case TYPE2OP::GET: { - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; const int nIdx = static_cast<int>(pTop[0]); - assert( nIdx >= 0 ); - assert( nIdx < NMAXTRANS ); + if ( nIdx < 0 || nIdx >= NMAXTRANS ) + return false; pTop[0] = mnTransVals[ nIdx ]; break; } case TYPE2OP::IFELSE: { - assert( mnStackIdx >= 4 ); + if ( mnStackIdx < 4 ) + return false; if( pTop[-1] > pTop[0] ) pTop[-3] = pTop[-2]; mnStackIdx -= 3; @@ -1480,37 +1498,42 @@ void CffContext::convertOneTypeEsc() // TODO: implement break; case TYPE2OP::DUP: - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; pTop[+1] = pTop[0]; ++mnStackIdx; break; case TYPE2OP::EXCH: { - assert( mnStackIdx >= 2 ); + if ( mnStackIdx < 2 ) + return false; const ValType nVal = pTop[0]; pTop[0] = pTop[-1]; pTop[-1] = nVal; break; } case TYPE2OP::INDEX: { - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; const int nVal = static_cast<int>(pTop[0]); - assert( nVal >= 0 ); - assert( nVal < mnStackIdx-1 ); + if ( nVal < 0 || nVal >= mnStackIdx-1 ) + return false; pTop[0] = pTop[-1-nVal]; break; } case TYPE2OP::ROLL: { - assert( mnStackIdx >= 1 ); + if ( mnStackIdx < 1 ) + return false; const int nNum = static_cast<int>(pTop[0]); - assert( nNum >= 0); - assert( nNum < mnStackIdx-2 ); - (void)nNum; // TODO: implement + if ( nNum < 0 || nNum >= mnStackIdx-2 ) + return false; + // (void)nNum; // TODO: implement // TODO: implement: const int nOfs = static_cast<int>(pTop[-1]); mnStackIdx -= 2; break; } case TYPE2OP::HFLEX1: { - assert( mnStackIdx == 9); + if ( mnStackIdx != 9 ) + return false; writeCurveTo( mnStackIdx, -9, -8, -7, -6, -5, 0); writeCurveTo( mnStackIdx, -4, 0, -3, -2, -1, 0); @@ -1520,7 +1543,8 @@ void CffContext::convertOneTypeEsc() } break; case TYPE2OP::HFLEX: { - assert( mnStackIdx == 7); + if ( mnStackIdx != 7 ) + return false; ValType* pX = &mnValStack[ mnStackIdx]; pX[+1] = -pX[-5]; // temp: +dy5==-dy2 @@ -1532,7 +1556,8 @@ void CffContext::convertOneTypeEsc() } break; case TYPE2OP::FLEX: { - assert( mnStackIdx == 13 ); + if ( mnStackIdx != 13 ) + return false; writeCurveTo( mnStackIdx, -13, -12, -11, -10, -9, -8 ); writeCurveTo( mnStackIdx, -7, -6, -5, -4, -3, -2 ); // ignoring ValType nFlexDepth = mnValStack[ mnStackIdx-1 ]; @@ -1540,7 +1565,8 @@ void CffContext::convertOneTypeEsc() } break; case TYPE2OP::FLEX1: { - assert( mnStackIdx == 11 ); + if ( mnStackIdx != 11 ) + return false; // write the first part of the flex1-hinted curve writeCurveTo( mnStackIdx, -11, -10, -9, -8, -7, -6 ); @@ -1561,30 +1587,36 @@ void CffContext::convertOneTypeEsc() } break; default: - SAL_WARN("vcl.fonts", "unhandled type2esc " << nType2Esc); - assert( false); - break; + SAL_WARN("vcl.fonts.cff", "unhandled type2esc " << nType2Esc); + return false; } + return true; } -void CffContext::callType2Subr( bool bGlobal, int nSubrNumber) +bool CffContext::callType2Subr( bool bGlobal, int nSubrNumber) { const U8* const pOldReadPtr = mpReadPtr; const U8* const pOldReadEnd = mpReadEnd; if( bGlobal ) { nSubrNumber += mnGlobalSubrBias; - seekIndexData( mnGlobalSubrBase, nSubrNumber); + if (seekIndexData( mnGlobalSubrBase, nSubrNumber) < 0) + return false; } else { nSubrNumber += mpCffLocal->mnLocalSubrBias; - seekIndexData( mpCffLocal->mnLocalSubrBase, nSubrNumber); + if (seekIndexData( mpCffLocal->mnLocalSubrBase, nSubrNumber) < 0) + return false; } while( mpReadPtr < mpReadEnd) - convertOneTypeOp(); + { + if (!convertOneTypeOp()) + return false; + } mpReadPtr = pOldReadPtr; mpReadEnd = pOldReadEnd; + return true; } int CffContext::convert2Type1Ops( CffLocal* pCffLocal, const U8* const pT2Ops, int nT2Len, U8* const pT1Ops) @@ -1624,7 +1656,10 @@ int CffContext::convert2Type1Ops( CffLocal* pCffLocal, const U8* const pT2Ops, i mnHintSize=mnHorzHintSize=mnStackIdx=0; maCharWidth=-1;//####### mnCntrMask = 0; while( mpReadPtr < mpReadEnd) - convertOneTypeOp(); + { + if (!convertOneTypeOp()) + return -1; + } if( maCharWidth != -1 ) { // overwrite earlier charWidth value, which we only now have @@ -1723,7 +1758,8 @@ RealType CffContext::readRealVal() // prepare to access an element inside a CFF/CID index table int CffContext::seekIndexData( int nIndexBase, int nDataIndex) { - assert( (nIndexBase > 0) && (mpBasePtr + nIndexBase + 3 <= mpBaseEnd)); + if ( nIndexBase <= 0 || mpBasePtr + nIndexBase + 3 > mpBaseEnd) + return -1; if( nDataIndex < 0) return -1; mpReadPtr = mpBasePtr + nIndexBase; @@ -1734,7 +1770,7 @@ int CffContext::seekIndexData( int nIndexBase, int nDataIndex) mpReadPtr += 3 + (nDataOfsSz * nDataIndex); int nOfs1 = 0; switch( nDataOfsSz) { - default: SAL_WARN("vcl.fonts", " INVALID nDataOfsSz=" << nDataOfsSz); return -1; + default: SAL_WARN("vcl.fonts.cff", " INVALID nDataOfsSz=" << nDataOfsSz); return -1; case 1: nOfs1 = mpReadPtr[0]; break; case 2: nOfs1 = (mpReadPtr[0]<<8) + mpReadPtr[1]; break; case 3: nOfs1 = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2]; break; @@ -1752,10 +1788,8 @@ int CffContext::seekIndexData( int nIndexBase, int nDataIndex) mpReadPtr = mpBasePtr + (nIndexBase + 2) + nDataOfsSz * (nDataCount + 1) + nOfs1; mpReadEnd = mpReadPtr + (nOfs2 - nOfs1); - assert( nOfs1 >= 0); - assert( nOfs2 >= nOfs1); - assert( mpReadPtr <= mpBaseEnd); - assert( mpReadEnd <= mpBaseEnd); + if (nOfs1 < 0 || nOfs2 < nOfs1 || mpReadPtr > mpBaseEnd || mpReadEnd > mpBaseEnd) + return -1; return (nOfs2 - nOfs1); } @@ -1770,7 +1804,7 @@ void CffContext::seekIndexEnd( int nIndexBase) assert( mpReadPtr <= mpBaseEnd); int nEndOfs = 0; switch( nDataOfsSz) { - default: SAL_WARN("vcl.fonts", " INVALID nDataOfsSz=" << nDataOfsSz); return; + default: SAL_WARN("vcl.fonts.cff", " INVALID nDataOfsSz=" << nDataOfsSz); return; case 1: nEndOfs = mpReadPtr[0]; break; case 2: nEndOfs = (mpReadPtr[0]<<8) + mpReadPtr[1]; break; case 3: nEndOfs = (mpReadPtr[0]<<16) + (mpReadPtr[1]<<8) + mpReadPtr[2];break; @@ -1827,9 +1861,13 @@ bool CffContext::initialCffRead() const U8 nVerMinor = *(mpReadPtr++); const U8 nHeaderSize = *(mpReadPtr++); const U8 nOffsetSize = *(mpReadPtr++); - // TODO: is the version number useful for anything else? - assert( (nVerMajor == 1) && (nVerMinor == 0)); - (void)(nVerMajor + nVerMinor + nOffsetSize); // avoid compiler warnings + if (nVerMajor != 1 || nVerMinor != 0) + { + SAL_WARN("vcl.fonts.cff", "Unsupported CFF version: " << int(nVerMajor) << "." << int(nVerMinor)); + return false; + } + if (!nOffsetSize) + return false; // prepare access to the NameIndex mnNameIdxBase = nHeaderSize; @@ -1841,10 +1879,12 @@ bool CffContext::initialCffRead() const int nTopDictCount = (mpReadPtr[0]<<8) + mpReadPtr[1]; if( nTopDictCount) { for( int i = 0; i < nTopDictCount; ++i) { - seekIndexData( nTopDictBase, i); + if (seekIndexData( nTopDictBase, i) < 0) + return false; while( mpReadPtr < mpReadEnd) readDictOp(); - assert( mpReadPtr == mpReadEnd); + if (mpReadPtr != mpReadEnd) + return false; } } @@ -1877,17 +1917,19 @@ bool CffContext::initialCffRead() mnFDAryCount = (mpReadPtr[0]<<8) + mpReadPtr[1]; if (o3tl::make_unsigned(mnFDAryCount) >= SAL_N_ELEMENTS(maCffLocal)) { - SAL_INFO("vcl.fonts", "CffContext: too many CFF in font"); + SAL_INFO("vcl.fonts.cff", "CffContext: too many CFF in font"); return false; } // read FDArray details to get access to the PRIVDICTs for( int i = 0; i < mnFDAryCount; ++i) { mpCffLocal = &maCffLocal[i]; - seekIndexData( mnFontDictBase, i); + if (seekIndexData( mnFontDictBase, i) < 0) + return false; while( mpReadPtr < mpReadEnd) readDictOp(); - assert( mpReadPtr == mpReadEnd); + if (mpReadPtr != mpReadEnd) + return false; } } @@ -1951,8 +1993,9 @@ OString CffContext::getString( int nStringID) // access a CID's FDSelect table int CffContext::getFDSelect( int nGlyphIndex) const { - assert( nGlyphIndex >= 0); - assert( nGlyphIndex < mnCharStrCount); + if ( nGlyphIndex < 0 || nGlyphIndex >= mnCharStrCount) + return -1; + if( !mbCIDFont) return 0; @@ -1984,11 +2027,10 @@ int CffContext::getFDSelect( int nGlyphIndex) const } } break; default: // invalid FDselect format - SAL_WARN("vcl.fonts", "invalid CFF.FdselType=" << nFDSelFormat); + SAL_WARN("vcl.fonts.cff", "invalid CFF.FdselType=" << nFDSelFormat); break; } - assert( false); return -1; } @@ -2029,7 +2071,7 @@ int CffContext::getGlyphSID( int nGlyphIndex) const } break; default: - SAL_WARN("vcl.fonts", "ILLEGAL CFF-Charset format " << nCSetFormat); + SAL_WARN("vcl.fonts.cff", "ILLEGAL CFF-Charset format " << nCSetFormat); return -2; } @@ -2205,7 +2247,7 @@ void Type1Emitter::emitValVector( const char* pLineHead, const char* pLineTail, maBuffer.append( pLineTail); } -void CffContext::convertCharStrings(std::vector<CharString>& rCharStrings, +bool CffContext::convertCharStrings(std::vector<CharString>& rCharStrings, int nGlyphCount, const sal_GlyphId* pGlyphIds) { // If we are doing extra glyphs used for seac operator, check for already @@ -2215,7 +2257,8 @@ void CffContext::convertCharStrings(std::vector<CharString>& rCharStrings, for (int i = 0; i < nGlyphCount; ++i) { const int nCffGlyphId = pGlyphIds ? pGlyphIds[i] : i; - assert((nCffGlyphId >= 0) && (nCffGlyphId < mnCharStrCount)); + if ((nCffGlyphId < 0) || (nCffGlyphId >= mnCharStrCount)) + return false; if (!bCheckDuplicates) { @@ -2229,23 +2272,27 @@ void CffContext::convertCharStrings(std::vector<CharString>& rCharStrings, // get privdict context matching to the glyph const int nFDSelect = getFDSelect(nCffGlyphId); if (nFDSelect < 0) - continue; + return false; mpCffLocal = &maCffLocal[nFDSelect]; // convert the Type2op charstring to its Type1op counterpart const int nT2Len = seekIndexData(mnCharStrBase, nCffGlyphId); - assert(nT2Len > 0); + if (nT2Len <= 0) + return false; CharString aCharString; const int nT1Len = convert2Type1Ops(mpCffLocal, mpReadPtr, nT2Len, aCharString.aOps); + if (nT1Len < 0) + return false; aCharString.nLen = nT1Len; aCharString.nCffGlyphId = nCffGlyphId; rCharStrings.push_back(aCharString); } + return true; } -void CffContext::emitAsType1(Type1Emitter& rEmitter, FontSubsetInfo& rFSInfo) +bool CffContext::emitAsType1(Type1Emitter& rEmitter, FontSubsetInfo& rFSInfo) { OString aFontName = rFSInfo.m_aPSName.toUtf8(); if (aFontName.getLength() > 255) @@ -2437,14 +2484,16 @@ void CffContext::emitAsType1(Type1Emitter& rEmitter, FontSubsetInfo& rFSInfo) // emit the CharStrings for the requested glyphs std::vector<CharString> aCharStrings; mbDoSeac = true; - convertCharStrings(aCharStrings, mnCharStrCount); + if (!convertCharStrings(aCharStrings, mnCharStrCount)) + return false; // The previous convertCharStrings might collect extra glyphs used in seac // operator, convert them as well if (!maExtraGlyphIds.empty()) { mbDoSeac = false; - convertCharStrings(aCharStrings, maExtraGlyphIds.size(), maExtraGlyphIds.data()); + if (!convertCharStrings(aCharStrings, maExtraGlyphIds.size(), maExtraGlyphIds.data())) + return false; } rEmitter.maBuffer.append( "2 index /CharStrings " + OString::number(aCharStrings.size()) + " dict dup begin "); @@ -2486,6 +2535,8 @@ void CffContext::emitAsType1(Type1Emitter& rEmitter, FontSubsetInfo& rFSInfo) rEmitter.emitRawData( aPfxFooter, sizeof(aPfxFooter)-1); rFSInfo.m_nFontType = FontType::TYPE1_PFB; + + return true; } namespace vcl @@ -2495,15 +2546,15 @@ bool ConvertCFFfontToType1(const unsigned char* pFontBytes, int nByteLength, FontSubsetInfo& rInfo) { CffContext aCff(pFontBytes, nByteLength); - bool bRC = aCff.initialCffRead(); - if (!bRC) - return bRC; + if (!aCff.initialCffRead()) + return false; SvMemoryStream aStream; // emit Type1 font from the CFF input Type1Emitter aType1Emitter(aStream); - aCff.emitAsType1(aType1Emitter, rInfo); + if (!aCff.emitAsType1(aType1Emitter, rInfo)) + return false; rOutBuffer.assign(static_cast<const sal_uInt8*>(aStream.GetData()), static_cast<const sal_uInt8*>(aStream.GetData()) + aStream.Tell());
