Files
core/PdfFile/SrcReader/PdfFont.cpp
Svetlana Kulikova 507afb473f All fonts to PdfFont
2026-05-25 17:29:34 +03:00

2490 lines
71 KiB
C++

/*
* 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<std::string> 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<std::string>& arrCMap, int nDepth, std::vector<int>& 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<std::string>& 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<std::string>& arrCMap, std::vector<int>& arrUniqueResources)
{
Object oAP;
if (!oAnnot->dictLookup("AP", &oAP)->isDict())
{
oAP.free();
return false;
}
auto fScanAPView = [&arrUniqueResources](Object* oAP, const std::vector<std::string>& 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<CFontData*> ReadRC(const std::string& sRC)
{
std::vector<CFontData*> 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<double>& arrCFromDA)
{
NSStringUtils::CStringBuilder oRC;
oRC += L"<?xml version=\"1.0\"?><body xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\" xfa:APIVersion=\"Acrobat:23.8.0\" xfa:spec=\"2.0.2\"><p dir=\"ltr\"><span style=\"";
if (sDS.find("font-family") == std::string::npos)
oRC += L"font-family:Helvetica;";
if (sDS.find("font-size") == std::string::npos)
oRC += L"font-size:14.0pt;";
if (sDS.find("text-align") == std::string::npos)
oRC += L"text-align:left;";
if (sDS.find("font-weight") == std::string::npos)
oRC += L"font-weight:normal;";
if (sDS.find("font-style") == std::string::npos)
oRC += L"font-style:normal;";
if (sDS.find("text-decoration") == std::string::npos)
oRC += L"text-decoration:none;";
if (sDS.find("color") == std::string::npos)
{
oRC += L"color:";
if (arrCFromDA.size() == 3)
oRC.WriteHexColor3((unsigned char)(arrCFromDA[0] * 255.0),
(unsigned char)(arrCFromDA[1] * 255.0),
(unsigned char)(arrCFromDA[2] * 255.0));
else
oRC += L"#000000";
}
oRC += (UTF8_TO_U(sDS));
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"</span></p></body>";
std::wstring wsRC = oRC.GetData();
return U_TO_UTF8(wsRC);
}
bool IsNeedCMap(PDFDoc* pDoc)
{
std::vector<int> 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<std::wstring, std::wstring> GetAllFonts(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList)
{
std::map<std::wstring, std::wstring> mFonts;
AcroForm* pAcroForms = pdfDoc->getCatalog()->getForm();
if (pAcroForms)
{
std::vector<int> 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<CFontData*> arrRC = ReadRC(sRC);
std::map<std::wstring, std::wstring> mFreeText = GetFreeTextFont(pdfDoc, pFontManager, pFontList, &oAnnotRef, arrRC);
for (std::map<std::wstring, std::wstring>::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<CAnnotFontInfo> GetAnnotFontInfos(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef)
{
std::vector<CAnnotFontInfo> 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<CAnnotFontInfo>& 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<std::wstring, std::wstring> GetFreeTextFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef, std::vector<CFontData*>& arrRC)
{
std::map<std::wstring, std::wstring> mRes;
std::vector<CAnnotFontInfo> 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<unsigned int, unsigned int>& 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<Gfx8BitFont*>(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<Gfx8BitFont*>(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<GfxCIDFont*>(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<GfxCIDFont*>(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<int> 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;
}
}