Files
DocumentServer-v-9.2.0/core/DesktopEditor/fontengine/ApplicationFontsWorker.cpp
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1895 lines
53 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (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 "ApplicationFontsWorker.h"
#include <map>
#include <set>
#include "../common/File.h"
#include "../common/Directory.h"
#include "../common/Array.h"
#include "../common/StringBuilder.h"
#include "../common/StringExt.h"
#include "../common/ByteBuilder.h"
#include "../graphics/pro/Fonts.h"
#include "../raster/BgraFrame.h"
#include "../graphics/pro/Graphics.h"
bool g_sort_font_names(const std::wstring& i, const std::wstring& j)
{
std::wstring s1 = i;
std::wstring s2 = j;
wchar_t symbol;
size_t len = s1.length();
for (size_t c = 0; c < len; ++c)
{
symbol = s1[c];
if (symbol >= 'A' && symbol <= 'Z')
s1[c] = symbol + 'a' - 'A';
}
len = s2.length();
for (size_t c = 0; c < len; ++c)
{
symbol = s2[c];
if (symbol >= 'A' && symbol <= 'Z')
s2[c] = symbol + 'a' - 'A';
}
return s1 < s2;
}
class CApplicationFontsWorker_private
{
public:
CApplicationFontsWorker_private(CApplicationFontsWorker* pMain)
{
m_pMain = pMain;
m_pBreaker = NULL;
m_bIsCheckThumbnailsMode = false;
}
~CApplicationFontsWorker_private()
{
}
public:
class CFontThumbnail
{
public:
std::wstring m_sFile;
std::wstring m_sName;
int m_lFaceIndex;
public:
CFontThumbnail()
{
m_sFile = L"";
m_sName = L"";
m_lFaceIndex = 0;
}
CFontThumbnail(const CFontThumbnail& oSrc)
{
m_sFile = oSrc.m_sFile;
m_sName = oSrc.m_sName;
m_lFaceIndex = oSrc.m_lFaceIndex;
}
CFontThumbnail& operator=(const CFontThumbnail& oSrc)
{
m_sFile = oSrc.m_sFile;
m_sName = oSrc.m_sName;
m_lFaceIndex = oSrc.m_lFaceIndex;
return *this;
}
};
public:
CApplicationFontsWorker* m_pMain;
CApplicationFontsWorkerBreaker* m_pBreaker;
std::vector<CFontThumbnail> m_arFonts;
bool m_bIsCheckThumbnailsMode;
std::map<std::wstring, std::wstring> m_mapAlternateEA;
class CFontInfoJS
{
public:
std::wstring m_sName;
int m_lIndexR;
int m_lFaceIndexR;
std::wstring m_sStyleR;
int m_lIndexI;
int m_lFaceIndexI;
std::wstring m_sStyleI;
int m_lIndexB;
int m_lFaceIndexB;
std::wstring m_sStyleB;
int m_lIndexBI;
int m_lFaceIndexBI;
std::wstring m_sStyleBI;
CFontInfoJS()
{
m_sName = L"";
m_lIndexR = -1;
m_lFaceIndexR = -1;
m_lIndexI = -1;
m_lFaceIndexI = -1;
m_lIndexB = -1;
m_lFaceIndexB = -1;
m_lIndexBI = -1;
m_lFaceIndexBI = -1;
m_sStyleR = L"";
m_sStyleI = L"";
m_sStyleB = L"";
m_sStyleBI = L"";
}
CFontInfoJS(const CFontInfoJS& oSrc)
{
*this = oSrc;
}
CFontInfoJS& operator=(const CFontInfoJS& oSrc)
{
m_sName = oSrc.m_sName;
m_lIndexR = oSrc.m_lIndexR;
m_lIndexI = oSrc.m_lIndexI;
m_lIndexB = oSrc.m_lIndexB;
m_lIndexBI = oSrc.m_lIndexBI;
m_lFaceIndexR = oSrc.m_lFaceIndexR;
m_lFaceIndexI = oSrc.m_lFaceIndexI;
m_lFaceIndexB = oSrc.m_lFaceIndexB;
m_lFaceIndexBI = oSrc.m_lFaceIndexBI;
m_sStyleR = oSrc.m_sStyleR;
m_sStyleI = oSrc.m_sStyleI;
m_sStyleB = oSrc.m_sStyleB;
m_sStyleBI = oSrc.m_sStyleBI;
return *this;
}
bool IsNeedUpdateR()
{
return !NSFonts::CFontInfo::IsStyleRegular(m_sStyleR);
}
bool IsNeedUpdateI()
{
return !NSFonts::CFontInfo::IsStyleItalic(m_sStyleI);
}
bool IsNeedUpdateB()
{
return !NSFonts::CFontInfo::IsStyleBold(m_sStyleB);
}
bool IsNeedUpdateBI()
{
return !NSFonts::CFontInfo::IsStyleBoldItalic(m_sStyleBI);
}
};
class CSymbolSimpleChecker : public NSFonts::CApplicationFontsSymbolsChecker
{
public:
int m_nMaxSymbols;
int* m_pSymbols;
int m_nPriority;
public:
CSymbolSimpleChecker()
{
}
virtual void Check(const int& nCode, const unsigned int& nIndex)
{
if (nCode > m_nMaxSymbols)
return;
if (m_pSymbols[nCode] == 0)
m_pSymbols[nCode] = m_nPriority;
else if (m_pSymbols[nCode] > m_nPriority)
m_pSymbols[nCode] = m_nPriority;
}
~CSymbolSimpleChecker()
{
}
};
class CSymbolSimpleChecker2 : public NSFonts::CApplicationFontsSymbolsChecker
{
public:
int m_nMaxSymbols;
int* m_pSymbols;
int m_nPriority;
int m_nStyle;
BYTE* m_pTmpSymbols;
int m_nMin;
int m_nMax;
public:
CSymbolSimpleChecker2(int* pSymbols, int nMax)
{
m_pSymbols = pSymbols;
m_nMaxSymbols = nMax;
m_pTmpSymbols = new BYTE[m_nMaxSymbols + 1];
memset(m_pTmpSymbols, 0, (m_nMaxSymbols + 1) * sizeof(BYTE));
m_nMin = m_nMaxSymbols + 1;
m_nMax = -1;
m_nStyle = 0;
}
void Apply1(int nMask)
{
BYTE* tmp = m_pTmpSymbols + m_nMin;
for (int i = m_nMin; i <= m_nMax; ++i, ++tmp)
{
if (nMask == *tmp)
{
if (m_pSymbols[i] == 0)
m_pSymbols[i] = m_nPriority;
else if (m_pSymbols[i] > m_nPriority)
m_pSymbols[i] = m_nPriority;
}
}
if (m_nMax >= m_nMin)
memset(m_pTmpSymbols, 0, (m_nMax - m_nMin) * sizeof(BYTE));
m_nMin = m_nMaxSymbols + 1;
m_nMax = -1;
}
void Apply2(int nMask, int nSumPriority)
{
int nSmallRangeLen = 10;
int nSmallRangeLenCJK = 30;
BYTE* tmp = m_pTmpSymbols + m_nMin;
BYTE* tmpLast = m_pTmpSymbols + m_nMax + 1;
BYTE* tmpFirst = NULL;
int* pSymbols = NULL;
int* pSymbolsLast = NULL;
int nPriority = 0;
int nFirstOffset = 0;
while (tmp < tmpLast)
{
if (nMask != *tmp)
{
++tmp;
continue;
}
tmpFirst = tmp;
int nCount = 1;
++tmp;
while (tmp < tmpLast && nMask == *tmp)
{
++tmp;
nCount++;
}
nFirstOffset = (int)(tmpFirst - m_pTmpSymbols);
pSymbols = m_pSymbols + nFirstOffset;
pSymbolsLast = pSymbols + nCount;
if (m_nPriority != 1)
{
if (nFirstOffset > 0x4DFF && nFirstOffset < 0x9FFF)
nPriority = (nCount > nSmallRangeLenCJK) ? m_nPriority : (m_nPriority + nSumPriority);
else
nPriority = (nCount > nSmallRangeLen) ? m_nPriority : (m_nPriority + nSumPriority);
}
else
{
// для шрифта ASCW3 допускаем маленькую длину range
nPriority = m_nPriority;
}
while (pSymbols < pSymbolsLast)
{
if (*pSymbols == 0)
*pSymbols = nPriority;
else if (*pSymbols > nPriority)
*pSymbols = nPriority;
++pSymbols;
}
}
if (m_nMax >= m_nMin)
memset(m_pTmpSymbols, 0, (m_nMax - m_nMin) * sizeof(BYTE));
m_nMin = m_nMaxSymbols + 1;
m_nMax = -1;
}
virtual void Check(const int& nCode, const unsigned int& nIndex)
{
if (nCode > m_nMaxSymbols)
return;
if (nCode > m_nMax)
m_nMax = nCode;
if (nCode < m_nMin)
m_nMin = nCode;
m_pTmpSymbols[nCode] |= m_nStyle;
}
~CSymbolSimpleChecker2()
{
RELEASEARRAYOBJECTS(m_pTmpSymbols);
}
};
class CFontPriority
{
public:
std::wstring name;
int priority;
CFontPriority()
{
priority = 0;
}
static bool Compare(const CFontPriority& p1, const CFontPriority& p2)
{
if (p1.priority != p2.priority)
return (p1.priority < p2.priority) ? true : false;
return (p1.name < p2.name) ? true : false;
}
};
public:
inline bool CheckBreak()
{
if (m_pBreaker && !m_pBreaker->IsFontsWorkerRunned())
return true;
return false;
}
bool IsEastAsianScript(const unsigned int& value)
{
// Bopomofo (3100312F)
// Bopomofo Extended (31A031BF)
// CJK Unified Ideographs (4E009FEA)
// CJK Unified Ideographs Extension A (34004DB5)
// CJK Unified Ideographs Extension B (200002A6D6)
// CJK Unified Ideographs Extension C (2A7002B734)
// CJK Unified Ideographs Extension D (2B7402B81D)
// CJK Unified Ideographs Extension E (2B8202CEA1)
// CJK Unified Ideographs Extension F (2CEB02EBE0)
// CJK Compatibility Ideographs (F900FAFF)
// CJK Compatibility Ideographs Supplement (2F8002FA1F)
// Kangxi Radicals (2F002FDF)
// CJK Radicals Supplement (2E802EFF)
// CJK Strokes (31C031EF)
// Ideographic Description Characters (2FF02FFF)
// Hangul Jamo (110011FF)
// Hangul Jamo Extended-A (A960A97F)
// Hangul Jamo Extended-B (D7B0D7FF)
// Hangul Compatibility Jamo (3130318F)
// Halfwidth and Fullwidth Forms (FF00FFEF)
// Hangul Syllables (AC00D7AF)
// Hiragana (3040309F)
// Kana Extended-A (1B1001B12F)
// Kana Supplement (1B0001B0FF)
// Kanbun (3190319F)
// Katakana (30A030FF)
// Katakana Phonetic Extensions (31F031FF)
// Lisu (A4D0A4FF)
// Miao (16F0016F9F)
// Nushu (1B1701B2FF)
// Tangut (17000187EC)
// Tangut Components (1880018AFF)
// Yi Syllables (A000A48F)
// Yi Radicals (A490A4CF)
return ((0x3100 <= value && value <= 0x312F)
|| (0x31A0 <= value && value <= 0x31BF)
|| (0x4E00 <= value && value <= 0x9FEA)
|| (0x3400 <= value && value <= 0x4DB5)
|| (0x20000 <= value && value <= 0x2A6D6)
|| (0x2A700 <= value && value <= 0x2B734)
|| (0x2B740 <= value && value <= 0x2B81D)
|| (0x2B820 <= value && value <= 0x2CEA1)
|| (0x2CEB0 <= value && value <= 0x2EBE0)
|| (0xF900 <= value && value <= 0xFAFF)
|| (0x2F800 <= value && value <= 0x2FA1F)
|| (0x2F00 <= value && value <= 0x2FDF)
|| (0x2E80 <= value && value <= 0x2EFF)
|| (0x31C0 <= value && value <= 0x31EF)
|| (0x2FF0 <= value && value <= 0x2FFF)
|| (0x1100 <= value && value <= 0x11FF)
|| (0xA960 <= value && value <= 0xA97F)
|| (0xD7B0 <= value && value <= 0xD7FF)
|| (0x3130 <= value && value <= 0x318F)
|| (0xFF00 <= value && value <= 0xFFEF)
|| (0xAC00 <= value && value <= 0xD7AF)
|| (0x3040 <= value && value <= 0x309F)
|| (0x1B100 <= value && value <= 0x1B12F)
|| (0x1B000 <= value && value <= 0x1B0FF)
|| (0x3190 <= value && value <= 0x319F)
|| (0x30A0 <= value && value <= 0x30FF)
|| (0x31F0 <= value && value <= 0x31FF)
|| (0xA4D0 <= value && value <= 0xA4FF)
|| (0x16F00 <= value && value <= 0x16F9F)
|| (0x1B170 <= value && value <= 0x1B2FF)
|| (0x17000 <= value && value <= 0x187EC)
|| (0x18800 <= value && value <= 0x18AFF)
|| (0xA000 <= value && value <= 0xA48F)
|| (0xA490 <= value && value <= 0xA4CF));
}
bool IsEastAsianName(const std::wstring& sName)
{
for (NSStringExt::CStringUnicodeIterator oIterator(sName); oIterator.Check(); oIterator.Next())
{
if (IsEastAsianScript(oIterator.Value()))
return true;
}
return false;
}
void CheckFontAlternateName(NSFonts::CFontInfo* info, std::map<std::wstring, std::wstring>& mapAlternate)
{
if (info->names.empty())
return;
if (!IsEastAsianName(info->m_wsFontName) && IsEastAsianName(info->names[0]))
{
mapAlternate.insert(std::pair<std::wstring, std::wstring>(info->m_wsFontName, info->names[0]));
}
}
std::wstring SaveWebFonts(const std::map<std::wstring, LONG>& mapFontFiles)
{
int nCountFiles = (int)mapFontFiles.size();
BYTE correct16[16] = {0xA0, 0x66, 0xD6, 0x20, 0x14, 0x96, 0x47, 0xfa, 0x95, 0x69, 0xB8, 0x50, 0xB0, 0x41, 0x49, 0x48};
int nCountIdSymbols = (nCountFiles >= 1000) ? 4 : 3;
BYTE encode[32];
std::wstring* pMassFiles2 = new std::wstring[nCountFiles];
int nCurrentId = 0;
for (std::map<std::wstring, LONG>::const_iterator pos = mapFontFiles.begin(); pos != mapFontFiles.end(); pos++)
{
std::wstring strFontId = pos->first;
std::wstring sId = std::to_wstring(nCurrentId++);
int nLenId = (int)sId.length();
while (nLenId < nCountIdSymbols)
{
sId = L"0" + sId;
++nLenId;
}
pMassFiles2[pos->second] = sId;
std::wstring sOutputFile = m_pMain->m_sWebFontsDirectory + L"/" + sId;
NSFile::CFileBinary::Copy(strFontId, sOutputFile);
NSFile::CFileBinary oFileDst;
if (oFileDst.OpenFile(sOutputFile, true))
{
DWORD dwRead = (DWORD)(oFileDst.GetFileSize());
if (dwRead > 32)
dwRead = 32;
DWORD dwWorked = 0;
oFileDst.SeekFile(0);
oFileDst.ReadFile(encode, dwRead, dwWorked);
for (DWORD k = 0; k < dwRead; ++k)
encode[k] ^= correct16[k & 0x0F];
oFileDst.SeekFile(0);
oFileDst.WriteFile(encode, dwRead);
oFileDst.CloseFile();
}
}
NSStringUtils::CStringBuilder oWriterJS;
oWriterJS.WriteString(L"window[\"__fonts_files\"] = [\n");
for (size_t nIndex = 0; nIndex < nCountFiles; ++nIndex)
{
oWriterJS.WriteString(L"\"");
oWriterJS.WriteString(pMassFiles2[nIndex]);
if (nIndex != (nCountFiles - 1))
oWriterJS.WriteString(L"\",\n");
else
oWriterJS.WriteString(L"\"");
}
oWriterJS.WriteString(L"\n];\n\n");
delete [] pMassFiles2;
return oWriterJS.GetData();
}
void SaveAllFontsJS(NSFonts::IApplicationFonts* applicationFonts, int nVersion = ONLYOFFICE_ALL_FONTS_VERSION)
{
if (CheckBreak()) return;
std::vector<NSFonts::CFontInfo*>* pList = applicationFonts->GetList()->GetFonts();
int nCount = (int)pList->size();
// сначала строим массив всех файлов шрифтов
std::map<std::wstring, LONG> mapFontFiles;
std::map<LONG, std::wstring> mapFontFiles2;
LONG lFontFiles = 0;
for (std::vector<NSFonts::CFontInfo*>::iterator i = pList->begin(); i != pList->end(); i++)
{
NSFonts::CFontInfo* pInfo = *i;
if (m_pMain->m_bIsGenerateThumbnailsEA)
CheckFontAlternateName(pInfo, m_mapAlternateEA);
if (mapFontFiles.find(pInfo->m_wsFontPath) == mapFontFiles.end())
{
mapFontFiles.insert(std::pair<std::wstring, LONG>(pInfo->m_wsFontPath, lFontFiles));
mapFontFiles2.insert(std::pair<LONG, std::wstring>(lFontFiles, pInfo->m_wsFontPath));
++lFontFiles;
}
}
// -----------------------------------------
if (CheckBreak()) return;
// теперь строим массив всех шрифтов по имени
std::map<std::wstring, CFontInfoJS> mapFonts;
std::vector<std::wstring> arrFonts;
for (std::vector<NSFonts::CFontInfo*>::iterator i = pList->begin(); i != pList->end(); i++)
{
NSFonts::CFontInfo* pInfo = *i;
std::wstring strPath = pInfo->m_wsFontPath;
std::wstring strName = pInfo->m_wsFontName;
int lFontIndex = 0;
int lFaceIndex = 0;
std::map<std::wstring, LONG>::iterator it = mapFontFiles.find(strPath);
lFontIndex = it->second;
if (pInfo->m_lIndex >= 0)
lFaceIndex = (int)pInfo->m_lIndex;
std::map<std::wstring, CFontInfoJS>::iterator pPair = mapFonts.find(pInfo->m_wsFontName);
if (mapFonts.end() != pPair)
{
pPair->second.m_sName = pInfo->m_wsFontName;
if (pInfo->m_bBold && pInfo->m_bItalic)
{
if (pPair->second.IsNeedUpdateBI())
{
pPair->second.m_lIndexBI = lFontIndex;
pPair->second.m_lFaceIndexBI = lFaceIndex;
pPair->second.m_sStyleBI = pInfo->m_wsStyle;
}
}
else if (pInfo->m_bBold)
{
if (pPair->second.IsNeedUpdateB())
{
pPair->second.m_lIndexB = lFontIndex;
pPair->second.m_lFaceIndexB = lFaceIndex;
pPair->second.m_sStyleB = pInfo->m_wsStyle;
}
}
else if (pInfo->m_bItalic)
{
if (pPair->second.IsNeedUpdateI())
{
pPair->second.m_lIndexI = lFontIndex;
pPair->second.m_lFaceIndexI = lFaceIndex;
pPair->second.m_sStyleI = pInfo->m_wsStyle;
}
}
else
{
if (pPair->second.IsNeedUpdateR())
{
pPair->second.m_lIndexR = lFontIndex;
pPair->second.m_lFaceIndexR = lFaceIndex;
pPair->second.m_sStyleR = pInfo->m_wsStyle;
}
}
}
else
{
CFontInfoJS fontInfo;
fontInfo.m_sName = pInfo->m_wsFontName;
if (pInfo->m_bBold && pInfo->m_bItalic)
{
fontInfo.m_lIndexBI = lFontIndex;
fontInfo.m_lFaceIndexBI = lFaceIndex;
fontInfo.m_sStyleBI = pInfo->m_wsStyle;
}
else if (pInfo->m_bBold)
{
fontInfo.m_lIndexB = lFontIndex;
fontInfo.m_lFaceIndexB = lFaceIndex;
fontInfo.m_sStyleB = pInfo->m_wsStyle;
}
else if (pInfo->m_bItalic)
{
fontInfo.m_lIndexI = lFontIndex;
fontInfo.m_lFaceIndexI = lFaceIndex;
fontInfo.m_sStyleI = pInfo->m_wsStyle;
}
else
{
fontInfo.m_lIndexR = lFontIndex;
fontInfo.m_lFaceIndexR = lFaceIndex;
fontInfo.m_sStyleR = pInfo->m_wsStyle;
}
mapFonts.insert(std::pair<std::wstring, CFontInfoJS>(fontInfo.m_sName, fontInfo));
arrFonts.push_back(fontInfo.m_sName);
}
}
// -------------------------------------------
if (CheckBreak()) return;
// теперь сортируем шрифты по имени ----------
size_t nCountFonts = arrFonts.size();
#if 1
std::sort(arrFonts.begin(), arrFonts.end(), g_sort_font_names);
#else
for (size_t i = 0; i < nCountFonts; ++i)
{
for (size_t j = i + 1; j < nCountFonts; ++j)
{
if (arrFonts[i] > arrFonts[j])
{
std::wstring temp = arrFonts[i];
arrFonts[i] = arrFonts[j];
arrFonts[j] = temp;
}
}
}
#endif
std::wstring strFontSelectionBin = L"";
// нужно ли скидывать font_selection.bin
if (ONLYOFFICE_ALL_FONTS_VERSION == nVersion && !m_bIsCheckThumbnailsMode)
{
strFontSelectionBin = m_pMain->m_sDirectory + L"/font_selection.bin";
}
NSMemoryUtils::CByteBuilder* pRangeBuilder = NULL;
int nRangeBuilderCount = 0;
if (!strFontSelectionBin.empty())
{
pRangeBuilder = new NSMemoryUtils::CByteBuilder();
pRangeBuilder->WriteInt(0);
}
// и самое главное. Здесь должен скидываться скрипт для работы со всеми шрифтами.
// все объекты, которые позволят не знать о существующих фонтах
std::wstring sAllFontsPath = m_pMain->m_sDirectory + L"/AllFonts.js";
if (!m_pMain->m_sAllFontsJSPath.empty())
sAllFontsPath = m_pMain->m_sAllFontsJSPath;
if (nVersion != ONLYOFFICE_ALL_FONTS_VERSION)
sAllFontsPath += (L"." + std::to_wstring((int)(nVersion + 1)));
if (m_bIsCheckThumbnailsMode)
sAllFontsPath = L"";
// AllFonts.js для веба точно такой же, но с другими путями
std::wstring sFontFilesWeb = L"";
size_t nFontFilesWeb1 = 0;
size_t nFontFilesWeb2 = 0;
if (!m_pMain->m_sWebFontsDirectory.empty() && !m_pMain->m_sWebAllFontsJSPath.empty() && !m_bIsCheckThumbnailsMode)
{
sFontFilesWeb = SaveWebFonts(mapFontFiles);
}
if (!sAllFontsPath.empty())
{
NSStringUtils::CStringBuilder oWriterJS;
int nAllFontsVersion = nVersion;
oWriterJS.WriteString(L"window[\"__all_fonts_js_version__\"] = ");
oWriterJS.AddInt(nAllFontsVersion);
oWriterJS.WriteString(L";\n\n");
nFontFilesWeb1 = oWriterJS.GetCurSize();
// сначала все файлы
size_t nCountFiles = mapFontFiles.size();
if (nCountFiles == 0)
oWriterJS.WriteString(L"window[\"__fonts_files\"] = []; \n\n");
else
{
std::wstring* pMassFiles = new std::wstring[nCountFiles];
for ( std::map<std::wstring, LONG>::iterator pos = mapFontFiles.begin(); pos != mapFontFiles.end(); ++pos)
{
std::wstring strFontId = pos->first;
NSStringUtils::string_replace(strFontId, L"\\\\", L"\\");
NSStringUtils::string_replace(strFontId, L"\\", L"/");
pMassFiles[pos->second] = strFontId;
}
oWriterJS.WriteString(L"window[\"__fonts_files\"] = [\n");
for (size_t nIndex = 0; nIndex < nCountFiles; ++nIndex)
{
oWriterJS.WriteString(L"\"");
oWriterJS.WriteString(pMassFiles[nIndex]);
if (nIndex != (nCountFiles - 1))
oWriterJS.WriteString(L"\",\n");
else
oWriterJS.WriteString(L"\"");
}
oWriterJS.WriteString(L"\n];\n\n");
nFontFilesWeb2 = oWriterJS.GetCurSize();
delete [] pMassFiles;
}
oWriterJS += L"window[\"__fonts_infos\"] = [\n";
std::map<std::wstring, int> mapFontIndexes;
for (int index = 0; index < nCountFonts; ++index)
{
std::map<std::wstring, CFontInfoJS>::iterator pPair = mapFonts.find(arrFonts[index]);
mapFontIndexes.insert(std::pair<std::wstring, int>(arrFonts[index], index));
oWriterJS += L"[\"";
std::wstring sNameCorrect = pPair->second.m_sName;
NSStringUtils::string_replace(sNameCorrect, L"\\", L"\\\\");
NSStringUtils::string_replace(sNameCorrect, L"\"", L"\\\"");
NSStringUtils::string_replace(sNameCorrect, L"\n", L"");
NSStringUtils::string_replace(sNameCorrect, L"\r", L"");
oWriterJS += sNameCorrect;
oWriterJS.AddSize(120);
oWriterJS.AddCharNoCheck('\"');
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lIndexR);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lFaceIndexR);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lIndexI);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lFaceIndexI);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lIndexB);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lFaceIndexB);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lIndexBI);
oWriterJS.AddCharNoCheck(',');
oWriterJS.AddIntNoCheck(pPair->second.m_lFaceIndexBI);
if (index != (nCountFonts - 1))
oWriterJS += (L"],\n");
else
oWriterJS += (L"]\n");
}
oWriterJS += (L"];\n\n");
if (true)
{
//DWORD dwTime = NSTimers::GetTickCount();
int nMaxSymbol = 0x10FFFF;
int* arSymbolsAll = new int[nMaxSymbol + 1];
memset(arSymbolsAll, 0, (nMaxSymbol + 1) * sizeof(int));
CSymbolSimpleChecker2 oAllChecker(arSymbolsAll, nMaxSymbol);
std::map<std::wstring, int> mapFontsPriorityStandard;
int nCurrentPriority = 1;
#define SET_FONT_PRIORITY(fontName) mapFontsPriorityStandard.insert(std::pair<std::wstring, int>(fontName, nCurrentPriority++))
SET_FONT_PRIORITY(L"ASCW3");
SET_FONT_PRIORITY(L"Arial");
SET_FONT_PRIORITY(L"Times New Roman");
SET_FONT_PRIORITY(L"Tahoma");
SET_FONT_PRIORITY(L"Cambria");
SET_FONT_PRIORITY(L"Calibri");
SET_FONT_PRIORITY(L"Verdana");
SET_FONT_PRIORITY(L"Georgia");
SET_FONT_PRIORITY(L"Open Sans");
SET_FONT_PRIORITY(L"Liberation Sans");
SET_FONT_PRIORITY(L"Helvetica");
SET_FONT_PRIORITY(L"Nimbus Sans L");
SET_FONT_PRIORITY(L"DejaVu Sans");
SET_FONT_PRIORITY(L"Liberation Serif");
SET_FONT_PRIORITY(L"Trebuchet MS");
SET_FONT_PRIORITY(L"Courier New");
SET_FONT_PRIORITY(L"Carlito");
SET_FONT_PRIORITY(L"Segoe UI");
SET_FONT_PRIORITY(L"SimSun");
SET_FONT_PRIORITY(L"MS Gothic");
SET_FONT_PRIORITY(L"Nirmala UI");
SET_FONT_PRIORITY(L"Batang");
SET_FONT_PRIORITY(L"MS Mincho");
SET_FONT_PRIORITY(L"Wingdings");
SET_FONT_PRIORITY(L"Microsoft JhengHei");
SET_FONT_PRIORITY(L"Microsoft JhengHei UI");
SET_FONT_PRIORITY(L"Microsoft YaHei");
SET_FONT_PRIORITY(L"PMingLiU");
SET_FONT_PRIORITY(L"MingLiU");
SET_FONT_PRIORITY(L"DFKai-SB");
SET_FONT_PRIORITY(L"FangSong");
SET_FONT_PRIORITY(L"KaiTi");
SET_FONT_PRIORITY(L"SimKai");
SET_FONT_PRIORITY(L"SimHei");
SET_FONT_PRIORITY(L"Meiryo");
#ifdef _MAC
SET_FONT_PRIORITY(L"PingFang SC");
SET_FONT_PRIORITY(L"PingFang TC");
SET_FONT_PRIORITY(L"PingFang HK");
SET_FONT_PRIORITY(L"Heiti SC");
SET_FONT_PRIORITY(L"Heiti TC");
SET_FONT_PRIORITY(L"Songti SC");
SET_FONT_PRIORITY(L"Songti TC");
#endif
SET_FONT_PRIORITY(L"Malgun Gothic");
SET_FONT_PRIORITY(L"Nanum Gothic");
SET_FONT_PRIORITY(L"NanumGothic");
SET_FONT_PRIORITY(L"Noto Sans KR");
SET_FONT_PRIORITY(L"TakaoGothic");
NSFonts::CApplicationFontsSymbols oApplicationChecker;
// приоритеты шрифтов. по имени (все стили)
// если шрифт из mapFontsPriorityStandard, то приоритет из этого map
// иначе - максимальный размер из файлов-стиля шрифта (max(regular,italic,bold,bolditalic))
std::vector<CFontPriority> arrFontsPriority;
for (int index = 0; index < nCountFonts; ++index)
{
std::map<std::wstring, CFontInfoJS>::iterator pPair = mapFonts.find(arrFonts[index]);
CFontInfoJS& info = pPair->second;
std::map<std::wstring, int>::iterator find = mapFontsPriorityStandard.find(arrFonts[index]);
if (find != mapFontsPriorityStandard.end())
{
CFontPriority f;
f.name = arrFonts[index];
f.priority = find->second;
arrFontsPriority.push_back(f);
continue;
}
int nSize = 0;
if (-1 != info.m_lIndexR)
{
NSFile::CFileBinary oFile;
if (oFile.OpenFile(mapFontFiles2[info.m_lIndexR]))
{
int nTmp = oFile.GetFileSize();
if (nTmp > nSize)
nSize = nTmp;
}
}
if (-1 != info.m_lIndexB)
{
NSFile::CFileBinary oFile;
if (oFile.OpenFile(mapFontFiles2[info.m_lIndexB]))
{
int nTmp = oFile.GetFileSize();
if (nTmp > nSize)
nSize = nTmp;
}
}
if (-1 != info.m_lIndexI)
{
NSFile::CFileBinary oFile;
if (oFile.OpenFile(mapFontFiles2[info.m_lIndexI]))
{
int nTmp = oFile.GetFileSize();
if (nTmp > nSize)
nSize = nTmp;
}
}
if (-1 != info.m_lIndexBI)
{
NSFile::CFileBinary oFile;
if (oFile.OpenFile(mapFontFiles2[info.m_lIndexBI]))
{
int nTmp = oFile.GetFileSize();
if (nTmp > nSize)
nSize = nTmp;
}
}
CFontPriority f;
f.name = arrFonts[index];
f.priority = nSize;
arrFontsPriority.push_back(f);
}
// сортируем по приоритету
std::sort(arrFontsPriority.begin(), arrFontsPriority.end(), CFontPriority::Compare);
// для удобства - делаем map
std::map<std::wstring, int> mapFontsPriority;
int nIndexPriority = 1;
for (std::vector<CFontPriority>::iterator i = arrFontsPriority.begin(); i != arrFontsPriority.end(); i++)
{
CFontPriority& o = *i;
mapFontsPriority.insert(std::pair<std::wstring, int>(o.name, nIndexPriority));
nIndexPriority++;
}
int nSumPriority = (int)(arrFontsPriority.size() + 1);
bool bIsSmallRangesDetect = true;
for (int index = 0; index < nCountFonts; ++index)
{
std::map<std::wstring, CFontInfoJS>::iterator pPair = mapFonts.find(arrFonts[index]);
CFontInfoJS& info = pPair->second;
int nPriority = mapFontsPriority.find(arrFonts[index])->second;
oAllChecker.m_nPriority = nPriority;
int nCounterFonts = 0;
if (-1 != info.m_lIndexR)
nCounterFonts++;
if (-1 != info.m_lIndexB)
nCounterFonts++;
if (-1 != info.m_lIndexI)
nCounterFonts++;
if (-1 != info.m_lIndexBI)
nCounterFonts++;
if (1 == nCounterFonts && !bIsSmallRangesDetect)
{
// отключили этот режим (bIsSmallRangesDetect всегда true)
std::wstring sPathC = L"";
int nFaceIndexC = 0;
if (-1 != info.m_lIndexR)
{
sPathC = mapFontFiles2[info.m_lIndexR];
nFaceIndexC = info.m_lFaceIndexR;
}
else if (-1 != info.m_lIndexB)
{
sPathC = mapFontFiles2[info.m_lIndexB];
nFaceIndexC = info.m_lFaceIndexB;
}
else if (-1 != info.m_lIndexI)
{
sPathC = mapFontFiles2[info.m_lIndexI];
nFaceIndexC = info.m_lFaceIndexI;
}
else if (-1 != info.m_lIndexBI)
{
sPathC = mapFontFiles2[info.m_lIndexBI];
nFaceIndexC = info.m_lFaceIndexBI;
}
CSymbolSimpleChecker checker;
checker.m_nMaxSymbols = nMaxSymbol;
checker.m_pSymbols = arSymbolsAll;
checker.m_nPriority = nPriority;
oApplicationChecker.CheckSymbols(sPathC, nFaceIndexC, &checker);
}
else
{
// каждый шрифт - анализируем на символы. в массив символов (tmp buffer) -
// пишем m_nStyle
int nMask = 0;
if (-1 != info.m_lIndexR)
{
oAllChecker.m_nStyle = 1;
nMask |= 1;
oApplicationChecker.CheckSymbols(mapFontFiles2[info.m_lIndexR], info.m_lFaceIndexR, &oAllChecker);
}
if (-1 != info.m_lIndexB)
{
oAllChecker.m_nStyle = 2;
nMask |= 2;
oApplicationChecker.CheckSymbols(mapFontFiles2[info.m_lIndexB], info.m_lFaceIndexB, &oAllChecker);
}
if (-1 != info.m_lIndexI)
{
oAllChecker.m_nStyle = 4;
nMask |= 4;
oApplicationChecker.CheckSymbols(mapFontFiles2[info.m_lIndexI], info.m_lFaceIndexI, &oAllChecker);
}
if (-1 != info.m_lIndexBI)
{
oAllChecker.m_nStyle = 8;
nMask |= 8;
oApplicationChecker.CheckSymbols(mapFontFiles2[info.m_lIndexBI], info.m_lFaceIndexBI, &oAllChecker);
}
if (bIsSmallRangesDetect)
{
// чекаем все символы, которые есть во ВСЕХ стилях шрифта
// nSumPriority - это добавка для ranges маленькой длины, чтобы если есть
// длинный диапазон но большим приоритетом - то он его перебьет.
// для этого добавка - количество шрифтов. чтобы хватило
oAllChecker.Apply2(nMask, nSumPriority);
}
else
{
// просто чекаем все символы, которые есть во ВСЕХ стилях шрифта
oAllChecker.Apply1(nMask);
}
}
}
//dwTime = NSTimers::GetTickCount() - dwTime;
oWriterJS += L"window[\"__fonts_ranges\"] = [\n";
int nFontPriority = 0;
int nFontPriorityStart = 0;
int nTopBorder = nMaxSymbol + 1;
for (int i = 0; i < nTopBorder; ++i)
{
int nFontPriorityTestSym = arSymbolsAll[i];
int nFontPriorityTest = (nFontPriorityTestSym > nSumPriority) ? (nFontPriorityTestSym - nSumPriority) : nFontPriorityTestSym;
if (nFontPriority == nFontPriorityTest)
continue;
if (nFontPriority != 0)
{
++nRangeBuilderCount;
oWriterJS.AddInt(nFontPriorityStart);
oWriterJS.AddCharSafe(',');
oWriterJS.AddInt(i - 1);
oWriterJS.AddCharSafe(',');
oWriterJS.AddInt(mapFontIndexes.find(arrFontsPriority[nFontPriority - 1].name)->second);
oWriterJS.AddCharSafe(',');
if (pRangeBuilder)
{
pRangeBuilder->WriteStringUTF8(arrFontsPriority[nFontPriority - 1].name);
pRangeBuilder->WriteInt(nFontPriorityStart);
pRangeBuilder->WriteInt(i - 1);
}
}
nFontPriority = nFontPriorityTest;
nFontPriorityStart = i;
}
if (nFontPriority != 0)
{
++nRangeBuilderCount;
oWriterJS.AddInt(nFontPriorityStart);
oWriterJS.AddCharSafe(',');
oWriterJS.AddInt(nMaxSymbol - 1);
oWriterJS.AddCharSafe(',');
oWriterJS.AddInt(mapFontIndexes.find(arrFontsPriority[nFontPriority - 1].name)->second);
oWriterJS.AddCharSafe(',');
if (pRangeBuilder)
{
pRangeBuilder->WriteStringUTF8(arrFontsPriority[nFontPriority - 1].name);
pRangeBuilder->WriteInt(nFontPriorityStart);
pRangeBuilder->WriteInt(nMaxSymbol - 1);
}
}
oWriterJS.SetCurSize(oWriterJS.GetCurSize() - 1);
oWriterJS += (L"];\n\n");
delete[] arSymbolsAll;
}
if (true)
{
BYTE* pData = NULL;
LONG lLen = 0;
NSFonts::CFontListToBufferSerializer oSerializer(L"", false, nVersion);
applicationFonts->GetList()->ToBuffer(&pData, &lLen, oSerializer);
char* cData64 = NULL;
int nData64Dst = 0;
NSFile::CBase64Converter::Encode(pData, (int)lLen, cData64, nData64Dst, NSBase64::B64_BASE64_FLAG_NOCRLF);
std::wstring sData64 = NSFile::CUtf8Converter::GetUnicodeFromCharPtr(cData64, (LONG)nData64Dst, FALSE);
oWriterJS.WriteString(L"window[\"g_fonts_selection_bin\"] = \"");
oWriterJS.WriteString(sData64);
oWriterJS.WriteString(L"\";\n");
RELEASEARRAYOBJECTS(pData);
RELEASEARRAYOBJECTS(cData64);
}
NSFile::CFileBinary oFile;
oFile.CreateFileW(sAllFontsPath);
oFile.WriteStringUTF8(oWriterJS.GetData(), true);
oFile.CloseFile();
if (!sFontFilesWeb.empty())
{
NSFile::CFileBinary oFile;
oFile.CreateFileW(m_pMain->m_sWebAllFontsJSPath);
std::wstring sWebAllFonts = oWriterJS.GetSubData(0, nFontFilesWeb1) + sFontFilesWeb + oWriterJS.GetSubData(nFontFilesWeb2);
oFile.WriteStringUTF8(sWebAllFonts, true);
oFile.CloseFile();
}
}
if (!strFontSelectionBin.empty())
{
BYTE* pData = NULL;
LONG lLen = 0;
NSFonts::CFontListToBufferSerializer oSerializer(L"", false, ONLYOFFICE_ALL_FONTS_VERSION);
applicationFonts->GetList()->ToBuffer(&pData, &lLen, oSerializer);
NSFile::CFileBinary oFile;
oFile.CreateFileW(strFontSelectionBin);
oFile.WriteFile(pData, (DWORD)lLen);
if (pRangeBuilder)
{
size_t nPosCur = pRangeBuilder->GetCurSize();
pRangeBuilder->SetCurSize(0);
pRangeBuilder->WriteInt(nRangeBuilderCount);
pRangeBuilder->SetCurSize(nPosCur);
oFile.WriteFile(pRangeBuilder->GetData(), (DWORD)pRangeBuilder->GetCurSize());
// init ranges
applicationFonts->InitializeRanges(pRangeBuilder->GetData());
}
oFile.CloseFile();
RELEASEARRAYOBJECTS(pData);
}
RELEASEOBJECT(pRangeBuilder);
if (CheckBreak()) return;
if (m_pMain->m_bIsNeedThumbnails)
{
m_arFonts.clear();
m_arFonts.resize(nCountFonts);
for (int index = 0; index < nCountFonts; ++index)
{
std::map<std::wstring, CFontInfoJS>::iterator pPair = mapFonts.find(arrFonts[index]);
// thumbnail
int lFontIndex = 0;
int lFaceIndex = 0;
if (pPair->second.m_lIndexR != -1)
{
lFontIndex = pPair->second.m_lIndexR;
lFaceIndex = pPair->second.m_lFaceIndexR;
}
else if (pPair->second.m_lIndexI != -1)
{
lFontIndex = pPair->second.m_lIndexI;
lFaceIndex = pPair->second.m_lFaceIndexI;
}
else if (pPair->second.m_lIndexB != -1)
{
lFontIndex = pPair->second.m_lIndexB;
lFaceIndex = pPair->second.m_lFaceIndexB;
}
else if (pPair->second.m_lIndexBI != -1)
{
lFontIndex = pPair->second.m_lIndexBI;
lFaceIndex = pPair->second.m_lFaceIndexBI;
}
std::map<LONG, std::wstring>::iterator pPair2 = mapFontFiles2.find(lFontIndex);
std::wstring strFontPath = L"";
if (mapFontFiles2.end() != pPair2)
strFontPath = pPair2->second;
m_arFonts[index].m_sFile = strFontPath;
m_arFonts[index].m_sName = pPair->second.m_sName;
m_arFonts[index].m_lFaceIndex = lFaceIndex;
}
}
if (m_pMain->m_bSeparateThumbnails)
return;
if (m_pMain->m_bIsNeedThumbnails)
{
SaveThumbnails(applicationFonts);
}
}
void PrepareForThumbnails(NSFonts::IApplicationFonts* applicationFonts)
{
if (m_arFonts.empty())
{
m_bIsCheckThumbnailsMode = true;
SaveAllFontsJS(applicationFonts, -1);
m_bIsCheckThumbnailsMode = false;
}
}
void SaveThumbnails(NSFonts::IApplicationFonts* applicationFonts)
{
std::vector<std::wstring> arrFiles;
std::vector<double> arrScales;
std::vector<bool> arrEA;
bool bIsNeedCheck = false;
int nCountScales = m_pMain->m_arThumbnailsScales.size();
bool bIsEA = false;
while (true)
{
for (int iX = 0; iX < nCountScales; ++iX)
{
if (CheckBreak()) return;
double dScale = m_pMain->m_arThumbnailsScales[iX];
std::wstring strThumbnailPath = (m_pMain->m_sThumbnailsDirectory.empty() ?
m_pMain->m_sDirectory : m_pMain->m_sThumbnailsDirectory) + L"/fonts_thumbnail";
if (bIsEA)
strThumbnailPath += L"_ea";
int nScaleOut = (int)(dScale * 100 + 0.5);
if (nScaleOut == 100)
strThumbnailPath += L".png";
else if ((nScaleOut % 100) == 0)
strThumbnailPath += L"@" + std::to_wstring((int)(nScaleOut / 100)) + L"x.png";
else if ((nScaleOut % 10) == 0)
strThumbnailPath += L"@" + std::to_wstring((int)(nScaleOut / 100)) + L"." + std::to_wstring((int)((nScaleOut / 10) % 10)) + L"x.png";
else
strThumbnailPath += L"@" + std::to_wstring((int)(nScaleOut / 100)) + L"." + std::to_wstring((int)(nScaleOut % 100)) + L"x.png";
arrScales.push_back(dScale);
arrFiles.push_back(strThumbnailPath);
arrEA.push_back(bIsEA);
bool bIsFileExist = NSFile::CFileBinary::Exists(strThumbnailPath);
if (bIsFileExist && m_pMain->m_bIsRemoveOldThumbnails)
{
NSFile::CFileBinary::Remove(strThumbnailPath);
bIsFileExist = false;
}
if (!bIsFileExist)
bIsNeedCheck = true;
}
if (bIsEA || !m_pMain->m_bIsGenerateThumbnailsEA)
break;
bIsEA = true;
}
if (!bIsNeedCheck)
return;
NSFonts::IApplicationFonts* applicationFontsGood = applicationFonts;
if (NULL == applicationFontsGood)
{
applicationFontsGood = NSFonts::NSApplication::Create();
applicationFontsGood->InitializeFromFolder(m_pMain->m_sDirectory);
}
NSFonts::IFontManager* pManager = applicationFontsGood->GenerateFontManager();
NSFonts::IFontsCache* pCache = NSFonts::NSFontCache::Create();
pCache->SetStreams(applicationFontsGood->GetStreams());
pManager->SetOwnerCache(pCache);
PrepareForThumbnails(applicationFontsGood);
int nCountFonts = (int)m_arFonts.size();
nCountScales = arrScales.size();
for (int iX = 0; iX < nCountScales; ++iX)
{
if (CheckBreak()) return;
double dScale = arrScales[iX];
std::wstring strThumbnailPath = arrFiles[iX];
if (NSFile::CFileBinary::Exists(strThumbnailPath))
continue;
// создаем картинку для табнейлов
double dDpi = 96 * dScale;
LONG lH1_px = (LONG)(28 * dScale);
LONG lWidthPix = (LONG)(300 * dScale);
LONG lHeightPix = (LONG)(nCountFonts * lH1_px);
double dW_mm = 25.4 * lWidthPix / dDpi;
double dH1_mm = 25.4 * lH1_px / dDpi;
LONG lCountPixels = 4 * lWidthPix * lHeightPix;
BYTE* pImageData = new BYTE[lCountPixels];
memset(pImageData, 0xFF, lCountPixels);
CBgraFrame oFrame;
oFrame.put_Data(pImageData);
oFrame.put_Width((int)lWidthPix);
oFrame.put_Height((int)lHeightPix);
oFrame.put_Stride(4 * (int)lWidthPix);
for (LONG i = 3; i < lWidthPix * lHeightPix * 4; i += 4)
{
pImageData[i] = 0;
}
NSGraphics::IGraphicsRenderer* pRenderer = NSGraphics::Create();
pRenderer->CreateFromBgraFrame(&oFrame);
pRenderer->SetFontManager(pManager);
pRenderer->put_Width(lWidthPix * 25.4 / dDpi);
pRenderer->put_Height(lHeightPix * 25.4 / dDpi);
bIsEA = arrEA[iX];
for (int index = 0; index < nCountFonts; ++index)
{
if (CheckBreak()) return;
CFontThumbnail& font = m_arFonts[index];
pRenderer->put_FontPath(font.m_sFile);
pRenderer->put_FontFaceIndex(font.m_lFaceIndex);
pManager->LoadFontFromFile(font.m_sFile, font.m_lFaceIndex, 14, dDpi, dDpi);
bool bIsSymbol = false;
NSFonts::IFontFile* pFile = pManager->GetFile();
if (pFile)
bIsSymbol = pFile->IsSymbolic(false);
std::wstring sFontName = font.m_sName;
if (bIsEA)
{
std::map<std::wstring, std::wstring>::const_iterator iter = m_mapAlternateEA.find(sFontName);
if (iter != m_mapAlternateEA.end())
{
sFontName = iter->second;
}
}
if (bIsSymbol)
{
NSFonts::CFontSelectFormat oSelectFormat;
oSelectFormat.wsName = new std::wstring(L"Courier New");
NSFonts::CFontInfo* pInfoCur = pManager->GetFontInfoByParams(oSelectFormat);
if (NULL != pInfoCur)
{
pManager->LoadFontFromFile(pInfoCur->m_wsFontPath, 0, 14, dDpi, dDpi);
}
pRenderer->put_FontPath(pInfoCur->m_wsFontPath);
pRenderer->put_FontFaceIndex(0);
}
else if (pFile)
{
// у нас режим "без квадратов"
// но есть шрифты, в которых символы есть, но нулевой ширины.
// только из-за таких шрифтов делаем заглушку
bool bIsExistEmpty = false;
for (NSStringExt::CStringUnicodeIterator oIterator(sFontName); oIterator.Check(); oIterator.Next())
{
int nCMapIndex = 0;
int nGid = pFile->SetCMapForCharCode(oIterator.Value(), &nCMapIndex);
if (0 < nGid && 0.0001 > pFile->GetCharWidth(nGid))
{
bIsExistEmpty = true;
break;
}
}
if (bIsExistEmpty)
{
NSFonts::CFontSelectFormat oSelectFormat;
oSelectFormat.wsName = new std::wstring(L"Arial");
NSFonts::CFontInfo* pInfoCur = pManager->GetFontInfoByParams(oSelectFormat);
if (NULL != pInfoCur)
{
pManager->LoadFontFromFile(pInfoCur->m_wsFontPath, 0, 14, dDpi, dDpi);
}
pRenderer->put_FontPath(pInfoCur->m_wsFontPath);
pRenderer->put_FontFaceIndex(0);
}
}
pRenderer->PathCommandStart();
pRenderer->BeginCommand(c_nClipType);
pRenderer->PathCommandRect(0, 25.4 * (index * lH1_px) / dDpi, dW_mm, 25.4 * lH1_px / dDpi);
pRenderer->EndCommand(c_nClipType);
pRenderer->PathCommandEnd();
pRenderer->put_FontStringGID(FALSE);
pRenderer->put_FontCharSpace(0);
pRenderer->put_FontSize(14);
pRenderer->CommandDrawText(sFontName, 5, 25.4 * (index * lH1_px + lH1_px) / dDpi - 2, 0, 0);
pRenderer->BeginCommand(c_nResetClipType);
pRenderer->EndCommand(c_nResetClipType);
pRenderer->CloseFont();
pCache->Clear();
applicationFontsGood->GetStreams()->Clear();
}
RELEASEOBJECT(pRenderer);
oFrame.SaveFile(strThumbnailPath, 4);
LONG lBinarySize = lWidthPix * lHeightPix;
BYTE* pBinaryFormat = new BYTE[lBinarySize + 12];
pBinaryFormat[0] = 0xFF & (lWidthPix >> 24);
pBinaryFormat[1] = 0xFF & (lWidthPix >> 16);
pBinaryFormat[2] = 0xFF & (lWidthPix >> 8);
pBinaryFormat[3] = 0xFF & (lWidthPix >> 0);
pBinaryFormat[4] = 0xFF & (lH1_px >> 24);
pBinaryFormat[5] = 0xFF & (lH1_px >> 16);
pBinaryFormat[6] = 0xFF & (lH1_px >> 8);
pBinaryFormat[7] = 0xFF & (lH1_px >> 0);
pBinaryFormat[8] = 0xFF & (nCountFonts >> 24);
pBinaryFormat[9] = 0xFF & (nCountFonts >> 16);
pBinaryFormat[10] = 0xFF & (nCountFonts >> 8);
pBinaryFormat[11] = 0xFF & (nCountFonts >> 0);
BYTE* pBinaryFormatCur = pBinaryFormat + 12;
BYTE* pImageDataAlpha = pImageData + 3;
LONG nIndexCur = 0;
while (nIndexCur < lBinarySize)
{
*pBinaryFormatCur++ = *pImageDataAlpha;
if (0 != *pImageDataAlpha)
{
++nIndexCur;
pImageDataAlpha += 4;
}
else
{
LONG nIndexCur0 = nIndexCur;
LONG nIndexCurLimit = nIndexCur0 + 255;
if (nIndexCurLimit > lBinarySize)
nIndexCurLimit = lBinarySize;
while (nIndexCur < nIndexCurLimit)
{
++nIndexCur;
pImageDataAlpha += 4;
if (0 != *pImageDataAlpha)
break;
}
*pBinaryFormatCur++ = nIndexCur - nIndexCur0;
}
}
#if 0
BYTE* pPixelsValues = new BYTE[4 * lWidthPix * lHeightPix];
memset(pPixelsValues, 0x00, 4 * lWidthPix * lHeightPix);
CBgraFrame oFrameTest;
oFrameTest.put_Data(pPixelsValues);
oFrameTest.put_Width((int)lWidthPix);
oFrameTest.put_Height((int)lHeightPix);
oFrameTest.put_Stride(4 * (int)lWidthPix);
BYTE* binaryAlpha = pBinaryFormat;
LONG binaryIndex = 12;
LONG binaryLen = (LONG)(pBinaryFormatCur - pBinaryFormat);
BYTE* imagePixels = pPixelsValues;
LONG alphaIndex = 3;
int len0 = 0;
while (binaryIndex < binaryLen)
{
imagePixels[alphaIndex] = binaryAlpha[binaryIndex];
binaryIndex++;
alphaIndex += 4;
if (0 == binaryAlpha[binaryIndex - 1])
{
len0 = binaryAlpha[binaryIndex++] - 1;
while (len0 > 0)
{
len0--;
imagePixels[alphaIndex] = 0;
alphaIndex += 4;
}
}
else
{
imagePixels[alphaIndex - 5] = imagePixels[alphaIndex - 6] = imagePixels[alphaIndex - 7] = 0xFF - binaryAlpha[binaryIndex - 1];
}
}
oFrameTest.SaveFile(L"FILE", 4);
#endif
NSFile::CFileBinary oThumbnailBinary;
if (oThumbnailBinary.CreateFileW(strThumbnailPath + L".bin"))
{
oThumbnailBinary.WriteFile(pBinaryFormat, (DWORD)(pBinaryFormatCur - pBinaryFormat));
oThumbnailBinary.CloseFile();
}
RELEASEARRAYOBJECTS(pBinaryFormat);
}
RELEASEOBJECT(pManager);
if (applicationFonts == NULL)
RELEASEOBJECT(applicationFontsGood);
}
};
CApplicationFontsWorker::CApplicationFontsWorker()
{
m_bIsUseSystemFonts = true;
m_bIsUseSystemUserFonts = true;
m_bIsNeedThumbnails = true;
m_bIsUseOpenType = true;
m_bIsUseAllVersions = false;
m_bSeparateThumbnails = false;
m_bIsGenerateThumbnailsEA = true;
m_bIsCleanDirectory = true;
m_bIsRemoveOldThumbnails = false;
m_arThumbnailsScales.clear();
m_arThumbnailsScales.push_back(1);
m_arThumbnailsScales.push_back(1.25);
m_arThumbnailsScales.push_back(1.5);
m_arThumbnailsScales.push_back(1.75);
m_arThumbnailsScales.push_back(2);
m_pInternal = new CApplicationFontsWorker_private(this);
}
CApplicationFontsWorker::~CApplicationFontsWorker()
{
RELEASEOBJECT(m_pInternal);
}
NSFonts::IApplicationFonts* CApplicationFontsWorker::Check()
{
if (m_sDirectory.empty())
return NULL;
for (std::vector<std::wstring>::iterator i = m_arAdditionalFolders.begin(); i != m_arAdditionalFolders.end(); i++)
{
if (0 == i->find(L"./"))
*i = NSFile::GetProcessDirectory() + L"/" + i->substr(2);
else if (0 == i->find(L"../"))
*i = NSFile::GetProcessDirectory() + L"/" + *i;
}
std::wstring strAllFontsJSPath = m_sDirectory + L"/AllFonts.js";
std::wstring strFontsSelectionBin = m_sDirectory + L"/font_selection.bin";
std::vector<std::string> strFonts;
std::wstring strFontsCheckPath = m_sDirectory + L"/fonts.log";
// читаем "старый" набор шрифтов
if (true)
{
NSFile::CFileBinary oFile;
if (oFile.OpenFile(strFontsCheckPath))
{
int nSize = oFile.GetFileSize();
char* pBuffer = new char[nSize];
DWORD dwReaden = 0;
oFile.ReadFile((BYTE*)pBuffer, nSize, dwReaden);
oFile.CloseFile();
int nStart = 0;
int nCur = nStart;
for (; nCur < nSize; ++nCur)
{
if (pBuffer[nCur] == '\n')
{
int nEnd = nCur - 1;
if (nEnd > nStart)
{
std::string s(pBuffer + nStart, nEnd - nStart + 1);
strFonts.push_back(s);
}
nStart = nCur + 1;
}
}
delete[] pBuffer;
}
#ifdef ONLYOFFICE_FONTS_VERSION
if (0 != strFonts.size())
{
// check version!!!
std::string sOO_Version = strFonts[0];
if (0 != sOO_Version.find("ONLYOFFICE_FONTS_VERSION_"))
{
strFonts.clear();
}
else
{
std::string sVersion = sOO_Version.substr(25);
int nVersion = std::stoi(sVersion);
if (nVersion != ONLYOFFICE_FONTS_VERSION)
strFonts.clear();
else
strFonts.erase(strFonts.begin());
}
}
#endif
}
// читаем "новый" набор шрифтов
NSFonts::IApplicationFonts* pApplicationF = NSFonts::NSApplication::Create();
std::vector<std::wstring> strFontsW_CurSrc;
std::vector<std::wstring> strFontsW_Cur;
if (m_bIsUseSystemFonts)
strFontsW_CurSrc = pApplicationF->GetSetupFontFiles(m_bIsUseSystemUserFonts);
for (std::vector<std::wstring>::iterator i = m_arAdditionalFolders.begin(); i != m_arAdditionalFolders.end(); i++)
{
NSDirectory::GetFiles2(*i, strFontsW_CurSrc, true);
}
// удаляем папки, которые не нужно парсить
strFontsW_Cur.reserve(strFontsW_CurSrc.size());
for (std::vector<std::wstring>::iterator i = strFontsW_CurSrc.begin(); i != strFontsW_CurSrc.end(); i++)
{
#ifdef _WIN32
if (i->find(L"\\.git\\") == std::wstring::npos)
strFontsW_Cur.push_back(*i);
//if (i->find(L"\\.svn\\") == std::wstring::npos)
// strFontsW_Cur.push_back(*i);
#else
if (i->find(L"/.git/") == std::wstring::npos)
strFontsW_Cur.push_back(*i);
//if (i->find(L"/.svn/") == std::wstring::npos)
// strFontsW_Cur.push_back(*i);
#endif
}
// сортируем (нужно для сравнения для старого набора)
std::sort(strFontsW_Cur.begin(), strFontsW_Cur.end());
bool bIsEqual = true;
// если количество шрифтов в наборах не совпадают - то нужно перегенерировать
if (strFonts.size() != strFontsW_Cur.size())
bIsEqual = false;
if (bIsEqual)
{
// если наборы не совпадают - то нужно перегенерировать
int nCount = (int)strFonts.size();
for (int i = 0; i < nCount; ++i)
{
if (strFonts[i] != NSFile::CUtf8Converter::GetUtf8StringFromUnicode2(strFontsW_Cur[i].c_str(), (LONG)strFontsW_Cur[i].length()))
{
bIsEqual = false;
break;
}
}
}
if (bIsEqual)
{
// наборы совпадают - скинут ли font_selection.bin
// если нет - нужно перегенерировать
if (!NSFile::CFileBinary::Exists(strFontsSelectionBin))
bIsEqual = false;
}
if (!bIsEqual)
{
if (m_bIsCleanDirectory)
{
std::vector<std::wstring> arFiles = NSDirectory::GetFiles(m_sDirectory, false);
for (std::vector<std::wstring>::iterator fileIter = arFiles.begin(); fileIter != arFiles.end(); fileIter++)
{
NSFile::CFileBinary::Remove(*fileIter);
}
}
int nFlag = 3;
if (!m_bIsUseOpenType)
nFlag = 2;
// формируем новый набор шрифтов
NSStringUtils::CStringBuilder oFontsLog;
#ifdef ONLYOFFICE_FONTS_VERSION
oFontsLog.WriteString(L"ONLYOFFICE_FONTS_VERSION_");
oFontsLog.WriteString(std::to_wstring(ONLYOFFICE_FONTS_VERSION));
oFontsLog.WriteString(L"\n");
#endif
int nCount = (int)strFontsW_Cur.size();
for (int i = 0; i < nCount; ++i)
{
oFontsLog.WriteString(strFontsW_Cur[i]);
oFontsLog.WriteString(L"\n");
}
// читаем шрифты
pApplicationF->InitializeFromArrayFiles(strFontsW_Cur, nFlag);
// скидываем все
m_pInternal->SaveAllFontsJS(pApplicationF, ONLYOFFICE_ALL_FONTS_VERSION);
// поддержка старой версии AllFonts.js
if (m_bIsUseAllVersions)
{
for (int nVer = 0; nVer < ONLYOFFICE_ALL_FONTS_VERSION; ++nVer)
m_pInternal->SaveAllFontsJS(pApplicationF, nVer);
}
// скидываем новый набор шрифтов
if (!m_pInternal->CheckBreak())
NSFile::CFileBinary::SaveToFile(strFontsCheckPath, oFontsLog.GetData());
}
pApplicationF->Release();
pApplicationF = NULL;
if (!m_pInternal->m_pBreaker || m_pInternal->m_pBreaker->IsFontsWorkerRunned())
{
// никаких прерываний не было
pApplicationF = NSFonts::NSApplication::Create();
pApplicationF->InitializeFromFolder(m_sDirectory);
}
else
{
// прервали - подчищаем рабочую директорию
if (m_bIsCleanDirectory)
{
std::vector<std::wstring> arFiles = NSDirectory::GetFiles(m_sDirectory, false);
for (std::vector<std::wstring>::iterator fileIter = arFiles.begin(); fileIter != arFiles.end(); fileIter++)
{
NSFile::CFileBinary::Remove(*fileIter);
}
}
}
return pApplicationF;
}
void CApplicationFontsWorker::CheckThumbnails()
{
m_pInternal->SaveThumbnails(NULL);
}
void CApplicationFontsWorker::SetBreaker(CApplicationFontsWorkerBreaker* pChecker)
{
m_pInternal->m_pBreaker = pChecker;
}
std::string CApplicationFontsWorker::GetAllFonts()
{
std::string sAllFonts = "";
NSFile::CFileBinary::ReadAllTextUtf8A(m_sDirectory + L"/AllFonts.js", sAllFonts);
return sAllFonts;
}
std::vector<std::wstring> CApplicationFontsWorker::GetFontNames(NSFonts::IApplicationFonts* pFonts)
{
std::vector<std::wstring> arNames;
if (!pFonts || !pFonts->GetList())
return arNames;
std::vector<NSFonts::CFontInfo*>* arInfos = pFonts->GetList()->GetFonts();
std::map<std::wstring, bool> map;
for (std::vector<NSFonts::CFontInfo*>::iterator iter = arInfos->begin(); iter != arInfos->end(); iter++)
{
if (map.find((*iter)->m_wsFontName) == map.end())
arNames.push_back((*iter)->m_wsFontName);
}
std::sort(arNames.begin(), arNames.end());
return arNames;
}
std::vector<std::wstring> CApplicationFontsWorker::GetFontNamesWithExcludes(NSFonts::IApplicationFonts* pFonts, std::vector<std::wstring> excludes)
{
std::vector<std::wstring> arNames;
if (!pFonts || !pFonts->GetList())
return arNames;
std::vector<NSFonts::CFontInfo*>* arInfos = pFonts->GetList()->GetFonts();
std::map<std::wstring, bool> map;
for (std::vector<NSFonts::CFontInfo*>::iterator iter = arInfos->begin(); iter != arInfos->end(); iter++)
{
std::wstring fontName = (*iter)->m_wsFontName;
bool isExclude = false;
for (size_t i = 0; i < excludes.size(); ++i) {
if (fontName.find(excludes[i]) != std::string::npos) {
isExclude = true;
break;
}
}
if (isExclude) {
continue;
}
if (map.find(fontName) == map.end()) {
arNames.push_back(fontName);
map[fontName] = true;
}
}
std::sort(arNames.begin(), arNames.end());
return arNames;
}