/* * Copyright (C) Ascensio System SIA, 2009-2026 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation, together with the * additional terms provided in the LICENSE file. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA by email at info@onlyoffice.com * or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga, * LV-1050, Latvia, European Union. * * The interactive user interfaces in modified versions of the Program * are required to display Appropriate Legal Notices in accordance with * Section 5 of the GNU AGPL version 3. * * No trademark rights are granted under this License. * * All non-code elements of the Product, including illustrations, * icon sets, and technical writing content, are licensed under the * Creative Commons Attribution-ShareAlike 4.0 International License: * https://creativecommons.org/licenses/by-sa/4.0/legalcode * * This license applies only to such non-code elements and does not * modify or replace the licensing terms applicable to the Program's * source code, which remains licensed under the GNU Affero General * Public License v3. * * SPDX-License-Identifier: AGPL-3.0-only */ #include "PdfFont.h" #include "Adaptors.h" #include "../lib/xpdf/TextString.h" #include "../lib/xpdf/GfxFont.h" #include "../lib/xpdf/Lexer.h" #include "../lib/xpdf/Parser.h" #include "../lib/xpdf/CharCodeToUnicode.h" #include "../lib/xpdf/Decrypt.h" #include "../lib/fofi/FoFiTrueType.h" #include "../lib/fofi/FoFiType1C.h" #include "../lib/fofi/FoFiIdentifier.h" #include "../Resources/BaseFonts.h" #include "../../DesktopEditor/common/StringExt.h" #include "../../DesktopEditor/common/StringBuilder.h" #include "../../DesktopEditor/common/Path.h" #include "../../DesktopEditor/xml/include/xmlutils.h" #include "../../DesktopEditor/fontengine/ApplicationFonts.h" #ifndef BUILDING_WASM_MODULE #define FONTS_USE_AFM_SETTINGS #else #include "../../DesktopEditor/graphics/pro/js/wasm/src/serialize.h" #include "FontsWasm.h" #define FONTS_USE_ONLY_MEMORY_STREAMS #endif const std::vector arrCMap = {"GB-EUC-H", "GB-EUC-V", "GB-H", "GB-V", "GBpc-EUC-H", "GBpc-EUC-V", "GBK-EUC-H", "GBK-EUC-V", "GBKp-EUC-H", "GBKp-EUC-V", "GBK2K-H", "GBK2K-V", "GBT-H", "GBT-V", "GBTpc-EUC-H", "GBTpc-EUC-V", "UniGB-UCS2-H", "UniGB-UCS2-V", "UniGB-UTF8-H", "UniGB-UTF8-V", "UniGB-UTF16-H", "UniGB-UTF16-V", "UniGB-UTF32-H", "UniGB-UTF32-V", "B5pc-H", "B5pc-V", "B5-H", "B5-V", "HKscs-B5-H", "HKscs-B5-V", "HKdla-B5-H", "HKdla-B5-V", "HKdlb-B5-H", "HKdlb-B5-V", "HKgccs-B5-H", "HKgccs-B5-V", "HKm314-B5-H", "HKm314-B5-V", "HKm471-B5-H", "HKm471-B5-V", "ETen-B5-H", "ETen-B5-V", "ETenms-B5-H", "ETenms-B5-V", "ETHK-B5-H", "ETHK-B5-V", "CNS-EUC-H", "CNS-EUC-V", "CNS1-H", "CNS1-V", "CNS2-H", "CNS2-V", "UniCNS-UCS2-H", "UniCNS-UCS2-V", "UniCNS-UTF8-H", "UniCNS-UTF8-V", "UniCNS-UTF16-H", "UniCNS-UTF16-V", "UniCNS-UTF32-H", "UniCNS-UTF32-V", "78-EUC-H", "78-EUC-V", "78-H", "78-V", "78-RKSJ-H", "78-RKSJ-V", "78ms-RKSJ-H", "78ms-RKSJ-V","83pv-RKSJ-H", "90ms-RKSJ-H", "90ms-RKSJ-V", "90msp-RKSJ-H", "90msp-RKSJ-V", "90pv-RKSJ-H", "90pv-RKSJ-V", "Add-H", "Add-V", "Add-RKSJ-H", "Add-RKSJ-V", "EUC-H", "EUC-V", "Ext-RKSJ-H", "Ext-RKSJ-V", "H", "V", "NWP-H", "NWP-V", "RKSJ-H", "RKSJ-V", "UniJIS-UCS2-H", "UniJIS-UCS2-V", "UniJIS-UCS2-HW-H", "UniJIS-UCS2-HW-V", "UniJIS-UTF8-H", "UniJIS-UTF8-V", "UniJIS-UTF16-H", "UniJIS-UTF16-V", "UniJIS-UTF32-H", "UniJIS-UTF32-V", "UniJIS2004-UTF8-H", "UniJIS2004-UTF8-V", "UniJIS2004-UTF16-H", "UniJIS2004-UTF16-V", "UniJIS2004-UTF32-H", "UniJIS2004-UTF32-V", "UniJISPro-UCS2-V", "UniJISPro-UCS2-HW-V", "UniJISPro-UTF8-V", "UniJISX0213-UTF32-H", "UniJISX0213-UTF32-V", "UniJISX02132004-UTF32-H", "UniJISX02132004-UTF32-V", "WP-Symbol", "Hankaku", "Hiragana", "Katakana", "Roman", "KSC-EUC-H", "KSC-EUC-V", "KSC-H", "KSC-V", "KSC-Johab-H", "KSC-Johab-V", "KSCms-UHC-H", "KSCms-UHC-V", "KSCms-UHC-HW-H", "KSCms-UHC-HW-V", "KSCpc-EUC-H", "KSCpc-EUC-V", "UniKS-UCS2-H", "UniKS-UCS2-V", "UniKS-UTF8-H", "UniKS-UTF8-V", "UniKS-UTF16-H", "UniKS-UTF16-V", "UniKS-UTF32-H", "UniKS-UTF32-V", "UniAKR-UTF8-H", "UniAKR-UTF16-H", "UniAKR-UTF32-H"}; bool scanFonts(Dict *pResources, const std::vector& arrCMap, int nDepth, std::vector& arrUniqueResources) { if (nDepth > 5) return false; Object oFonts; if (pResources->lookup("Font", &oFonts)->isDict()) { for (int i = 0, nLength = oFonts.dictGetLength(); i < nLength; ++i) { Object oFont, oEncoding; if (!oFonts.dictGetVal(i, &oFont)->isDict() || !oFont.dictLookup("Encoding", &oEncoding)->isName()) { oFont.free(); oEncoding.free(); continue; } oFont.free(); char* sName = oEncoding.getName(); if (std::find(arrCMap.begin(), arrCMap.end(), sName) != arrCMap.end()) { oEncoding.free(); oFonts.free(); return true; } oEncoding.free(); } } oFonts.free(); auto fScanFonts = [pResources, nDepth, &arrUniqueResources](const std::vector& arrCMap, const char* sName) { Object oObject; if (!pResources->lookup(sName, &oObject)->isDict()) { oObject.free(); return false; } for (int i = 0, nLength = oObject.dictGetLength(); i < nLength; ++i) { Object oXObj, oResources; if (!oObject.dictGetVal(i, &oXObj)->isStream() || !oXObj.streamGetDict()->lookup("Resources", &oResources)->isDict()) { oXObj.free(); oResources.free(); continue; } Object oRef; if (oXObj.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end()) { oXObj.free(); oResources.free(); oRef.free(); continue; } arrUniqueResources.push_back(oRef.getRef().num); oXObj.free(); oRef.free(); if (scanFonts(oResources.getDict(), arrCMap, nDepth + 1, arrUniqueResources)) { oResources.free(); oObject.free(); return true; } oResources.free(); } oObject.free(); return false; }; if (fScanFonts(arrCMap, "XObject") || fScanFonts(arrCMap, "Pattern")) return true; Object oExtGState; if (!pResources->lookup("ExtGState", &oExtGState)->isDict()) { oExtGState.free(); return false; } for (int i = 0, nLength = oExtGState.dictGetLength(); i < nLength; ++i) { Object oGS, oSMask, oSMaskGroup, oResources; if (!oExtGState.dictGetVal(i, &oGS)->isDict() || !oGS.dictLookup("SMask", &oSMask)->isDict() || !oSMask.dictLookup("G", &oSMaskGroup)->isStream() || !oSMaskGroup.streamGetDict()->lookup("Resources", &oResources)->isDict()) { oGS.free(); oSMask.free(); oSMaskGroup.free(); oResources.free(); continue; } oGS.free(); oSMask.free(); Object oRef; if (oSMaskGroup.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end()) { oSMaskGroup.free(); oResources.free(); oRef.free(); continue; } arrUniqueResources.push_back(oRef.getRef().num); oSMaskGroup.free(); oRef.free(); if (scanFonts(oResources.getDict(), arrCMap, nDepth + 1, arrUniqueResources)) { oResources.free(); oExtGState.free(); return true; } oResources.free(); } oExtGState.free(); return false; } bool scanAPfonts(Object* oAnnot, const std::vector& arrCMap, std::vector& arrUniqueResources) { Object oAP; if (!oAnnot->dictLookup("AP", &oAP)->isDict()) { oAP.free(); return false; } auto fScanAPView = [&arrUniqueResources](Object* oAP, const std::vector& arrCMap, const char* sName) { Object oAPi, oRes; if (!oAP->dictLookup(sName, &oAPi)->isStream() || !oAPi.streamGetDict()->lookup("Resources", &oRes)->isDict()) { oAPi.free(); oRes.free(); return false; } Object oRef; if (oAPi.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end()) { oAPi.free(); oRes.free(); oRef.free(); return false; } arrUniqueResources.push_back(oRef.getRef().num); oAPi.free(); oRef.free(); bool bRes = scanFonts(oRes.getDict(), arrCMap, 0, arrUniqueResources); oRes.free(); return bRes; }; bool bRes = fScanAPView(&oAP, arrCMap, "N") || fScanAPView(&oAP, arrCMap, "D") || fScanAPView(&oAP, arrCMap, "R"); oAP.free(); return bRes; } bool CheckFontNameStyle(std::wstring& sName, const std::wstring& sStyle) { size_t nPos = 0; size_t nLenReplace = sStyle.length(); bool bRet = false; std::wstring sName2 = sName; NSStringExt::ToLower(sName2); while (std::wstring::npos != (nPos = sName2.find(sStyle, nPos))) { size_t nOffset = 0; if ((nPos > 0) && (sName2.at(nPos - 1) == '-' || sName2.at(nPos - 1) == ',')) { --nPos; ++nOffset; } bRet = true; sName.erase(nPos, nLenReplace + nOffset); sName2.erase(nPos, nLenReplace + nOffset); } return bRet; } std::wstring Normalize(const std::wstring& wsName) { std::wstring s = wsName; bool bIsRemove = false; size_t lastSpace = s.find_last_of(L' '); if (lastSpace != std::wstring::npos) { bIsRemove = true; for (size_t nIndex = lastSpace + 1; nIndex < s.size(); ++nIndex) { wchar_t nChar = s.at(nIndex); if (nChar < '0' || nChar > 'F' || (nChar > '9' && nChar < 'A')) { bIsRemove = false; break; } } if (bIsRemove) s.erase(lastSpace + 1); } bool bDummy1 = false, bDummy2 = false; PdfReader::CheckFontStylePDF(s, bDummy1, bDummy2); NSStringExt::ToLower(s); s.erase(std::remove_if(s.begin(), s.end(), [](wchar_t c){ return c == L' ' || c == L'-' || c == L'_'; }), s.end()); return s; }; std::wstring ComputeFontHash(XRef* pXref, GfxFont* pFont) { MD5State oMD5; md5Start(&oMD5); Ref* pRef = pFont->getID(); Object oRefObj, oFontObj; oRefObj.initRef(pRef->num, pRef->gen); oRefObj.fetch(pXref, &oFontObj); oRefObj.free(); if (oFontObj.isDict()) { const char* aFontObjNames[] = { "Name", "Subtype", "BaseFont", "Encoding" }; for (const char* sKey : aFontObjNames) { Object oItem; oFontObj.dictLookup(sKey, &oItem); if (oItem.isName()) md5Append(&oMD5, (BYTE*)oItem.getName(), strlen(oItem.getName())); oItem.free(); } Object oDescObj, oDescendantFonts; oFontObj.dictLookup("FontDescriptor", &oDescObj); if (!oDescObj.isDict()) { oDescObj.free(); if (oFontObj.dictLookup("DescendantFonts", &oDescendantFonts)->isArray()) { Object oDescendant; if (oDescendantFonts.arrayGet(0, &oDescendant)->isDict()) oDescendant.dictLookup("FontDescriptor", &oDescObj); oDescendant.free(); } oDescendantFonts.free(); } if (oDescObj.isDict()) { Object oItem; oDescObj.dictLookup("FontName", &oItem); if (oItem.isName()) md5Append(&oMD5, (BYTE*)oItem.getName(), strlen(oItem.getName())); oItem.free(); const char* aMetricNames[] = { "Flags", "ItalicAngle", "Ascent", "Descent", "CapHeight", "XHeight", "StemV", "StemH" }; for (const char* sName : aMetricNames) { oDescObj.dictLookup(sName, &oItem); if (oItem.isInt()) { int nVal = oItem.getInt(); md5Append(&oMD5, (BYTE*)&nVal, sizeof(int)); } oItem.free(); } oDescObj.dictLookup("FontBBox", &oItem); if (oItem.isArray() && oItem.arrayGetLength() == 4) { for (int i = 0; i < 4; i++) { Object oCoord; if (oItem.arrayGet(i, &oCoord)->isInt()) { int nVal = oCoord.getInt(); md5Append(&oMD5, (BYTE*)&nVal, sizeof(int)); } oCoord.free(); } } oItem.free(); } oDescObj.free(); } oFontObj.free(); Ref oEmbRef; if (pFont->getEmbeddedFontID(&oEmbRef)) { Object oRefObj, oStreamObj; oRefObj.initRef(oEmbRef.num, oEmbRef.gen); oRefObj.fetch(pXref, &oStreamObj); oRefObj.free(); if (oStreamObj.isStream()) { oStreamObj.streamReset(); const int nMaxBytes = 512; BYTE aBuf[nMaxBytes]; int nRead = 0; int nChar; while (nRead < nMaxBytes && (nChar = oStreamObj.streamGetChar()) != EOF) aBuf[nRead++] = (BYTE)nChar; oStreamObj.streamClose(); md5Append(&oMD5, aBuf, nRead); } oStreamObj.free(); } md5Finish(&oMD5); static const char aHexChars[] = "0123456789ABCDEF"; std::string sHex; sHex.reserve(32); for (int i = 0; i < 16; i++) { sHex += aHexChars[(oMD5.digest[i] >> 4) & 0x0F]; sHex += aHexChars[oMD5.digest[i] & 0x0F]; } return UTF8_TO_U(sHex); } void ReadFontData(const std::string& sData, PdfReader::CFontData* pFont) { size_t nSemicolon = 0; size_t nColon = sData.find(':'); while (nColon != std::string::npos && nColon > nSemicolon) { std::string sProperty = sData.substr(nSemicolon, nColon - nSemicolon); nSemicolon = sData.find(';', nSemicolon); nColon++; std::string sValue = sData.substr(nColon, nSemicolon - nColon); nColon = sData.find(':', nSemicolon); nSemicolon++; if (sProperty == "font-size") pFont->dFontSise = std::stod(sValue); else if (sProperty == "text-align") { // 0 start / left if (sValue == "center" || sValue == "middle") pFont->nAlign = 1; else if (sValue == "right" || sValue == "end") pFont->nAlign = 2; else if (sValue == "justify") pFont->nAlign = 3; } else if (sProperty == "color") { if (sValue[0] == '#') { sValue = sValue.substr(1); BYTE nColor1 = 0, nColor2 = 0, nColor3 = 0; if (sValue.length() == 6) sscanf(sValue.c_str(), "%2hhx%2hhx%2hhx", &nColor1, &nColor2, &nColor3); else if (sValue.length() == 3) { sscanf(sValue.c_str(), "%1hhx%1hhx%1hhx", &nColor1, &nColor2, &nColor3); nColor1 *= 17; nColor2 *= 17; nColor3 *= 17; } pFont->dColor[0] = (double)nColor1 / 255.0; pFont->dColor[1] = (double)nColor2 / 255.0; pFont->dColor[2] = (double)nColor3 / 255.0; } } else if (sProperty == "font-weight") { // 0 normal / 300 / 400 / 500 if (sValue == "normal" || sValue == "300" || sValue == "400" || sValue == "500") pFont->unFontFlags &= ~(1 << 0); else if (sValue == "bold" || sValue == "bolder" || sValue == "600" || sValue == "700" || sValue == "800" || sValue == "900") pFont->unFontFlags |= (1 << 0); } else if (sProperty == "font-style") { // 0 normal if (sValue == "normal") pFont->unFontFlags &= ~(1 << 1); else if (sValue == "italic" || sValue.find("oblique") != std::string::npos) pFont->unFontFlags |= (1 << 1); } else if (sProperty == "font-family") pFont->sFontFamily = sValue[0] == '\'' ? sValue.substr(1, sValue.length() - 2) : sValue; else if (sProperty == "text-decoration") { if (sValue.find("line-through") != std::string::npos) pFont->unFontFlags |= (1 << 3); if (sValue.find("word") != std::string::npos || sValue.find("underline") != std::string::npos) pFont->unFontFlags |= (1 << 4); if (sValue.find("none") != std::string::npos) { pFont->unFontFlags &= ~(1 << 3); pFont->unFontFlags &= ~(1 << 4); } } else if (sProperty == "vertical-align") { pFont->unFontFlags |= (1 << 5); pFont->dVAlign = std::stod(sValue); if (pFont->dVAlign == 0 && sValue[0] == '-') pFont->dVAlign = -0.01; } // font-stretch } } class CMemoryFontStream { public: BYTE* m_pData; int m_nSize; int m_nPos; bool m_bIsAttach; CMemoryFontStream() { m_pData = NULL; m_nSize = 0; m_nPos = 0; m_bIsAttach = false; } ~CMemoryFontStream() { if (NULL != m_pData && !m_bIsAttach) RELEASEARRAYOBJECTS(m_pData); } void fromStream(std::wstring& sStreamName) { NSFonts::IFontStream* pStream = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->Get(sStreamName); if (pStream) { LONG lSize = 0; pStream->GetMemory(m_pData, lSize); m_nSize = (int)lSize; m_nPos = 0; m_bIsAttach = true; } } void fromBuffer(BYTE* pData, int nSize) { if (pData) { m_pData = pData; m_nSize = nSize; m_nPos = 0; m_bIsAttach = true; } } void load(Object& oStreamObject) { int nCurrentSize = 0xFFFF; int nCurrentPos = 0; BYTE* pStream = new BYTE[nCurrentSize]; int nChar; while ((nChar = oStreamObject.streamGetChar()) != EOF) { if (nCurrentPos >= nCurrentSize) { int nNewSize = 2 * nCurrentSize; BYTE* pNewBuffer = new BYTE[nNewSize]; memcpy(pNewBuffer, pStream, nCurrentSize); RELEASEARRAYOBJECTS(pStream); pStream = pNewBuffer; nCurrentSize = nNewSize; } pStream[nCurrentPos++] = nChar; } m_pData = pStream; m_nSize = nCurrentPos; m_nPos = 0; } int getChar() { if (m_nPos >= m_nSize) return EOF; return m_pData[m_nPos++]; } void toStart() { m_nPos = 0; } }; static int readFromMemoryStream(void* data) { return ((CMemoryFontStream*)data)->getChar(); } namespace PdfReader { std::vector ReadRC(const std::string& sRC) { std::vector arrRC; XmlUtils::CXmlLiteReader oLightReader; if (sRC.empty() || !oLightReader.FromStringA(sRC) || !oLightReader.ReadNextNode() || oLightReader.GetNameA() != "body") return arrRC; CFontData oFontBase; while (oLightReader.MoveToNextAttribute()) { if (oLightReader.GetNameA() == "style") { ReadFontData(oLightReader.GetTextA(), &oFontBase); break; } } oLightReader.MoveToElement(); int nDepthP = oLightReader.GetDepth(); while (oLightReader.ReadNextSiblingNode2(nDepthP)) { if (oLightReader.GetNameA() != "p") continue; bool bRTL = false; while (oLightReader.MoveToNextAttribute()) { if (oLightReader.GetNameA() == "dir" && oLightReader.GetTextA() == "rtl") { bRTL = true; break; } } oLightReader.MoveToElement(); int nDepthSpan = oLightReader.GetDepth(); if (oLightReader.IsEmptyNode() || !oLightReader.ReadNextSiblingNode2(nDepthSpan)) continue; do { std::string sName = oLightReader.GetNameA(); if (sName == "span") { CFontData* pFont = new CFontData(oFontBase); while (oLightReader.MoveToNextAttribute()) { if (oLightReader.GetNameA() == "style") { ReadFontData(oLightReader.GetTextA(), pFont); break; } } oLightReader.MoveToElement(); if (bRTL) pFont->unFontFlags |= (1 << 7); pFont->sText = oLightReader.GetText2A(); arrRC.push_back(pFont); } else if (sName == "#text") { CFontData* pFont = new CFontData(oFontBase); if (bRTL) pFont->unFontFlags |= (1 << 7); pFont->sText = oLightReader.GetTextA(); arrRC.push_back(pFont); } } while (oLightReader.ReadNextSiblingNode2(nDepthSpan)); } return arrRC; } std::string GetRCFromDS(const std::string& sDS, Object* pContents, const std::vector& arrCFromDA) { NSStringUtils::CStringBuilder oRC; oRC += L"

