/* * (c) Copyright Ascensio System SIA 2010-2023 * * 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. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * 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: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ #include "FontCidTT.h" #include "Document.h" #include "Streams.h" #include "Utils.h" #include "FontTTWriter.h" #include "../../DesktopEditor/graphics/pro/Fonts.h" #include "../../DesktopEditor/common/File.h" #include #include FT_TRUETYPE_TABLES_H namespace PdfWriter { static const char* c_sToUnicodeHeader = "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n"; static const char* c_sToUnicodeInfo = "/CIDSystemInfo\n<< /Registry (Adobe)\n /Ordering (UCS)\n /Supplement 0\n >> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> \nendcodespacerange\n"; static const char* c_sToUnicodeFooter = "endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n"; static int GetSymbolicCmapIndex(FT_Face pFace) { TT_OS2 *pOs2 = (TT_OS2 *)FT_Get_Sfnt_Table(pFace, ft_sfnt_os2); if (NULL == pOs2 || 0xFFFF == pOs2->version) return -1; // Проверяем установлен ли 31 бит if (!(pOs2->ulCodePageRange1 & 0x80000000) && !(pOs2->ulCodePageRange1 == 0 && pOs2->ulCodePageRange2 == 0)) return -1; for (int nIndex = 0; nIndex < pFace->num_charmaps; nIndex++) { // Symbol if (0 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id) return nIndex; } return -1; } //---------------------------------------------------------------------------------------- // CFontFileBase //---------------------------------------------------------------------------------------- CFontCidTrueType::CFontCidTrueType(CXref* pXref, CDocument* pDocument, const std::wstring& wsFontPath, unsigned int unIndex, CFontFileTrueType* pFontTT) : CFontDict(pXref, pDocument) { m_bNeedAddFontName = true; m_pFontFile = pFontTT; m_wsFontPath = wsFontPath; m_unFontIndex = unIndex; Add("Type", "Font"); Add("Subtype", "Type0"); Add("Encoding", "Identity-H"); CDictObject* pFont = new CDictObject(); m_pXref->Add(pFont); CArrayObject* pDescendantFonts = new CArrayObject(); pDescendantFonts->Add(pFont); Add("DescendantFonts", pDescendantFonts); CDictObject* pToUnicodeDict = new CDictObject(m_pXref); Add("ToUnicode", pToUnicodeDict); pToUnicodeDict->SetFilter(STREAM_FILTER_FLATE_DECODE); m_pToUnicodeStream = pToUnicodeDict->GetStream(); CreateCIDFont(pFont); if (pFontTT->GetOpenTypeCFF()) CreateCIDFont0(pFont); else CreateCIDFont2(pFont); m_pFace = NULL; m_pFaceMemory = NULL; m_nGlyphsCount = 0; m_nSymbolicCmap = -1; m_ushCodesCount = 0; } CFontCidTrueType::~CFontCidTrueType() { if (m_pFontFile) delete m_pFontFile; if (m_pFace) FT_Done_Face(m_pFace); if (m_pFaceMemory) delete[] m_pFaceMemory; } void CFontCidTrueType::CreateCIDFont(CDictObject* pFont) { m_pFont = pFont; pFont->Add("Type", "Font"); CDictObject* pSystemInfo = new CDictObject(); pSystemInfo->Add("Registry", new CStringObject("Adobe")); pSystemInfo->Add("Ordering", new CStringObject("Identity")); pSystemInfo->Add("Supplement", 0); pFont->Add("CIDSystemInfo", pSystemInfo); CDictObject* pFontDescriptor = new CDictObject(); // FontDescriptor обязательно должен идти ссылкой m_pXref->Add(pFontDescriptor); pFontDescriptor->Add("Type", "FontDescriptor"); m_pFontDescriptor = pFontDescriptor; // Выставляем бит Symbolic, а бит NonSymbolic убираем unsigned int nFlags = 0; if (!(nFlags & 4)) UIntChangeBit(nFlags, 2); if (nFlags & 32) UIntChangeBit(nFlags, 4); pFontDescriptor->Add("Flags", nFlags); if (m_pFontFile) { CArrayObject* pBBox = new CArrayObject(); int* pFontBBox = m_pFontFile->GetBBox(); pBBox->Add(pFontBBox[0]); pBBox->Add(pFontBBox[1]); pBBox->Add(pFontBBox[2]); pBBox->Add(pFontBBox[3]); pFontDescriptor->Add("FontBBox", pBBox); } pFontDescriptor->Add("ItalicAngle", 0); pFontDescriptor->Add("Ascent", m_pFontFile ? m_pFontFile->GetAscent() : 1000); pFontDescriptor->Add("Descent", m_pFontFile ? m_pFontFile->GetDescent() : -500); pFontDescriptor->Add("CapHeight", m_pFontFile ? m_pFontFile->GetCapHeight() : 800); pFontDescriptor->Add("StemV", 0); pFontDescriptor->Add("FontWeight", m_pFontFile ? m_pFontFile->GetWeight() : 400); m_pFontFileDict = new CDictObject(m_pXref); pFont->Add("FontDescriptor", pFontDescriptor); } void CFontCidTrueType::CreateCIDFont0(CDictObject* pFont) { pFont->Add("Subtype", "CIDFontType0"); pFont->Add("DW", 1000); m_pFontDescriptor->Add("FontFile3", m_pFontFileDict); m_pFontDescriptor->Add("CIDSet", new CDictObject(m_pXref)); } void CFontCidTrueType::CreateCIDFont2(CDictObject* pFont) { pFont->Add("Subtype", "CIDFontType2"); m_pFontDescriptor->Add("FontFile2", m_pFontFileDict); CDictObject* pCIDToGIDMapDict = new CDictObject(m_pXref); pFont->Add("CIDToGIDMap", pCIDToGIDMapDict); pCIDToGIDMapDict->SetFilter(STREAM_FILTER_FLATE_DECODE); m_pCidToGidMapStream = pCIDToGIDMapDict->GetStream(); if (m_pXref->IsPDFA()) { //pFontDescriptor->Add("CIDSet", new CDictObject(m_pXref)); } } bool CFontCidTrueType::HaveChar(const unsigned int &unUnicode) { if (!OpenFontFace()) return false; return (!!GetGID(m_pFace, unUnicode)); } unsigned int CFontCidTrueType::GetChar(const unsigned int &unUnicode) { if (!OpenFontFace()) return 0; return GetGID(m_pFace, unUnicode); } unsigned int CFontCidTrueType::GetWidth(unsigned short ushCode) { if (ushCode >= m_vWidths.size()) return 0; return m_vWidths.at(ushCode); } unsigned int CFontCidTrueType::GetGlyphWidth(unsigned short ushCode) { if (ushCode >= m_vGlypWidths.size()) return 0; return m_vGlypWidths.at(ushCode); } bool CFontCidTrueType::IsItalic() { if (!OpenFontFace() || !m_pFace) return false; return ((m_pFace->style_flags & FT_STYLE_FLAG_ITALIC) != 0); } bool CFontCidTrueType::IsBold() { if (!OpenFontFace() || !m_pFace) return false; TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2); if (pOS2 && pOS2->version != 0xFFFF && pOS2->usWeightClass >= 800) return true; return ((m_pFace->style_flags & FT_STYLE_FLAG_BOLD) != 0); } void CFontCidTrueType::BeforeWrite() { if (m_pFontDescriptor) { CDictObject* pCIDSet = (CDictObject*)m_pFontDescriptor->Get("CIDSet"); if (pCIDSet) { #ifndef FILTER_FLATE_DECODE_DISABLED pCIDSet->SetFilter(STREAM_FILTER_FLATE_DECODE); #endif CStream* pStream = pCIDSet->GetStream(); unsigned int unBytes = (m_ushCodesCount) / 8; if (unBytes * 8 < m_ushCodesCount) unBytes++; if (1 == unBytes) { BYTE nValue = 0xFF; nValue = (BYTE)(nValue << (8 - m_ushCodesCount)); nValue &= 0x7F; pStream->WriteChar(nValue); } else { BYTE nStartValue = 0x7F, nMidValue = 0xFF; pStream->WriteChar(nStartValue); for (unsigned int unIndex = 0; unIndex < unBytes - 2; ++unIndex) { pStream->WriteChar(nMidValue); } BYTE nEndValue = 0xFF; nEndValue = (BYTE)(nEndValue << (unBytes * 8 - m_ushCodesCount)); pStream->WriteChar(nEndValue); } } } if (m_pFontFile) { unsigned short* pCodeToGid; unsigned int* pWidths; unsigned char* pGlyphs; unsigned int unGlyphsCount; if (!GetWidthsAndGids(&pCodeToGid, &pWidths, &pGlyphs, unGlyphsCount)) return; CStream* pStream = m_pFontFileDict->GetStream(); m_pFontFile->WriteTTF(pStream, NULL, pCodeToGid, m_ushCodesCount, pGlyphs, unGlyphsCount); m_pFontFileDict->SetFilter(STREAM_FILTER_FLATE_DECODE); CArrayObject* pWArray = new CArrayObject(); m_pFont->Add("W", pWArray); pWArray->Add(0); CArrayObject* pWidthsArray = new CArrayObject(); pWArray->Add(pWidthsArray); for (unsigned short ushIndex = 0; ushIndex < m_ushCodesCount; ushIndex++) { pWidthsArray->Add(pWidths[ushIndex]); } if (!m_pFontFile->GetOpenTypeCFF()) { m_pFontFileDict->Add("Length1", pStream->Size()); pStream = m_pCidToGidMapStream; for (unsigned short ushCode = 0; ushCode < m_ushCodesCount; ushCode++) { unsigned short ushGid = pCodeToGid[ushCode]; pStream->WriteChar(((ushGid >> 8) & 0xFF)); pStream->WriteChar((ushGid & 0xFF)); } } else { m_pFont->Add("DW", pWidths[0]); m_pFontFileDict->Add("Subtype", "CIDFontType0C"); } RELEASEARRAYOBJECTS(pCodeToGid); RELEASEARRAYOBJECTS(pWidths); RELEASEARRAYOBJECTS(pGlyphs); WriteToUnicode(); } } bool CFontCidTrueType::GetWidthsAndGids(unsigned short** ppCodeToGid, unsigned int** ppWidths, unsigned char** ppGlyphs, unsigned int& unGlyphsCount) { *ppCodeToGid = NULL; *ppWidths = NULL; *ppGlyphs = NULL; unGlyphsCount = 0; if (!m_nGlyphsCount) return false; unsigned short* pCodeToGID = new unsigned short[m_ushCodesCount]; if (!pCodeToGID) return false; unsigned int* pWidths = new unsigned int[m_ushCodesCount]; if (!pWidths) { delete[] pCodeToGID; return false; } memset((void*)pWidths, 0x00, m_ushCodesCount * sizeof(unsigned int)); for (unsigned short ushCode = 0; ushCode < m_ushCodesCount; ushCode++) { pCodeToGID[ushCode] = m_vCodeToGid.at(ushCode); pWidths[ushCode] = m_vWidths.at(ushCode); } unsigned char *pGlyphs = new unsigned char[m_nGlyphsCount]; if (!pGlyphs) { delete[] pCodeToGID; delete[] pWidths; return false; } memset((void *)pGlyphs, 0x00, m_nGlyphsCount * sizeof(unsigned char)); if (m_nGlyphsCount >= m_mGlyphs.size()) { for (auto oIt : m_mGlyphs) { pGlyphs[oIt.first] = 1; } } *ppCodeToGid = pCodeToGID; *ppWidths = pWidths; *ppGlyphs = pGlyphs; unGlyphsCount = m_nGlyphsCount; return true; } void CFontCidTrueType::WriteToUnicode() { CStream* pS = m_pToUnicodeStream; pS->WriteStr(c_sToUnicodeHeader); pS->WriteStr(c_sToUnicodeInfo); int pCodesCount = pS->Tell(); int nCodesCount = 0; pS->WriteInt(m_ushCodesCount); pS->WriteStr(" beginbfchar\n"); for (unsigned short ushCode = 0; ushCode < m_ushCodesCount; ushCode++) { if (!m_mGlyphs[m_vCodeToGid[ushCode]]) continue; pS->WriteChar('<'); pS->WriteHex(ushCode, 4); pS->WriteStr("> <"); for (unsigned int i = 0, nLen = m_vUnicodes[ushCode].size(); i < nLen; i++) { unsigned int unUnicode = m_vUnicodes[ushCode][i]; if (unUnicode < 0x10000) { pS->WriteHex(unUnicode, 4); } else { unUnicode = unUnicode - 0x10000; unsigned short ushLo = 0xDC00 | (unUnicode & 0x3FF); unsigned short ushHi = 0xD800 | (unUnicode >> 10); pS->WriteHex(ushHi, 4); pS->WriteHex(ushLo, 4); } } pS->WriteStr(">\n"); nCodesCount++; } pS->WriteStr("endbfchar\n"); pS->WriteStr(c_sToUnicodeFooter); pS->Seek(pCodesCount, SeekSet); pS->WriteInt(nCodesCount); } void CFontCidTrueType::CloseFontFace() { if (m_pFace) { FT_Done_Face(m_pFace); m_pFace = NULL; } RELEASEARRAYOBJECTS(m_pFaceMemory); } bool CFontCidTrueType::OpenFontFace() { if (m_pFace) { m_pDocument->AddFreeTypeFont(this); return true; } m_nGlyphsCount = 0; m_nSymbolicCmap = -1; FT_Library pLibrary = m_pDocument->GetFreeTypeLibrary(); if (!pLibrary) return false; DWORD dwFileSize; m_pFaceMemory = NULL; NSFile::CFileBinary::ReadAllBytes(m_wsFontPath, &m_pFaceMemory, dwFileSize); if (!m_pFaceMemory) return false; FT_New_Memory_Face(pLibrary, m_pFaceMemory, dwFileSize, m_unFontIndex, &m_pFace); if (!m_pFace) { RELEASEARRAYOBJECTS(m_pFaceMemory); return false; } m_pDocument->AddFreeTypeFont(this); m_nGlyphsCount = m_pFace->num_glyphs; m_nSymbolicCmap = GetSymbolicCmapIndex(m_pFace); if (m_bNeedAddFontName) { // Дописываем имя шрифта во все необходимые словари, а также заполняем дескриптор std::string sFontName = m_pDocument->GetTTFontTag(); sFontName += (m_pFace->family_name ? std::string(m_pFace->family_name) : std::string()); if (m_pFace->style_flags & FT_STYLE_FLAG_ITALIC) sFontName += "-Italic"; if (m_pFace->style_flags & FT_STYLE_FLAG_BOLD) sFontName += "-Bold"; const char* sName = sFontName.c_str(); Add("BaseFont", sName); m_pFont->Add("BaseFont", sName); m_pFontDescriptor->Add("FontName", sName); m_bNeedAddFontName = false; m_pFontFile->SetName(sFontName); } return true; } unsigned short CFontCidTrueType::EncodeUnicode(const unsigned int &unUnicode) { std::map::const_iterator oIter = m_mUnicodeToCode.find(unUnicode); if (oIter != m_mUnicodeToCode.end()) return oIter->second; unsigned int unGID = GetGID(m_pFace, unUnicode); if (0 == unGID && -1 != m_nSymbolicCmap) unGID = GetGID(m_pFace, unUnicode + 0xF000); unsigned short ushCode = EncodeGID(unGID, &unUnicode, 1); m_mUnicodeToCode.insert(std::pair(unUnicode, ushCode)); return ushCode; } unsigned short CFontCidTrueType::EncodeGID(const unsigned int& unGID, const unsigned int* pUnicodes, const unsigned int& unCount) { for (unsigned short ushCurCode = 0, ushCodesCount = m_vCodeToGid.size(); ushCurCode < ushCodesCount; ushCurCode++) { if (unGID == m_vCodeToGid.at(ushCurCode)) { if (m_vUnicodes.at(ushCurCode).empty() && unCount) { std::vector vUnicodes; for (unsigned int i = 0; i < unCount; i++) vUnicodes.push_back(pUnicodes[i]); m_vUnicodes[ushCurCode] = vUnicodes; m_mGlyphs[unGID] = true; } return ushCurCode; } } if (!OpenFontFace()) return 0; unsigned short ushCode = m_ushCodesCount++; std::vector vUnicodes; for (unsigned int i = 0; i < unCount; i++) { vUnicodes.push_back(pUnicodes[i]); } m_vUnicodes.push_back(vUnicodes); m_vCodeToGid.push_back(unGID); m_mGlyphs.insert(std::pair(unGID, true)); // Если данный символ составной (CompositeGlyf), тогда мы должны учесть все его дочерные символы (subglyfs) if (0 == FT_Load_Glyph(m_pFace, unGID, FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE)) { if (0 != m_pFace->units_per_EM) { m_vWidths.push_back((unsigned int)m_pFace->glyph->metrics.horiAdvance * 1000 / m_pFace->units_per_EM); m_vGlypWidths.push_back((unsigned int)(m_pFace->glyph->metrics.width) * 1000 / m_pFace->units_per_EM); } else { m_vWidths.push_back((unsigned int)m_pFace->glyph->metrics.horiAdvance); m_vGlypWidths.push_back((unsigned int)(m_pFace->glyph->metrics.width) * 1000 / m_pFace->units_per_EM); } for (int nSubIndex = 0; nSubIndex < m_pFace->glyph->num_subglyphs; nSubIndex++) { FT_Int nSubGID; FT_UInt unFlags; FT_Int nArg1; FT_Int nArg2; FT_Matrix oMatrix; FT_Get_SubGlyph_Info(m_pFace->glyph, nSubIndex, &nSubGID, &unFlags, &nArg1, &nArg2, &oMatrix); m_mGlyphs.insert(std::pair(nSubGID, false)); EncodeGID(nSubGID, NULL, 0); FT_Load_Glyph(m_pFace, unGID, FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE); } } else { m_vWidths.push_back(0); m_vGlypWidths.push_back(0); m_vCodeToGid.back() = 0; return 0; } return ushCode; } }