"; TextString* s = new TextString(pContents->getString()); std::wstring wsContents = NSStringExt::CConverter::GetUnicodeFromUTF32(s->getUnicode(), s->getLength()); delete s; oRC.WriteEncodeXmlString(wsContents); oRC += L"

"; std::wstring wsRC = oRC.GetData(); return U_TO_UTF8(wsRC); } bool IsNeedCMap(PDFDoc* pDoc) { std::vector arrUniqueResources; for (int nPage = 1, nLastPage = pDoc->getNumPages(); nPage <= nLastPage; ++nPage) { Page* pPage = pDoc->getCatalog()->getPage(nPage); Dict* pResources = pPage->getResourceDict(); if (pResources && scanFonts(pResources, arrCMap, 0, arrUniqueResources)) return true; Object oAnnots; if (!pPage->getAnnots(&oAnnots)->isArray()) { oAnnots.free(); continue; } for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i) { Object oAnnot; if (!oAnnots.arrayGet(i, &oAnnot)->isDict()) { oAnnot.free(); continue; } Object oRefDR; if (oAnnot.dictLookupNF("DR", &oRefDR)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRefDR.getRef().num) != arrUniqueResources.end()) { oRefDR.free(); oAnnot.free(); continue; } arrUniqueResources.push_back(oRefDR.getRef().num); oRefDR.free(); Object oDR; if (oAnnot.dictLookup("DR", &oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources)) { oDR.free(); oAnnot.free(); oAnnots.free(); return true; } oDR.free(); if (scanAPfonts(&oAnnot, arrCMap, arrUniqueResources)) { oAnnot.free(); oAnnots.free(); return true; } oAnnot.free(); } oAnnots.free(); } AcroForm* pAcroForms = pDoc->getCatalog()->getForm(); if (!pAcroForms) return false; Object oDR; Object* oAcroForm = pAcroForms->getAcroFormObj(); Object oRefDR; if (oAcroForm->dictLookupNF("DR", &oRefDR)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRefDR.getRef().num) != arrUniqueResources.end()) { oRefDR.free(); return false; } arrUniqueResources.push_back(oRefDR.getRef().num); oRefDR.free(); if (oAcroForm->dictLookup("DR", &oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources)) { oDR.free(); return true; } oDR.free(); for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i) { AcroFormField* pField = pAcroForms->getField(i); if (pField->getResources(&oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources)) { oDR.free(); return true; } oDR.free(); Object oWidgetRef, oWidget; pField->getFieldRef(&oWidgetRef); oWidgetRef.fetch(pDoc->getXRef(), &oWidget); oWidgetRef.free(); if (scanAPfonts(&oWidget, arrCMap, arrUniqueResources)) { oWidget.free(); return true; } oWidget.free(); } return false; } bool IsBaseFont(const std::wstring& wsName) { return wsName == L"Courier" || wsName == L"Courier-Bold" || wsName == L"Courier-BoldOblique" || wsName == L"Courier-Oblique" || wsName == L"Helvetica" || wsName == L"Helvetica-Bold" || wsName == L"Helvetica-BoldOblique" || wsName == L"Helvetica-Oblique" || wsName == L"Symbol" || wsName == L"Times-Bold" || wsName == L"Times-BoldItalic" || wsName == L"Times-Italic" || wsName == L"Times-Roman" || wsName == L"ZapfDingbats"; } std::map GetAllFonts(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList) { std::map mFonts; AcroForm* pAcroForms = pdfDoc->getCatalog()->getForm(); if (pAcroForms) { std::vector arrUniqueFontsRef; for (int nField = 0, nNum = pAcroForms->getNumFields(); nField < nNum; ++nField) { AcroFormField* pField = pAcroForms->getField(nField); if (!pField) continue; // Font and font size - from DA Ref fontID; double dFontSize = 0; pField->getFont(&fontID, &dFontSize); Object oFontRef; if (fontID.num < 0) { std::string sFontKey; if (!GetFontFromAP(pdfDoc, pField, &oFontRef, sFontKey)) { oFontRef.free(); continue; } } else oFontRef.initRef(fontID.num, fontID.gen); if (std::find(arrUniqueFontsRef.begin(), arrUniqueFontsRef.end(), oFontRef.getRefNum()) != arrUniqueFontsRef.end()) { oFontRef.free(); continue; } std::string sFontName; std::string sActualFontName; std::wstring wsFileName; bool bBold = false, bItalic = false; wsFileName = GetFontData(pdfDoc, pFontManager, pFontList, &oFontRef, sFontName, sActualFontName, bBold, bItalic); if (!sActualFontName.empty()) { oFontRef.free(); continue; } if (!sFontName.empty()) { std::wstring wsFontName = UTF8_TO_U(sFontName); if (mFonts.find(wsFontName) == mFonts.end()) { arrUniqueFontsRef.push_back(oFontRef.getRefNum()); mFonts[wsFontName] = wsFileName; } } oFontRef.free(); if (pField->getAcroFormFieldType() == acroFormFieldPushbutton && fontID.num >= 0) { std::string sFontKey; if (GetFontFromAP(pdfDoc, pField, &oFontRef, sFontKey) && std::find(arrUniqueFontsRef.begin(), arrUniqueFontsRef.end(), oFontRef.getRefNum()) == arrUniqueFontsRef.end()) { wsFileName = GetFontData(pdfDoc, pFontManager, pFontList, &oFontRef, sFontName, sActualFontName, bBold, bItalic); std::wstring wsFontName = UTF8_TO_U(sFontName); if (sActualFontName.empty() && mFonts.find(wsFontName) == mFonts.end()) { arrUniqueFontsRef.push_back(oFontRef.getRefNum()); mFonts[wsFontName] = wsFileName; } } } oFontRef.free(); } } for (int nPage = 0, nLastPage = pdfDoc->getNumPages(); nPage < nLastPage; ++nPage) { Page* pPage = pdfDoc->getCatalog()->getPage(nPage + 1); if (!pPage) continue; Object oAnnots; if (!pPage->getAnnots(&oAnnots)->isArray()) { oAnnots.free(); continue; } for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i) { Object oAnnot; if (!oAnnots.arrayGet(i, &oAnnot)->isDict()) { oAnnot.free(); continue; } Object oSubtype; if (!oAnnot.dictLookup("Subtype", &oSubtype)->isName("FreeText")) { oSubtype.free(); oAnnot.free(); continue; } oSubtype.free(); std::string sRC; Object oObj; if (!oAnnot.dictLookup("RC", &oObj)->isString()) { oObj.free(); if (oAnnot.dictLookup("Contents", &oObj)->isString() && oObj.getString()->getLength()) { std::string sDS; Object oObj2; if (oAnnot.dictLookup("DS", &oObj2)->isString()) { TextString* s = new TextString(oObj2.getString()); sDS = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength()); delete s; } oObj2.free(); sRC = GetRCFromDS(sDS, &oObj, {}); if (sRC.find("font-family:Helvetica") != std::string::npos) { const unsigned char* pData14 = NULL; unsigned int nSize14 = 0; std::wstring wsFontName = L"Helvetica"; NSFonts::IFontsMemoryStorage* pMemoryStorage = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage(); if (pMemoryStorage && !pMemoryStorage->Get(wsFontName) && GetBaseFont(wsFontName, pData14, nSize14)) pMemoryStorage->Add(wsFontName, (BYTE*)pData14, nSize14, false); mFonts[L"Helvetica"] = L"Helvetica"; } } } else { TextString* s = new TextString(oObj.getString()); sRC = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength()); delete s; } oObj.free(); oAnnot.free(); Object oAnnotRef; oAnnots.arrayGetNF(i, &oAnnotRef); std::vector arrRC = ReadRC(sRC); std::map mFreeText = GetFreeTextFont(pdfDoc, pFontManager, pFontList, &oAnnotRef, arrRC); for (std::map::iterator it = mFreeText.begin(); it != mFreeText.end(); ++it) { if (mFonts.find(it->first) != mFonts.end()) continue; mFonts[it->first] = it->second; } oAnnotRef.free(); for (int j = 0; j < arrRC.size(); ++j) RELEASEOBJECT(arrRC[j]); } oAnnots.free(); } return mFonts; } std::wstring GetFontData(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList, Object* oFontRef, std::string& sFontName, std::string& sActualFontName, bool& bBold, bool& bItalic) { bBold = false, bItalic = false; XRef* xref = pdfDoc->getXRef(); Object oFont; if (!xref->fetch(oFontRef->getRefNum(), oFontRef->getRefGen(), &oFont)->isDict()) { oFont.free(); return L""; } GfxFont* gfxFont = GfxFont::makeFont(xref, "F", oFontRef->getRef(), oFont.getDict()); oFont.free(); if (!gfxFont) return L""; Ref oEmbRef; std::wstring wsFontBaseName = NSStrings::GetStringFromUTF32(gfxFont->getName()); std::wstring wsFileName; if (gfxFont->getEmbeddedFontID(&oEmbRef) || IsBaseFont(wsFontBaseName)) { std::wstring wsFontName; GetFont(xref, pFontManager, pFontList, gfxFont, wsFileName, wsFontName, false); sFontName = U_TO_UTF8(wsFontName); CheckFontStylePDF(wsFontName, bBold, bItalic); if (!bBold) bBold = gfxFont->isBold(); if (!bItalic) bItalic = gfxFont->isItalic(); } else { double dStretch = 1.0; std::wstring wsFBN = wsFontBaseName; NSFonts::CFontInfo* pFontInfo = GetFontByParams(xref, pFontManager, gfxFont, wsFBN, dStretch); if (pFontInfo && !pFontInfo->m_wsFontPath.empty()) { EraseSubsetTag(wsFontBaseName); wsFileName = pFontInfo->m_wsFontPath; sFontName = U_TO_UTF8(wsFontBaseName); sActualFontName = U_TO_UTF8(pFontInfo->m_wsFontName); bBold = pFontInfo->m_bBold; bItalic = pFontInfo->m_bItalic; } } RELEASEOBJECT(gfxFont); return wsFileName; } bool GetFontFromAP(PDFDoc* pdfDoc, AcroFormField* pField, Object* oFontRef, std::string& sFontKey) { bool bFindResources = false; Object oAP, oN; XRef* xref = pdfDoc->getXRef(); if (pField->fieldLookup("AP", &oAP)->isDict() && oAP.dictLookup("N", &oN)->isStream()) { Parser* parser = new Parser(xref, new Lexer(xref, &oN), gFalse); bool bFindFont = false; Object oObj1, oObj2, oObj3; parser->getObj(&oObj1); while (!oObj1.isEOF()) { if (oObj1.isName()) { parser->getObj(&oObj2); if (oObj2.isEOF()) break; if (oObj2.isNum()) { parser->getObj(&oObj3); if (oObj3.isEOF()) break; if (oObj3.isCmd("Tf")) { bFindFont = true; break; } } } if (oObj2.isName()) { oObj1.free(); oObj2.copy(&oObj1); oObj2.free(); oObj3.free(); continue; } if (oObj3.isName()) { oObj1.free(); oObj3.copy(&oObj1); oObj3.free(); oObj2.free(); continue; } oObj1.free(); oObj2.free(); oObj3.free(); parser->getObj(&oObj1); } if (bFindFont && oObj1.isName()) { Object oR, oFonts; bFindResources = oN.streamGetDict()->lookup("Resources", &oR)->isDict() && oR.dictLookup("Font", &oFonts)->isDict() && oFonts.dictLookupNF(oObj1.getName(), oFontRef)->isRef(); sFontKey = oObj1.getName(); oR.free(); oFonts.free(); } oObj1.free(); oObj2.free(); oObj3.free(); RELEASEOBJECT(parser); } oAP.free(); oN.free(); return bFindResources; } bool FindFonts(Object* oStream, int nDepth, Object* oResFonts) { if (nDepth > 5) return false; Object oResources; if (!oStream->streamGetDict()->lookup("Resources", &oResources)->isDict()) { oResources.free(); return false; } if (oResources.dictLookup("Font", oResFonts)->isDict()) { oResources.free(); return true; } Object oXObject; if (oResources.dictLookup("XObject", &oXObject)->isDict()) { for (int i = 0, nLength = oXObject.dictGetLength(); i < nLength; ++i) { Object oXObj; if (!oXObject.dictGetVal(i, &oXObj)->isStream()) { oXObj.free(); continue; } if (FindFonts(&oXObj, nDepth + 1, oResFonts)) { oXObj.free(); oXObject.free(); oResources.free(); return true; } oXObj.free(); } } oXObject.free(); oResources.free(); return false; } std::vector GetAnnotFontInfos(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef) { std::vector result; Object oAnnot, oObj; XRef* pXref = pdfDoc->getXRef(); oAnnotRef->fetch(pXref, &oAnnot); Object oAP, oN; if (!oAnnot.dictLookup("AP", &oAP)->isDict() || !oAP.dictLookup("N", &oN)->isStream()) { oAP.free(); oN.free(); oAnnot.free(); return result; } oAP.free(); Object oFonts; if (!FindFonts(&oN, 0, &oFonts)) { oN.free(); oFonts.free(); oAnnot.free(); return result; } oN.free(); for (int i = 0, nFonts = oFonts.dictGetLength(); i < nFonts; ++i) { Object oFontRef; if (!oFonts.dictGetValNF(i, &oFontRef)->isRef()) { oFontRef.free(); continue; } CAnnotFontInfo info; std::string sFontName, sActualFontName; info.wsFontPath = GetFontData(pdfDoc, pFontManager, pFontList, &oFontRef, sFontName, sActualFontName, info.bBold, info.bItalic); oFontRef.free(); if (info.wsFontPath.empty() || IsBaseFont(info.wsFontPath)) continue; info.wsFontName = UTF8_TO_U(sFontName); if (!sActualFontName.empty()) info.wsActualFontName = UTF8_TO_U(sActualFontName); result.push_back(info); } oFonts.free(); oAnnot.free(); return result; } int GetAnnotFontNamePenalty(const std::wstring& sCandNorm, const std::wstring& sReqNorm) { if (sReqNorm.empty()) return 0; if (sCandNorm.empty()) return 10000; if (sCandNorm == sReqNorm) return 0; bool bCandInReq = sReqNorm.find(sCandNorm) != std::wstring::npos; bool bReqInCand = sCandNorm.find(sReqNorm) != std::wstring::npos; if (bCandInReq || bReqInCand) return 500; return 10000; } const CAnnotFontInfo* FindMatchInAnnotFonts(const std::vector& annotFonts, const std::wstring& wsRCName, bool bBold, bool bItalic) { std::wstring wsReqNorm = Normalize(wsRCName); const CAnnotFontInfo* pBestMatch = nullptr; int nBestPenalty = INT_MAX; for (const auto& fi : annotFonts) { int nNamePenalty = GetAnnotFontNamePenalty(Normalize(fi.wsFontName), wsReqNorm); if (!fi.wsActualFontName.empty()) { int nActualPenalty = GetAnnotFontNamePenalty(Normalize(fi.wsActualFontName), wsReqNorm); if (nActualPenalty < nNamePenalty) nNamePenalty = nActualPenalty; } if (nNamePenalty >= 5000) continue; int nBoldPenalty = (fi.bBold != bBold) ? 1 : 0; int nItalicPenalty = (fi.bItalic != bItalic) ? 4 : 0; int nTotalPenalty = nNamePenalty + nBoldPenalty + nItalicPenalty; if (nTotalPenalty < nBestPenalty) { nBestPenalty = nTotalPenalty; pBestMatch = &fi; } if (nTotalPenalty == 0) break; } return pBestMatch; } std::map GetFreeTextFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef, std::vector& arrRC) { std::map mRes; std::vector annotFonts = GetAnnotFontInfos(pdfDoc, pFontManager, pFontList, oAnnotRef); NSFonts::IFontsMemoryStorage* pMemoryStorage = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage(); CFontList* pAppFontList = (CFontList*)pFontManager->GetApplication()->GetList(); for (int i = 0; i < (int)arrRC.size(); ++i) { if (arrRC[i]->bFind) continue; std::string sFontName = arrRC[i]->sFontFamily; std::wstring wsFontName = UTF8_TO_U(sFontName); bool bBold = (bool)((arrRC[i]->unFontFlags >> 0) & 1); bool bItalic = (bool)((arrRC[i]->unFontFlags >> 1) & 1); if (IsBaseFont(wsFontName)) { if (sFontName == "Times-Roman") { if (bBold && bItalic) sFontName = "Times-BoldItalic"; else if (bBold) sFontName = "Times-Bold"; else if (bItalic) sFontName = "Times-Italic"; } else if (sFontName == "Courier" || sFontName == "Helvetica") { if (bBold && bItalic) sFontName += "-BoldOblique"; else if (bBold) sFontName += "-Bold"; else if (bItalic) sFontName += "-Oblique"; } wsFontName = UTF8_TO_U(sFontName); if (pMemoryStorage && !pMemoryStorage->Get(wsFontName)) { const unsigned char* pData14 = NULL; unsigned int nSize14 = 0; if (GetBaseFont(wsFontName, pData14, nSize14)) pMemoryStorage->Add(wsFontName, (BYTE*)pData14, nSize14, false); } std::string sFontNameBefore = arrRC[i]->sFontFamily; arrRC[i]->sFontFamily = sFontName; arrRC[i]->bFind = true; mRes[wsFontName] = wsFontName; for (int j = i; j < (int)arrRC.size(); ++j) { if (arrRC[j]->sFontFamily == sFontNameBefore && bBold == (bool)((arrRC[j]->unFontFlags >> 0) & 1) && bItalic == (bool)((arrRC[j]->unFontFlags >> 1) & 1)) { arrRC[j]->sFontFamily = sFontName; arrRC[j]->bFind = true; } } } else { const CAnnotFontInfo* pMatch = FindMatchInAnnotFonts(annotFonts, wsFontName, bBold, bItalic); std::wstring wsResolvedName; std::wstring wsResolvedPath; bool bFoundInAnnot = false; if (pMatch) { wsResolvedPath = pMatch->wsFontPath; wsResolvedName = pMatch->wsActualFontName.empty() ? pMatch->wsFontName : pMatch->wsActualFontName; EraseSubsetTag(wsResolvedName); bFoundInAnnot = true; } else { NSFonts::CFontSelectFormat oFontSelect; if (bBold) oFontSelect.bBold = new INT(1); if (bItalic) oFontSelect.bItalic = new INT(1); oFontSelect.wsName = new std::wstring(wsFontName); NSFonts::CFontInfo* pFontInfo = pAppFontList->GetByParams(oFontSelect); if (pFontInfo && !pFontInfo->m_wsFontPath.empty()) { wsResolvedPath = pFontInfo->m_wsFontPath; wsResolvedName = pFontInfo->m_wsFontName; EraseSubsetTag(wsResolvedName); } } if (!wsResolvedName.empty()) { if (bFoundInAnnot) { arrRC[i]->sFontFamily = U_TO_UTF8(wsResolvedName); mRes[wsResolvedName] = wsResolvedPath; } else { arrRC[i]->unFontFlags |= (1 << 6); arrRC[i]->sActualFont = U_TO_UTF8(wsResolvedName); } arrRC[i]->bFind = true; std::string sFontNameNew = bFoundInAnnot ? arrRC[i]->sFontFamily : arrRC[i]->sActualFont; for (int j = i; j < (int)arrRC.size(); ++j) { if (arrRC[j]->sFontFamily == sFontName && bBold == (bool)((arrRC[j]->unFontFlags >> 0) & 1) && bItalic == (bool)((arrRC[j]->unFontFlags >> 1) & 1)) { if (bFoundInAnnot) arrRC[j]->sFontFamily = sFontNameNew; else { arrRC[j]->unFontFlags |= (1 << 6); arrRC[j]->sActualFont = sFontNameNew; } arrRC[j]->bFind = true; } } } } } return mRes; } int CollectFontWidths(Dict* pFontDict, std::map& mGIDToWidth) { int nDefaultWidth = 1000; // Try to get widths from Widths dictionary Object oWidths; if (pFontDict->lookup("Widths", &oWidths)->isArray()) { Object oFirstChar; int nFirstChar = 0; if (pFontDict->lookup("FirstChar", &oFirstChar)->isInt()) nFirstChar = oFirstChar.getInt(); oFirstChar.free(); for (int i = 0; i < oWidths.arrayGetLength(); ++i) { Object oWidth; if (oWidths.arrayGet(i, &oWidth)->isNum()) { unsigned int nGID = nFirstChar + i; unsigned int nWidth = (unsigned int)oWidth.getNum(); mGIDToWidth[nGID] = nWidth; } oWidth.free(); } } oWidths.free(); // For CID fonts, process DW and W Object oDescendantFonts; if (pFontDict->lookup("DescendantFonts", &oDescendantFonts)->isArray() && oDescendantFonts.arrayGetLength() > 0) { Object oCIDFont; if (oDescendantFonts.arrayGet(0, &oCIDFont)->isDict()) { // Get DW (default width) Object oDW; if (oCIDFont.dictLookup("DW", &oDW)->isInt()) nDefaultWidth = oDW.getInt(); oDW.free(); // Get W (widths array) Object oW; if (oCIDFont.dictLookup("W", &oW)->isArray()) { int i = 0; while (i < oW.arrayGetLength()) { Object oStart, oSecond, oThird; if (!oW.arrayGet(i, &oStart)->isInt()) { oStart.free(); break; } if (i + 1 >= oW.arrayGetLength()) { oStart.free(); break; } oW.arrayGet(i + 1, &oSecond); if (oSecond.isArray()) { // Format: c [w1 w2 ... wn] int nStartCID = oStart.getInt(); for (int j = 0; j < oSecond.arrayGetLength(); ++j) { Object oWidth; if (oSecond.arrayGet(j, &oWidth)->isNum()) mGIDToWidth[nStartCID + j] = (unsigned int)oWidth.getNum(); oWidth.free(); } i += 2; } else if (oSecond.isInt()) { // Format: cfirst clast w if (i + 2 >= oW.arrayGetLength()) { oStart.free(); oSecond.free(); break; } oW.arrayGet(i + 2, &oThird); if (oThird.isNum()) { int nStartCID = oStart.getInt(); int nEndCID = oSecond.getInt(); unsigned int nWidth = (unsigned int)oThird.getNum(); for (int cid = nStartCID; cid <= nEndCID; ++cid) mGIDToWidth[cid] = nWidth; } oThird.free(); i += 3; } else { oStart.free(); oSecond.free(); break; } oStart.free(); oSecond.free(); } } oW.free(); } oCIDFont.free(); } oDescendantFonts.free(); return nDefaultWidth; } double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic) { EraseSubsetTag(sName); CheckFontNameStyle(sName, L"condensedbold"); CheckFontNameStyle(sName, L"semibold"); CheckFontNameStyle(sName, L"regular"); double dStretch = 1.0; if (CheckFontNameStyle(sName, L"ultraexpanded")) dStretch = 2.0; if (CheckFontNameStyle(sName, L"extraexpanded")) dStretch = 1.5; if (CheckFontNameStyle(sName, L"semiexpanded")) dStretch = 1.125; if (CheckFontNameStyle(sName, L"expanded")) dStretch = 1.25; if (CheckFontNameStyle(sName, L"ultracondensed")) dStretch = 0.5; if (CheckFontNameStyle(sName, L"extracondensed")) dStretch = 0.625; if (CheckFontNameStyle(sName, L"semicondensed")) dStretch = 0.875; if (CheckFontNameStyle(sName, L"condensedlight")) dStretch = 0.75; if (CheckFontNameStyle(sName, L"condensed")) dStretch = 0.75; //CheckFontNameStyle(sName, L"light"); if (CheckFontNameStyle(sName, L"bold_italic")) { bBold = true; bItalic = true; } if (CheckFontNameStyle(sName, L"bold_oblique")) { bBold = true; bItalic = true; } if (CheckFontNameStyle(sName, L"boldmt")) bBold = true; if (CheckFontNameStyle(sName, L"bold")) bBold = true; if (CheckFontNameStyle(sName, L"italicmt")) bItalic = true; if (CheckFontNameStyle(sName, L"italic")) bItalic = true; if (CheckFontNameStyle(sName, L"oblique")) bItalic = true; //if (CheckFontNameStyle(sName, L"bolditalicmt")) { bBold = true; bItalic = true; } //if (CheckFontNameStyle(sName, L"bolditalic")) { bBold = true; bItalic = true; } //if (CheckFontNameStyle(sName, L"boldoblique")) { bBold = true; bItalic = true; } return dStretch; } double CheckFontNamePDF(std::wstring& sName, NSFonts::CFontSelectFormat* format) { bool bBold = false; bool bItalic = false; double dStretch = CheckFontStylePDF(sName, bBold, bItalic); if (format) { if (bBold) format->bBold = new INT(1); if (bItalic) format->bItalic = new INT(1); } return dStretch; } bool EraseSubsetTag(std::wstring& sFontName) { bool bIsRemove = false; if (sFontName.length() > 7 && sFontName.at(6) == '+') { bIsRemove = true; for (int nIndex = 0; nIndex < 6; nIndex++) { wchar_t nChar = sFontName.at(nIndex); if (nChar < 'A' || nChar > 'Z') { bIsRemove = false; break; } } if (bIsRemove) sFontName.erase(0, 7); } return bIsRemove; } USHORT StretchToWidthClass(double fStretch) { if (fStretch <= 0.50) return 1; // Ultra-condensed if (fStretch <= 0.625) return 2; // Extra-condensed if (fStretch <= 0.75) return 3; // Condensed if (fStretch <= 0.875) return 4; // Semi-condensed if (fStretch <= 1.0) return 5; // Normal if (fStretch <= 1.125) return 6; // Semi-expanded if (fStretch <= 1.25) return 7; // Expanded if (fStretch <= 1.50) return 8; // Extra-expanded return 9; // Ultra-expanded } NSFonts::CFontInfo* GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName, double& dStretch) { NSFonts::CFontInfo* pFontInfo = NULL; if (!pFontManager) return pFontInfo; Ref* pRef = pFont->getID(); Object oRefObject, oFontObject; oRefObject.initRef(pRef->num, pRef->gen); oRefObject.fetch(pXref, &oFontObject); oRefObject.free(); NSFonts::CFontSelectFormat oFontSelect; dStretch = CheckFontNamePDF(wsFontBaseName, &oFontSelect); if (std::abs(dStretch - 1.0f) > 1e-5f) oFontSelect.usWidth = new USHORT(StretchToWidthClass(dStretch)); if (oFontObject.isDict()) { Dict* pFontDict = oFontObject.getDict(); Object oFontDescriptor, oDescendantFonts; pFontDict->lookup("FontDescriptor", &oFontDescriptor); if (!oFontDescriptor.isDict() && pFontDict->lookup("DescendantFonts", &oDescendantFonts)->isArray()) { oFontDescriptor.free(); oFontObject.free(); if (oDescendantFonts.arrayGet(0, &oFontObject)->isDict()) oFontObject.dictLookup("FontDescriptor", &oFontDescriptor); } if (oFontDescriptor.isDict()) { Object oDictItem; oFontDescriptor.dictLookup("FontName", &oDictItem); if (oDictItem.isName()) oFontSelect.wsName = AStringToPWString(oDictItem.getName()); else oFontSelect.wsName = new std::wstring(wsFontBaseName); oDictItem.free(); oFontDescriptor.dictLookup("FontFamily", &oDictItem); if (oDictItem.isString()) { TextString* s = new TextString(oDictItem.getString()); oFontSelect.wsAltName = new std::wstring(NSStringExt::CConverter::GetUnicodeFromUTF32(s->getUnicode(), s->getLength())); delete s; } oDictItem.free(); oFontDescriptor.dictLookup("FontStretch", &oDictItem); oDictItem.free(); oFontDescriptor.dictLookup("FontWeight", &oDictItem); oDictItem.free(); oFontDescriptor.dictLookup("FontBBox", &oDictItem); oDictItem.free(); oFontDescriptor.dictLookup("Flags", &oDictItem); if (oDictItem.isInt() && 0 != oDictItem.getInt()) { int nFlags = oDictItem.getInt(); if (nFlags & 1) // monospaced oFontSelect.bFixedWidth = new INT(1); } oDictItem.free(); oFontDescriptor.dictLookup("ItalicAngle", &oDictItem); if (oDictItem.isInt() && 0 != oDictItem.getInt()) { if (oFontSelect.bItalic) RELEASEOBJECT(oFontSelect.bItalic); oFontSelect.bItalic = new INT(1); } oDictItem.free(); oFontDescriptor.dictLookup("Ascent", &oDictItem); if (oDictItem.isInt()) oFontSelect.shAscent = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("Leading", &oDictItem); if (oDictItem.isInt()) oFontSelect.shLineGap = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("CapHeight", &oDictItem); if (oDictItem.isInt()) oFontSelect.shCapHeight = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("XHeight", &oDictItem); if (oDictItem.isInt()) oFontSelect.shXHeight = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("StemV", &oDictItem); if (oDictItem.isNum() && !oFontSelect.usWidth) { double dStemV = oDictItem.getNum(); if (dStemV > 50.5) oFontSelect.usWeight = new USHORT(sqrt(oDictItem.getNum() - 50.5) * 65); } oDictItem.free(); oFontDescriptor.dictLookup("StemH", &oDictItem); oDictItem.free(); oFontDescriptor.dictLookup("Descent", &oDictItem); if (oDictItem.isInt()) oFontSelect.shDescent = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("AvgWidth", &oDictItem); if (oDictItem.isInt()) oFontSelect.shAvgCharWidth = new SHORT(oDictItem.getInt()); oDictItem.free(); oFontDescriptor.dictLookup("MaxWidth", &oDictItem); oDictItem.free(); oFontDescriptor.dictLookup("MissingWidth", &oDictItem); oDictItem.free(); } else oFontSelect.wsName = new std::wstring(wsFontBaseName); oFontDescriptor.free(); oDescendantFonts.free(); } else oFontSelect.wsName = new std::wstring(wsFontBaseName); oFontObject.free(); pFontInfo = pFontManager->GetFontInfoByParams(oFontSelect); return pFontInfo; } void GetFont(XRef* pXref, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, GfxFont* pFont, std::wstring& wsFileName, std::wstring& wsFontName, bool bNotFullName) { wsFileName = L""; wsFontName = L""; TFontEntry* pEntry = NULL; // MEMERR string dealocation pEntry if (!pFontList->Find2((*pFont->getID()), &pEntry)) { GfxFontType eFontType = pFont->getType(); if (fontType3 == eFontType) // FontType3 is handled by a separate command { pEntry->bAvailable = true; return; } std::wstring wsTempFileName = L""; Ref oEmbRef; bool bFontSubstitution = false, bFontBase14 = false; std::wstring wsFontBaseName = NSStrings::GetStringFromUTF32(pFont->getName()); if (wsFontBaseName.empty()) wsFontBaseName = L"Helvetica"; const BYTE* pData14 = NULL; unsigned int nSize14 = 0; double dStretch = 1.0; #ifdef FONTS_USE_ONLY_MEMORY_STREAMS CMemoryFontStream oMemoryFontStream; #endif // 1. If font is embedded, dump it to a temp file. // 2. If font is outside pdf but pdf has a reference to it, use that reference. // 3. Otherwise, select a font. if (pFont->getEmbeddedFontID(&oEmbRef)) { std::wstring wsExt; switch (pFont->getType()) { case fontType1: wsExt = L".pfb_t1"; break; case fontType1C: wsExt = L".pfb_t1c"; break; case fontType1COT: wsExt = L".pfb_t1cot"; break; case fontTrueType: wsExt = L".ttf"; break; case fontTrueTypeOT: wsExt = L".otf"; break; case fontCIDType0: wsExt = L".cid_0"; break; case fontCIDType0C: wsExt = L".cid_0c"; break; case fontCIDType0COT: wsExt = L".cid_0cot"; break; case fontCIDType2: wsExt = L".cid_2"; break; case fontCIDType2OT: wsExt = L".cid_2ot"; break; } #ifdef FONTS_USE_ONLY_MEMORY_STREAMS if (NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()) { wsTempFileName = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->GenerateId(); } #else FILE* pTempFile = NULL; if (!NSFile::CFileBinary::OpenTempFile(&wsTempFileName, &pTempFile, L"wb", (wchar_t*)wsExt.c_str(), (wchar_t*)((GlobalParamsAdaptor*)globalParams)->GetTempFolder().c_str(), NULL)) { if (L"" != wsTempFileName) NSFile::CFileBinary::Remove(wsTempFileName); pEntry->bAvailable = true; return; } wsTempFileName = UTF8_TO_U(NSSystemPath::NormalizePath(U_TO_UTF8(wsTempFileName))); #endif Object oReferenceObject, oStreamObject; oReferenceObject.initRef(oEmbRef.num, oEmbRef.gen); oReferenceObject.fetch(pXref, &oStreamObject); oReferenceObject.free(); if (!oStreamObject.isStream()) { // Embedded font is incorrectly written oStreamObject.free(); #ifndef FONTS_USE_ONLY_MEMORY_STREAMS fclose(pTempFile); if (L"" != wsTempFileName) NSFile::CFileBinary::Remove(wsTempFileName); #endif pEntry->bAvailable = true; return; } oStreamObject.streamReset(); #ifdef FONTS_USE_ONLY_MEMORY_STREAMS oMemoryFontStream.load(oStreamObject); NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->Add(wsTempFileName, oMemoryFontStream.m_pData, (LONG)oMemoryFontStream.m_nSize, true); #else int nChar; while ((nChar = oStreamObject.streamGetChar()) != EOF) { fputc(nChar, pTempFile); } fclose(pTempFile); #endif oStreamObject.streamClose(); oStreamObject.free(); wsFileName = wsTempFileName; #ifdef FONTS_USE_AFM_SETTINGS // For Type1 fonts, need to write Afm file with metrics if (fontType1 == pFont->getType() || fontType1C == pFont->getType() || fontType1COT == pFont->getType()) { std::wstring wsSplitFileName, wsSplitFileExt; SpitPathExt(wsFileName, &wsSplitFileName, &wsSplitFileExt); std::wstring wsAfmPath = wsSplitFileName + L".afm"; FILE* pFile = NSFile::CFileBinary::OpenFileNative(wsAfmPath, L"wb"); if (pFile) { Ref* pRef = pFont->getID(); Object oRefObject, oFontObject; oRefObject.initRef(pRef->num, pRef->gen); oRefObject.fetch(pXref, &oFontObject); oRefObject.free(); if (oFontObject.isDict()) { std::string sFontName, sFontFamily; int nFontWeight = 0, nItalicAngle = 0, nAscent = 0, nDescent = 0; int nCapHeight = 0, nXHeight = 0, nStemV = 0, nStemH = 0, nMissingWidth = 0; int arrBBox[4] = { 0, 0, 0, 0 }; Object oFontDescriptor; if (oFontObject.dictLookup("FontDescriptor", &oFontDescriptor)->isDict()) { Object oDictItem; oFontDescriptor.dictLookup("FontName", &oDictItem); if (oDictItem.isName()) sFontName = oDictItem.getName(); oDictItem.free(); oFontDescriptor.dictLookup("FontFamily", &oDictItem); if (oDictItem.isName()) sFontFamily = oDictItem.getName(); oDictItem.free(); oFontDescriptor.dictLookup("FontWeight", &oDictItem); if (oDictItem.isInt()) nFontWeight = oDictItem.getInt(); oDictItem.free(); if (oFontDescriptor.dictLookup("FontBBox", &oDictItem)->isArray() && oDictItem.arrayGetLength() == 4) { for (int nIndex = 0; nIndex < 4; nIndex++) { Object oArrayItem; if (oDictItem.arrayGet(nIndex, &oArrayItem)->isInt()) arrBBox[nIndex] = oArrayItem.getInt(); oArrayItem.free(); } } oDictItem.free(); oFontDescriptor.dictLookup("ItalicAngle", &oDictItem); if (oDictItem.isInt()) nItalicAngle = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("Ascent", &oDictItem); if (oDictItem.isInt()) nAscent = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("CapHeight", &oDictItem); if (oDictItem.isInt()) nCapHeight = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("XHeight", &oDictItem); if (oDictItem.isInt()) nXHeight = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("StemV", &oDictItem); if (oDictItem.isInt()) nStemV = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("StemH", &oDictItem); if (oDictItem.isInt()) nStemH = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("Descent", &oDictItem); if (oDictItem.isInt()) nDescent = oDictItem.getInt(); oDictItem.free(); oFontDescriptor.dictLookup("MissingWidth", &oDictItem); if (oDictItem.isInt()) nMissingWidth = oDictItem.getInt(); oDictItem.free(); } oFontDescriptor.free(); fprintf(pFile, "StartFontMetrics 3.0\n"); if (!sFontName.empty()) fprintf(pFile, "FontName %s\n", sFontName.c_str()); if (!sFontFamily.empty()) fprintf(pFile, "FamilyName %s\n", sFontFamily.c_str()); if (nFontWeight >= 550) fprintf(pFile, "Weight Bold\n"); fprintf(pFile, "ItalicAngle %d\n", nItalicAngle); fprintf(pFile, "FontBBox %d %d %d %d\n", arrBBox[0], arrBBox[1], arrBBox[2], arrBBox[3]); fprintf(pFile, "CapHeight %d\n", nCapHeight); fprintf(pFile, "XHeight %d\n", nXHeight); fprintf(pFile, "Ascender %d\n", nAscent); fprintf(pFile, "Descender %d\n", nDescent); fprintf(pFile, "StdHW %d\n", nStemH); fprintf(pFile, "StdHV %d\n", nStemV); int nFirstChar = 0; Object oDictItem; if (oFontObject.dictLookup("FirstChar", &oDictItem)->isInt()) nFirstChar = oDictItem.getInt(); oDictItem.free(); Gfx8BitFont* pT1Font = (Gfx8BitFont*)pFont; if (oFontObject.dictLookup("Widths", &oDictItem)->isArray()) { int nWidthsCount = oDictItem.arrayGetLength(); fprintf(pFile, "StartCharMetrics %d\n", nWidthsCount); for (int nIndex = 0; nIndex < nWidthsCount; nIndex++) { int nWidth = nMissingWidth; Object oArrayItem; if (oDictItem.arrayGet(nIndex, &oArrayItem)->isInt()) nWidth = oArrayItem.getInt(); oArrayItem.free(); char** ppEncoding = pT1Font->getEncoding(); if (ppEncoding && ppEncoding[nIndex]) fprintf(pFile, "C %d ; WX %d ; N %s ;\n", nIndex + nFirstChar, nWidth, ppEncoding[nIndex]); else fprintf(pFile, "C %d ; WX %d ;\n", nIndex + nFirstChar, nWidth); } fprintf(pFile, "EndCharMetrics\n"); } oDictItem.free(); } oFontObject.free(); } fclose(pFile); } #endif // Load the font file itself to determine its exact type if (!pFontManager->LoadFontFromFile(wsFileName, 0, 10, 72, 72)) { pEntry->bAvailable = true; return; } std::wstring wsFontType = pFontManager->GetFontType(); if (L"TrueType" == wsFontType) { if (eFontType != fontType1COT && eFontType != fontTrueType && eFontType != fontTrueTypeOT && eFontType != fontCIDType0COT && eFontType != fontCIDType2 && eFontType != fontCIDType2OT) { if (eFontType == fontType1 || eFontType == fontType1C) eFontType = fontType1COT; else if (eFontType == fontCIDType0 || eFontType == fontCIDType0C) eFontType = fontCIDType0COT; } } else if (L"Type 1" == wsFontType) { if (eFontType != fontType1 && eFontType != fontType1C) { eFontType = fontType1; } } else if (L"CID Type 1" == wsFontType) { if (eFontType != fontCIDType0 && eFontType != fontCIDType0C && eFontType != fontCIDType2OT && eFontType != fontCIDType0COT) { eFontType = fontCIDType0; } } else if (L"CFF" == wsFontType) { if (eFontType != fontType1C && eFontType != fontType1COT && eFontType != fontTrueTypeOT && eFontType != fontCIDType0C && eFontType != fontCIDType0COT && eFontType != fontCIDType2OT && eFontType != fontCIDType2) { if (eFontType == fontType1 || eFontType == fontTrueType) eFontType = fontType1C; else if (eFontType == fontCIDType0) eFontType = fontCIDType0C; } } } #ifndef FONTS_USE_ONLY_MEMORY_STREAMS else if (PdfReader::GetBaseFont(wsFontBaseName, pData14, nSize14)) { FILE* pFile = NULL; if (!NSFile::CFileBinary::OpenTempFile(&wsTempFileName, &pFile, L"wb", L".base", (wchar_t*)((GlobalParamsAdaptor*)globalParams)->GetTempFolder().c_str(), NULL)) { if (!wsTempFileName.empty()) NSFile::CFileBinary::Remove(wsTempFileName); pEntry->bAvailable = true; return; } fclose(pFile); NSFile::CFileBinary oFile; oFile.CreateFileW(wsTempFileName); oFile.WriteFile((BYTE*)pData14, nSize14); oFile.CloseFile(); wsFileName = wsTempFileName; bFontBase14 = true; eFontType = fontTrueType; } #else else if ([&oMemoryFontStream, wsFontBaseName]() { const BYTE* pData14 = NULL; unsigned int nSize14 = 0; if (PdfReader::GetBaseFont(wsFontBaseName, pData14, nSize14)) { oMemoryFontStream.fromBuffer((BYTE*)pData14, nSize14); return true; } return false; }()) { wsFileName = wsFontBaseName; bFontBase14 = true; NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage()->Add(wsFileName, oMemoryFontStream.m_pData, (LONG)oMemoryFontStream.m_nSize, true); } #endif else if (!pFont->locateFont(pXref, false) || (wsFileName = NSStrings::GetStringFromUTF32(pFont->locateFont(pXref, false)->path)).length() == 0) { NSFonts::CFontInfo* pFontInfo = GetFontByParams(pXref, pFontManager, pFont, wsFontBaseName, dStretch); if (pFontInfo && L"" != pFontInfo->m_wsFontPath) { wsFileName = pFontInfo->m_wsFontPath; eFontType = pFont->isCIDFont() ? fontCIDType2 : fontTrueType; #ifdef FONTS_USE_ONLY_MEMORY_STREAMS if (NSWasm::IsJSEnv()) wsFileName = pFontInfo->m_wsFontName; if (!wsFileName.empty()) { wsFileName = NSWasm::LoadFont(wsFileName, pFontInfo->m_bBold, pFontInfo->m_bItalic); if (wsFileName.empty()) { pFontList->Remove(*pFont->getID()); return; } } oMemoryFontStream.fromStream(wsFileName); #endif bFontSubstitution = true; } else // As a last resort, simply don't write anything with this font { pEntry->bAvailable = true; return; } } // Here we load encodings int* pCodeToGID = NULL, *pCodeToUnicode = NULL; int nLen = 0; FoFiTrueType* pTTFontFile = NULL; #ifdef FONTS_USE_ONLY_MEMORY_STREAMS FoFiIdentifierType fofiType = FoFiIdentifier::identifyStream(&readFromMemoryStream, &oMemoryFontStream); oMemoryFontStream.toStart(); #else FoFiIdentifierType fofiType = FoFiIdentifier::identifyFile((char*)U_TO_UTF8(wsFileName).c_str()); #endif switch (eFontType) { case fontType1: case fontType1C: case fontType1COT: { Gfx8BitFont* pFont8bit = NULL; if (fofiType == fofiIdTrueType) { #ifdef FONTS_USE_ONLY_MEMORY_STREAMS pTTFontFile = FoFiTrueType::make((char*)oMemoryFontStream.m_pData, oMemoryFontStream.m_nSize, 0); #else pTTFontFile = FoFiTrueType::load((char*)U_TO_UTF8(wsFileName).c_str(), 0); #endif if (pTTFontFile) { pCodeToGID = ((Gfx8BitFont*)pFont)->getCodeToGIDMap(pTTFontFile); nLen = 256; delete pTTFontFile; pTTFontFile = NULL; } else { pCodeToGID = NULL; nLen = 0; } } else if (L"" != wsFileName && (pFont8bit = dynamic_cast(pFont))) { char** ppEncoding = pFont8bit->getEncoding(); if (!ppEncoding) break; if (!pFontManager) break; pFontManager->LoadFontFromFile(wsFileName, 0, 1, 72, 72); pCodeToGID = (int*)MemUtilsMallocArray(256, sizeof(int)); if (!pCodeToGID) break; nLen = 256; for (int nIndex = 0; nIndex < 256; ++nIndex) { pCodeToGID[nIndex] = 0; char* sName = NULL; if ((sName = ppEncoding[nIndex])) { unsigned short ushGID = pFontManager->GetNameIndex(AStringToWString(sName)); pCodeToGID[nIndex] = ushGID; } } } break; } case fontTrueType: case fontTrueTypeOT: { if (fofiType == fofiIdType1PFB) { Gfx8BitFont* pFont8bit = dynamic_cast(pFont); if (L"" != wsFileName && pFont8bit && pFont8bit->getHasEncoding()) { char** ppEncoding = pFont8bit->getEncoding(); if (!ppEncoding) break; if (!pFontManager) break; pFontManager->LoadFontFromFile(wsFileName, 0, 1, 72, 72); pCodeToGID = (int*)MemUtilsMallocArray(256, sizeof(int)); if (!pCodeToGID) break; nLen = 256; for (int nIndex = 0; nIndex < 256; ++nIndex) { pCodeToGID[nIndex] = 0; char* sName = NULL; if ((sName = ppEncoding[nIndex])) { unsigned short ushGID = pFontManager->GetNameIndex(AStringToWString(sName)); pCodeToGID[nIndex] = ushGID; } } } break; } #ifdef FONTS_USE_ONLY_MEMORY_STREAMS pTTFontFile = FoFiTrueType::make((char*)oMemoryFontStream.m_pData, oMemoryFontStream.m_nSize, 0); #else pTTFontFile = FoFiTrueType::load((char*)U_TO_UTF8(wsFileName).c_str(), 0); #endif if (pTTFontFile) { pCodeToGID = ((Gfx8BitFont*)pFont)->getCodeToGIDMap(pTTFontFile); nLen = 256; delete pTTFontFile; pTTFontFile = NULL; } else { pCodeToGID = NULL; nLen = 0; if (pFontManager->LoadFontFromFile(wsFileName, 0, 10, 72, 72)) { nLen = 256; pCodeToGID = (int*)MemUtilsMallocArray(nLen, sizeof(int)); for (int nCode = 0; nCode < nLen; ++nCode) { pCodeToGID[nCode] = pFontManager->GetGIDByUnicode(nCode); } } } break; } case fontCIDType0: case fontCIDType0C: { GfxCIDFont* pFontCID = dynamic_cast(pFont); if (!bFontSubstitution && pFontCID && pFontCID->getCIDToGID()) { nLen = pFontCID->getCIDToGIDLen(); if (!nLen) break; pCodeToGID = (int*)MemUtilsMallocArray(nLen, sizeof(int)); if (!pCodeToGID) { nLen = 0; break; } memcpy(pCodeToGID, ((GfxCIDFont*)pFont)->getCIDToGID(), nLen * sizeof(int)); break; } /* FoFiType1C* pT1CFontFile = NULL; #ifdef FONTS_USE_ONLY_MEMORY_STREAMS pT1CFontFile = FoFiType1C::make((char*)oMemoryFontStream.m_pData, oMemoryFontStream.m_nSize); #else pT1CFontFile = FoFiType1C::load((char*)U_TO_UTF8(wsFileName).c_str()); #endif if (pT1CFontFile) { pCodeToGID = pT1CFontFile->getCIDToGIDMap(&nLen); delete pT1CFontFile; pT1CFontFile = NULL; } else { pCodeToGID = NULL; nLen = 0; } */ pCodeToGID = NULL; nLen = 0; break; } case fontCIDType0COT: { GfxCIDFont* pFontCID = dynamic_cast(pFont); if (!bFontSubstitution && pFontCID && pFontCID->getCIDToGID()) { nLen = pFontCID->getCIDToGIDLen(); if (!nLen) break; pCodeToGID = (int*)MemUtilsMallocArray(nLen, sizeof(int)); if (!pCodeToGID) { nLen = 0; break; } memcpy(pCodeToGID, ((GfxCIDFont*)pFont)->getCIDToGID(), nLen * sizeof(int)); break; } #ifdef FONTS_USE_ONLY_MEMORY_STREAMS pTTFontFile = FoFiTrueType::make((char*)oMemoryFontStream.m_pData, oMemoryFontStream.m_nSize, 0); #else pTTFontFile = FoFiTrueType::load((char*)U_TO_UTF8(wsFileName).c_str(), 0); #endif if (pTTFontFile) { if (pTTFontFile->isOpenTypeCFF()) { pCodeToGID = pTTFontFile->getCIDToGIDMap(&nLen); } else { pCodeToGID = NULL; nLen = 0; } delete pTTFontFile; pTTFontFile = NULL; } else { pCodeToGID = NULL; nLen = 0; } break; } case fontCIDType2: case fontCIDType2OT: { // Create CID-to-GID map // If font was not embedded and was substituted and has ToUnicode map, read GIDs from file based on unicode values. // For embedded fonts use CIDtoGID map pCodeToGID = NULL; nLen = 0; if (L"" != wsFileName && bFontSubstitution) { CharCodeToUnicode* pCodeToUnicode = NULL; if ((pCodeToUnicode = ((GfxCIDFont*)pFont)->getToUnicode())) { #ifdef FONTS_USE_ONLY_MEMORY_STREAMS pTTFontFile = FoFiTrueType::make((char*)oMemoryFontStream.m_pData, oMemoryFontStream.m_nSize, 0); #else pTTFontFile = FoFiTrueType::load((char*)U_TO_UTF8(wsFileName).c_str(), 0); #endif if (pTTFontFile) { // Looking for Unicode Cmap std::vector arrCMapIndex; for (int nCMapIndex = 0; nCMapIndex < pTTFontFile->getNumCmaps(); ++nCMapIndex) { if ((pTTFontFile->getCmapPlatform(nCMapIndex) == 3 && pTTFontFile->getCmapEncoding(nCMapIndex) == 1) || pTTFontFile->getCmapPlatform(nCMapIndex) == 0) { arrCMapIndex.push_back(nCMapIndex); } } if (arrCMapIndex.size() > 0) { // CID -> Unicode -> GID nLen = pCodeToUnicode->getLength(); pCodeToGID = (int*)MemUtilsMallocArray(nLen, sizeof(int)); for (int nCode = 0; nCode < nLen; ++nCode) { Unicode arrUnicodeBuffer[8]; if (pCodeToUnicode->mapToUnicode(nCode, arrUnicodeBuffer, 8) > 0) { pCodeToGID[nCode] = pTTFontFile->mapCodeToGID(arrCMapIndex[0], arrUnicodeBuffer[0]); for (size_t nIndex = 1; nIndex < arrCMapIndex.size(); nIndex++) { if (0 == pCodeToGID[nCode]) pCodeToGID[nCode] = pTTFontFile->mapCodeToGID(arrCMapIndex[nIndex], arrUnicodeBuffer[0]); else break; } } else { pCodeToGID[nCode] = 0; } } } delete pTTFontFile; pTTFontFile = NULL; } pCodeToUnicode->decRefCnt(); } } else if (((GfxCIDFont*)pFont)->getCIDToGID()) { nLen = ((GfxCIDFont*)pFont)->getCIDToGIDLen(); pCodeToGID = (int*)MemUtilsMallocArray(nLen, sizeof(int)); if (!pCodeToGID) break; memcpy(pCodeToGID, ((GfxCIDFont*)pFont)->getCIDToGID(), nLen * sizeof(int)); } break; } default: { // This should not happen #ifndef FONTS_USE_ONLY_MEMORY_STREAMS if (L"" != wsTempFileName) NSFile::CFileBinary::Remove(wsTempFileName); #endif break; } } // Build Code -> Unicode table int nToUnicodeLen = 0; if (pFont->isCIDFont()) { GfxCIDFont* pCIDFont = (GfxCIDFont*)pFont; CharCodeToUnicode* pToUnicode = pCIDFont->getToUnicode(); if (NULL != pToUnicode) { nToUnicodeLen = pToUnicode->getLength(); pCodeToUnicode = (int*)MemUtilsMallocArray(nToUnicodeLen, sizeof(int)); if (pCodeToUnicode) { for (int nIndex = 0; nIndex < nToUnicodeLen; ++nIndex) { Unicode aUnicode[2]; if (pToUnicode->mapToUnicode(nIndex, aUnicode, 2)) pCodeToUnicode[nIndex] = aUnicode[0]; else pCodeToUnicode[nIndex] = 0; } } pToUnicode->decRefCnt(); } } else { // memory troubles here CharCodeToUnicode* pToUnicode = ((Gfx8BitFont*)pFont)->getToUnicode(); if (NULL != pToUnicode) { nToUnicodeLen = pToUnicode->getLength(); pCodeToUnicode = (int*)MemUtilsMallocArray(nToUnicodeLen, sizeof(int));//literally here if (pCodeToUnicode) { for (int nIndex = 0; nIndex < nToUnicodeLen; ++nIndex) { Unicode nUnicode = 0; if (pToUnicode->mapToUnicode(nIndex, &nUnicode, 1)) pCodeToUnicode[nIndex] = (unsigned short)nUnicode; else pCodeToUnicode[nIndex] = nIndex; } } pToUnicode->decRefCnt(); } } // Trim index from FontName if present if (wsFontName.empty()) wsFontName = wsFontBaseName; if (bNotFullName) EraseSubsetTag(wsFontName); else if (!bFontBase14 && !bFontSubstitution) wsFontName += (L" " + ComputeFontHash(pXref, pFont)); pEntry->wsFilePath = wsFileName; pEntry->wsFontName = wsFontName; pEntry->pCodeToGID = pCodeToGID; pEntry->pCodeToUnicode = pCodeToUnicode; pEntry->unLenGID = (unsigned int)nLen; pEntry->unLenUnicode = (unsigned int)nToUnicodeLen; pEntry->bAvailable = true; pEntry->dStretch = dStretch; pEntry->bFontSubstitution = bFontSubstitution; pEntry->bIsIdentity = pFont->isCIDFont() == gTrue ? ((GfxCIDFont*)pFont)->usesIdentityEncoding() || ((GfxCIDFont*)pFont)->usesIdentityCIDToGID() || ((GfxCIDFont*)pFont)->ctuUsesCharCodeToUnicode() || pFont->getType() == fontCIDType0C : false; } else if (NULL != pEntry) { wsFileName = pEntry->wsFilePath; wsFontName = pEntry->wsFontName; } } CType3FontMetrics* BuildType3FontMetrics(XRef* pXref, GfxFont* pFont) { CType3FontMetrics* pMetrics = new CType3FontMetrics(); Ref* pRef = pFont->getID(); Object oRefObj, oFontObj; oRefObj.initRef(pRef->num, pRef->gen); oRefObj.fetch(pXref, &oFontObj); oRefObj.free(); if (!oFontObj.isDict()) { oFontObj.free(); return pMetrics; } Object oItem; if (oFontObj.dictLookup("FontMatrix", &oItem)->isArray() && oItem.arrayGetLength() == 6) { for (int i = 0; i < 6; ++i) { Object oVal; if (oItem.arrayGet(i, &oVal)->isNum()) pMetrics->arrFontMatrix[i] = oVal.getNum(); oVal.free(); } } oItem.free(); if (pMetrics->arrFontMatrix[0] > 0) pMetrics->dUnitsPerEm = std::round(1.0 / pMetrics->arrFontMatrix[0]); else pMetrics->dUnitsPerEm = 1000; if (oFontObj.dictLookup("FontBBox", &oItem)->isArray() && oItem.arrayGetLength() == 4) { Object oVal; if (oItem.arrayGet(0, &oVal)->isNum()) pMetrics->dLLx = oVal.getNum(); oVal.free(); if (oItem.arrayGet(1, &oVal)->isNum()) pMetrics->dLLy = oVal.getNum(); oVal.free(); if (oItem.arrayGet(2, &oVal)->isNum()) pMetrics->dURx = oVal.getNum(); oVal.free(); if (oItem.arrayGet(3, &oVal)->isNum()) pMetrics->dURy = oVal.getNum(); oVal.free(); double dTextY1 = pMetrics->arrFontMatrix[3] * pMetrics->dLLy; double dTextY2 = pMetrics->arrFontMatrix[3] * pMetrics->dURy; pMetrics->dAscent = std::round(std::max(dTextY2 / pMetrics->arrFontMatrix[0], 0.0)); pMetrics->dDescent = std::round(std::abs(std::min(dTextY1 / pMetrics->arrFontMatrix[0], 0.0))); if (pMetrics->dAscent == 0 && pMetrics->dDescent == 0) { pMetrics->dAscent = pMetrics->dUnitsPerEm * 0.8; pMetrics->dDescent = pMetrics->dUnitsPerEm * 0.2; } } oItem.free(); int nFirstChar = 0; if (oFontObj.dictLookup("FirstChar", &oItem)->isInt()) nFirstChar = oItem.getInt(); oItem.free(); if (oFontObj.dictLookup("Widths", &oItem)->isArray()) { int nWidthsLen = oItem.arrayGetLength(); for (int i = 0; i < nWidthsLen; ++i) { Object oVal; if (oItem.arrayGet(i, &oVal)->isNum()) pMetrics->mapWidths[nFirstChar + i] = oVal.getNum(); oVal.free(); } } oItem.free(); oFontObj.free(); return pMetrics; } }