Compare commits

...

24 Commits

Author SHA1 Message Date
7dffc8245a Merge remote-tracking branch 'origin/hotfix/v9.3.1' into develop 2026-02-28 11:07:16 +03:00
2b46e0251f Merge pull request 'fix for prev binary' (#705) from fix/fix-bugs into hotfix/v9.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/705
2026-02-27 13:42:47 +00:00
078a58772c fix for prev binary 2026-02-27 16:37:30 +03:00
1753007900 Merge pull request 'Fix bug 80293' (#704) from fix/bug80293 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/704
2026-02-27 08:55:33 +00:00
f43230dcaf Fix bug 80293 2026-02-27 11:45:55 +03:00
bb7d2ce8ee Merge pull request 'fix/bug79307' (#702) from fix/bug79307 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/702
2026-02-26 14:57:59 +00:00
ed8a47cbb7 fix/bug79307
fix bug #79307
2026-02-26 17:27:18 +03:00
3b2721e2da fix/bug79307
fix bug #79307
2026-02-26 15:15:23 +03:00
19c66750a8 fix/bug79307
fix bug #79307
2026-02-25 23:09:05 +03:00
927f49291a Merge branch release/v9.3.0 into develop 2026-02-25 15:14:16 +00:00
d54f0326cd Merge branch release/v9.3.0 into master 2026-02-24 14:07:21 +00:00
269dd9b8bc Merge pull request 'Fix bugs 80287, 80300, 80281' (#699) from fix/bug-80287 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/699
2026-02-24 14:00:15 +00:00
9da0b82ead Fix bug 80281 2026-02-24 16:45:55 +03:00
e3334cbea5 Fix bug 80300 2026-02-24 14:27:45 +03:00
01c928c724 Fix bug 80287 2026-02-24 13:41:01 +03:00
e5c1dc4bc3 Merge pull request 'fix bug #79951' (#698) from fix/fix_bugs into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/698
2026-02-23 16:20:47 +00:00
2c5cd93ac5 fix bug #79951 2026-02-23 19:14:03 +03:00
1ff1334746 Merge pull request 'Fix bugs 80285, 80286' (#697) from fix/pdf-bugs into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/697
2026-02-23 15:13:03 +00:00
de096d4229 Fix bug 80286 2026-02-23 17:58:42 +03:00
6c9d795167 Fix bug 80285 2026-02-23 15:39:09 +03:00
8116322018 Fix build on windows 2026-02-23 08:18:44 +03:00
7d06219664 Merge branch hotfix/v9.2.1 into master 2025-12-26 16:24:51 +00:00
e936b0e4e7 Merge branch hotfix/v9.2.1 into master 2025-12-25 10:57:58 +00:00
a22f0bfb60 Merge branch hotfix/v9.2.1 into master 2025-12-17 11:33:19 +00:00
16 changed files with 393 additions and 142 deletions

View File

@ -1,8 +1,6 @@
#include "HTMLReader.h"
#include "../Common/Network/FileTransporter/include/FileTransporter.h"
#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Directory.h"
#include "../DesktopEditor/common/Path.h"
#include "../Common/3dParty/html/htmltoxhtml.h"
@ -20,6 +18,9 @@
#include <boost/tuple/tuple.hpp>
#include "../DesktopEditor/common/Directory.h"
#include "../DesktopEditor/common/File.h"
namespace HTML
{
#define HTML_TAG(tag) GUMBO_TAG_##tag

View File

@ -1068,25 +1068,28 @@ public:
{
case ODRAW::rtLineTo:
{
if (valuePointer + 1 > m_arPoints.size())
for (_UINT16 j = 0; j < m_arSegments[i].m_nCount; j++)
{
break;
if (valuePointer + 1 > m_arPoints.size())
{
break;
strVmlPath += L"l";
strVmlPath += std::to_wstring(m_arPoints[0].x);
strVmlPath += L",";
strVmlPath += std::to_wstring(m_arPoints[0].y);
strVmlPath += L"l";
strVmlPath += std::to_wstring(m_arPoints[0].x);
strVmlPath += L",";
strVmlPath += std::to_wstring(m_arPoints[0].y);
++valuePointer;
}
else
{
strVmlPath += L"l";
strVmlPath += std::to_wstring(m_arPoints[valuePointer].x );
strVmlPath += L",";
strVmlPath += std::to_wstring(m_arPoints[valuePointer].y );
++valuePointer;
}
else
{
strVmlPath += L"l";
strVmlPath += std::to_wstring(m_arPoints[valuePointer].x );
strVmlPath += L",";
strVmlPath += std::to_wstring(m_arPoints[valuePointer].y );
++valuePointer;
++valuePointer;
}
}
}break;
case ODRAW::rtCurveTo:

View File

@ -3840,6 +3840,8 @@ void BinaryDocumentTableWriter::WriteAltChunk(OOX::Media& oAltChunkFile, OOX::CS
{
OOX::CDocx oDocx = OOX::CDocx(OOX::CPath(sResultDocxDir));
OOX::IFileContainer* oldRels = m_pOfficeDrawingConverter->GetRelsPtr();
if (oDocx.m_oMain.document)
{
ParamsDocumentWriter oParamsDocumentWriterEmb(oDocx.m_oMain.document);
@ -3875,6 +3877,9 @@ void BinaryDocumentTableWriter::WriteAltChunk(OOX::Media& oAltChunkFile, OOX::CS
oParamsWriterEmb.m_pSettings = oDocx.m_oMain.settings;
oParamsWriterEmb.m_pCurRels = oParamsDocumentWriterEmb.m_pRels;
m_pOfficeDrawingConverter->SetRelsPtr(oParamsDocumentWriterEmb.m_pRels);
m_pOfficeDrawingConverter->Clear();
BinaryDocumentTableWriter oBinaryDocumentEmbTableWriter(oParamsWriterEmb, oParamsDocumentWriterEmb, &oParamsWriterEmb.m_mapIgnoreComments, NULL);
oBinaryDocumentEmbTableWriter.WriteDocumentContent(oDocx.m_oMain.document->m_arrItems);
@ -3900,10 +3905,14 @@ void BinaryDocumentTableWriter::WriteAltChunk(OOX::Media& oAltChunkFile, OOX::CS
oParamsWriterEmb.m_pEmbeddedStyles = oDocxFlat.m_pStyles.GetPointer();
oParamsWriterEmb.m_pEmbeddedNumbering = oDocxFlat.m_pNumbering.GetPointer();
m_pOfficeDrawingConverter->SetRelsPtr(oParamsDocumentWriterEmb.m_pRels);
m_pOfficeDrawingConverter->Clear();
BinaryDocumentTableWriter oBinaryDocumentEmbTableWriter(oParamsWriterEmb, oParamsDocumentWriterEmb, &oParamsWriterEmb.m_mapIgnoreComments, NULL);
oBinaryDocumentEmbTableWriter.WriteDocumentContent(oDocxFlat.m_pDocument->m_arrItems);
}
}
m_pOfficeDrawingConverter->SetRelsPtr(oldRels);
}
NSDirectory::DeleteDirectory(sResultDocxDir);
}

View File

@ -683,6 +683,7 @@ namespace NSBinPptxRW
}
else
{
m_arHandoutMasters_Theme.emplace_back();
CreateDefaultHandoutMasters(m_arHandoutMasters_Theme[0]);
}
}

View File

@ -267,7 +267,7 @@ namespace SimpleTypes
std::wstring CGuid::ToString (bool braces) const
{
std::wstringstream sstream;
sstream << boost::wformat( L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" ) % m_oGUID.a % m_oGUID.b % m_oGUID.c % m_oGUID.d % m_oGUID.e % m_oGUID.f % m_oGUID.g % m_oGUID.h % m_oGUID.i % m_oGUID.j % m_oGUID.k;
sstream << boost::wformat( L"%08X-%04X-%04X-%02X%0X-%02X%02X%02X%02X%02X%02X" ) % m_oGUID.a % m_oGUID.b % m_oGUID.c % m_oGUID.d % m_oGUID.e % m_oGUID.f % m_oGUID.g % m_oGUID.h % m_oGUID.i % m_oGUID.j % m_oGUID.k;
std::wstring res = (braces ? L"{" : L"") + sstream.str() + (braces ? L"}" : L"");
return res;
}

View File

@ -5088,7 +5088,14 @@ bool DocxConverter::convert(OOX::Logic::CTableProperty *oox_table_pr, odf_writer
oox_table_pr->m_oTblW->m_oW->GetValue() > 0 )
{
if ( oox_table_pr->m_oTblW->m_oW->IsPercent() == false)
odt_context->table_context()->set_default_column_width(oox_table_pr->m_oTblW->m_oW->GetValue() / 20.);
{
const double PtPerCm = 72 / 2.54;
const double TwPerPt = 20.0;
const double TwPerCm = PtPerCm * TwPerPt;
const double WidthCm = oox_table_pr->m_oTblW->m_oW->GetValue() / TwPerCm;
odt_context->table_context()->set_default_column_width(WidthCm);
table_properties->content_.style_width_ = odf_types::length(WidthCm, odf_types::length::cm);
}
}
else if ( oox_table_pr->m_oTblW->m_oType->GetValue() == SimpleTypes::tblwidthAuto &&
oox_table_pr->m_oTblW->m_oW->GetValue() == 0 )

View File

@ -227,7 +227,10 @@ void odf_table_context::end_table()
style_table_properties * table_props = style_->content_.add_get_style_table_properties();
if (table_props)
{
table_props->content_.style_width_ = length(length(impl_->current_table().table_width,length::pt).get_value_unit(length::cm),length::cm);
if( !table_props->content_.style_width_ )
{
table_props->content_.style_width_ = length(length(impl_->current_table().table_width,length::pt).get_value_unit(length::cm),length::cm);
}
}
}
}

View File

@ -3452,8 +3452,12 @@ bool CPdfEditor::EditAnnot(int _nPageIndex, int nID)
pAnnot = new PdfWriter::CPolygonLineAnnotation(pXref);
else if (oType.isName("FreeText"))
{
std::map<std::wstring, std::wstring> mapFont = PdfReader::GetAnnotFont(pPDFDocument, m_pReader->GetFontManager(), pFontList, &oAnnotRef);
m_mFonts.insert(mapFont.begin(), mapFont.end());
std::vector<PdfReader::CAnnotFontInfo> arrFont = PdfReader::GetAnnotFontInfos(pPDFDocument, m_pReader->GetFontManager(), pFontList, &oAnnotRef);
for (int i = 0; i < arrFont.size(); ++i)
{
PdfReader::CAnnotFontInfo oFontInfo = arrFont[i];
m_mFonts.insert(std::make_pair(oFontInfo.wsFontName, oFontInfo.wsFontPath));
}
pAnnot = new PdfWriter::CFreeTextAnnotation(pXref);
}
else if (oType.isName("Caret"))
@ -4011,7 +4015,8 @@ void CPdfEditor::ClearPage()
{
PDFDoc* pPDFDocument = NULL;
PdfReader::CPdfFontList* pFontList = NULL;
int nPageIndex = m_pReader->GetPageIndex(m_nEditPage, &pPDFDocument, &pFontList);
int nStartRefID = 0;
int nPageIndex = m_pReader->GetPageIndex(m_nEditPage, &pPDFDocument, &pFontList, &nStartRefID);
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
if (nPageIndex < 0 || !pPDFDocument || !pDoc)
return;
@ -4074,7 +4079,7 @@ void CPdfEditor::ClearPage()
if (pResources)
{
std::vector<int> arrUniqueResources;
ScanAndProcessFonts(pPDFDocument, xref, pResources, 0, arrUniqueResources, pFontList);
ScanAndProcessFonts(pPDFDocument, xref, pResources, 0, arrUniqueResources, pFontList, nStartRefID);
m_pReader->SetFonts(m_nEditPage);
}
oAnnots.free();
@ -4325,7 +4330,7 @@ std::vector<double> CPdfEditor::WriteRedact(const std::vector<std::wstring>& arr
}
return arrRes;
}
void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pResources, int nDepth, std::vector<int>& arrUniqueResources, PdfReader::CPdfFontList* pFontList)
void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pResources, int nDepth, std::vector<int>& arrUniqueResources, PdfReader::CPdfFontList* pFontList, int nStartRefID)
{
if (nDepth > 5 || !pResources)
return;
@ -4384,7 +4389,7 @@ void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pRe
mCodeToGID[nIndex] = pFontEntry.pCodeToGID[nIndex];
}
m_pWriter->AddFont(L"Embedded: " + wsFontName, false, false, wsFileName, 0);
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(nFontRef.num);
PdfWriter::CObjectBase* pObj = m_mObjManager.GetObj(nFontRef.num + nStartRefID);
if (!pObj)
{
pObj = new PdfWriter::CObjectBase();
@ -4405,7 +4410,7 @@ void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pRe
oFonts.free();
// Обработка XObject
auto fScanFonts = [this, pPDFDocument, xref, nDepth, &arrUniqueResources, pFontList](Dict* pResources, const char* sName)
auto fScanFonts = [this, pPDFDocument, xref, nDepth, &arrUniqueResources, pFontList, nStartRefID](Dict* pResources, const char* sName)
{
Object oObject;
if (!pResources->lookup(sName, &oObject)->isDict())
@ -4431,7 +4436,7 @@ void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pRe
arrUniqueResources.push_back(oRef.getRef().num);
oXObj.free(); oRef.free();
ScanAndProcessFonts(pPDFDocument, xref, oResources.getDict(), nDepth + 1, arrUniqueResources, pFontList);
ScanAndProcessFonts(pPDFDocument, xref, oResources.getDict(), nDepth + 1, arrUniqueResources, pFontList, nStartRefID);
oResources.free();
}
oObject.free();
@ -4470,7 +4475,7 @@ void CPdfEditor::ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pRe
arrUniqueResources.push_back(oRef.getRef().num);
oSMaskGroup.free(); oRef.free();
ScanAndProcessFonts(pPDFDocument, xref, oResources.getDict(), nDepth + 1, arrUniqueResources, pFontList);
ScanAndProcessFonts(pPDFDocument, xref, oResources.getDict(), nDepth + 1, arrUniqueResources, pFontList, nStartRefID);
oResources.free();
}
oExtGState.free();

View File

@ -115,7 +115,7 @@ private:
void GetPageTree(XRef* xref, Object* pPagesRefObj, PdfWriter::CPageTree* pPageParent = NULL);
bool SplitPages(const int* arrPageIndex, unsigned int unLength, PDFDoc* _pDoc, int nStartRefID);
bool ChangeFullNameParent(int nParent, const std::string& sPrefixForm, std::vector<int>& arrRename);
void ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pResources, int nDepth, std::vector<int>& arrUniqueResources, PdfReader::CPdfFontList* pFontList);
void ScanAndProcessFonts(PDFDoc* pPDFDocument, XRef* xref, Dict* pResources, int nDepth, std::vector<int>& arrUniqueResources, PdfReader::CPdfFontList* pFontList, int nStartRefID);
struct CRedactData
{

View File

@ -1095,17 +1095,16 @@ HRESULT CPdfFile::put_FontName(const std::wstring& wsName)
size_t lastSpace = wsName.find_last_of(L' ');
if (lastSpace != std::wstring::npos)
{
std::wstring numberStr = wsName.substr(lastSpace + 1);
int nTargetCode = std::stoi(numberStr);
std::wstring sTargetHash = wsName.substr(lastSpace + 1);
const std::map<std::wstring, std::wstring>& mFonts = m_pInternal->pReader->GetFonts();
auto it = std::find_if(mFonts.begin(), mFonts.end(), [nTargetCode](const std::pair<const std::wstring, std::wstring>& pair)
std::map<std::wstring, std::wstring>::const_iterator it = std::find_if(mFonts.begin(), mFonts.end(), [&sTargetHash](const std::pair<const std::wstring, std::wstring>& pair)
{
const std::wstring& key = pair.first;
size_t pos = key.rfind(L' ');
if (pos != std::wstring::npos)
{
int keyCode = std::stoi(key.substr(pos + 1));
return keyCode == nTargetCode;
std::wstring sKeyHash = key.substr(pos + 1);
return sKeyHash == sTargetHash;
}
return false;
});

View File

@ -218,6 +218,34 @@ bool CheckFontNameStyle(std::wstring& sName, const std::wstring& sStyle)
}
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;
};
namespace PdfReader
{
@ -692,18 +720,19 @@ bool FindFonts(Object* oStream, int nDepth, Object* oResFonts)
oXObject.free(); oResources.free();
return false;
}
std::map<std::wstring, std::wstring> GetAnnotFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList, Object* oAnnotRef)
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);
std::map<std::wstring, std::wstring> mFontFreeText;
Object oAP, oN;
if (!oAnnot.dictLookup("AP", &oAP)->isDict() || !oAP.dictLookup("N", &oN)->isStream())
{
oAP.free(); oN.free(); oAnnot.free();
return mFontFreeText;
return result;
}
oAP.free();
@ -711,13 +740,10 @@ std::map<std::wstring, std::wstring> GetAnnotFont(PDFDoc* pdfDoc, NSFonts::IFont
if (!FindFonts(&oN, 0, &oFonts))
{
oN.free(); oFonts.free(); oAnnot.free();
return mFontFreeText;
return result;
}
oN.free();
CFontList* pAppFontList = (CFontList*)pFontManager->GetApplication()->GetList();
NSFonts::IFontsMemoryStorage* pMemoryStorage = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage();
for (int i = 0, nFonts = oFonts.dictGetLength(); i < nFonts; ++i)
{
Object oFontRef;
@ -727,101 +753,132 @@ std::map<std::wstring, std::wstring> GetAnnotFont(PDFDoc* pdfDoc, NSFonts::IFont
continue;
}
CAnnotFontInfo info;
std::string sFontName, sActualFontName;
bool bBold = false, bItalic = false;
std::wstring sFontPath = GetFontData(pdfDoc, pFontManager, pFontList, &oFontRef, sFontName, sActualFontName, bBold, bItalic);
info.wsFontPath = GetFontData(pdfDoc, pFontManager, pFontList, &oFontRef, sFontName, sActualFontName, info.bBold, info.bItalic);
oFontRef.free();
if (sFontPath.empty() || IsBaseFont(sFontPath) || !sActualFontName.empty())
if (info.wsFontPath.empty() || IsBaseFont(info.wsFontPath))
continue;
std::wstring wsFontName = UTF8_TO_U(sFontName);
NSFonts::IFontStream* pFontStream = NULL;
bool bRemoveStream = false;
if (pMemoryStorage)
pFontStream = (NSFonts::IFontStream*)pMemoryStorage->Get(sFontPath);
else
{
pFontStream = NSFonts::NSStream::Create();
pFontStream->CreateFromFile(sFontPath);
bRemoveStream = true;
}
if (pFontStream)
{
bool bNew = true;
std::vector<NSFonts::CFontInfo*>* arrFontList = pAppFontList->GetFonts();
for (int nIndex = 0; nIndex < arrFontList->size(); ++nIndex)
{
if (((*arrFontList)[nIndex]->m_wsFontPath == sFontPath ||
(*arrFontList)[nIndex]->m_wsFontName == wsFontName) &&
(*arrFontList)[nIndex]->m_bBold == (bBold ? 1 : 0) &&
(*arrFontList)[nIndex]->m_bItalic == (bItalic ? 1 : 0))
{
bNew = false;
break;
}
}
if (bNew)
pAppFontList->Add(sFontPath, pFontStream);
}
if (bRemoveStream)
RELEASEINTERFACE(pFontStream);
mFontFreeText[wsFontName] = sFontPath;
info.wsFontName = UTF8_TO_U(sFontName);
if (!sActualFontName.empty())
info.wsActualFontName = UTF8_TO_U(sActualFontName);
result.push_back(info);
}
oFonts.free(); oAnnot.free();
return mFontFreeText;
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<CAnnotMarkup::CFontData*>& arrRC)
{
std::map<std::wstring, std::wstring> mRes;
std::map<std::wstring, std::wstring> mFontFreeText = GetAnnotFont(pdfDoc, pFontManager, pFontList, oAnnotRef);
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 < arrRC.size(); ++i)
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 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";
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";
if (bBold && bItalic) sFontName += "-BoldOblique";
else if (bBold) sFontName += "-Bold";
else if (bItalic) sFontName += "-Oblique";
}
wsFontName = UTF8_TO_U(sFontName);
const unsigned char* pData14 = NULL;
unsigned int nSize14 = 0;
NSFonts::IFontsMemoryStorage* pMemoryStorage = NSFonts::NSApplicationFontStream::GetGlobalMemoryStorage();
if (pMemoryStorage && !pMemoryStorage->Get(wsFontName) && GetBaseFont(wsFontName, pData14, nSize14))
pMemoryStorage->Add(wsFontName, (BYTE*)pData14, nSize14, false);
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 < arrRC.size(); ++j)
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))
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;
@ -830,47 +887,57 @@ std::map<std::wstring, std::wstring> GetFreeTextFont(PDFDoc* pdfDoc, NSFonts::IF
}
else
{
NSFonts::CFontSelectFormat oFontSelect;
if (bBold)
oFontSelect.bBold = new INT(1);
if (bItalic)
oFontSelect.bItalic = new INT(1);
oFontSelect.wsName = new std::wstring(wsFontName);
const CAnnotFontInfo* pMatch = FindMatchInAnnotFonts(annotFonts, wsFontName, bBold, bItalic);
NSFonts::CFontInfo* pFontInfo = pAppFontList->GetByParams(oFontSelect);
if (pFontInfo && !pFontInfo->m_wsFontPath.empty())
std::wstring wsResolvedName;
std::wstring wsResolvedPath;
bool bFoundInAnnot = false;
if (pMatch)
{
std::wstring sFontPath = pFontInfo->m_wsFontPath;
bool bFindFreeText = false;
for (std::map<std::wstring, std::wstring>::iterator it = mFontFreeText.begin(); it != mFontFreeText.end(); ++it)
{
if (it->second == sFontPath)
{
bFindFreeText = true;
break;
}
}
std::wstring wsFontBaseName = pFontInfo->m_wsFontName;
EraseSubsetTag(wsFontBaseName);
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);
if (bFindFreeText)
NSFonts::CFontInfo* pFontInfo = pAppFontList->GetByParams(oFontSelect);
if (pFontInfo && !pFontInfo->m_wsFontPath.empty())
{
arrRC[i]->sFontFamily = U_TO_UTF8(wsFontBaseName);
mRes[wsFontBaseName] = pFontInfo->m_wsFontPath;
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(wsFontBaseName);
arrRC[i]->sActualFont = U_TO_UTF8(wsResolvedName);
}
arrRC[i]->bFind = true;
std::string sFontNameNew = bFindFreeText ? arrRC[i]->sFontFamily : arrRC[i]->sActualFont;
for (int j = i; j < arrRC.size(); ++j)
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 (arrRC[j]->sFontFamily == sFontName &&
bBold == (bool)((arrRC[j]->unFontFlags >> 0) & 1) &&
bItalic == (bool)((arrRC[j]->unFontFlags >> 1) & 1))
{
if (bFindFreeText)
if (bFoundInAnnot)
arrRC[j]->sFontFamily = sFontNameNew;
else
{

View File

@ -46,13 +46,22 @@
namespace PdfReader
{
struct CAnnotFontInfo
{
std::wstring wsFontName;
std::wstring wsFontPath;
std::wstring wsActualFontName;
bool bBold = false;
bool bItalic = false;
};
std::string GetRCFromDS(const std::string& sDS, Object* pContents, const std::vector<double>& arrCFromDA);
bool IsNeedCMap(PDFDoc* pDoc);
bool IsBaseFont(const std::wstring& wsName);
std::map<std::wstring, std::wstring> GetAllFonts(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, bool bIsNeedCMap);
std::wstring GetFontData(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList, Object* oFontRef, std::string& sFontName, std::string& sActualFontName, bool& bBold, bool& bItalic, bool bIsNeedCMap = false);
bool GetFontFromAP(PDFDoc* pdfDoc, AcroFormField* pField, Object* oFontRef, std::string& sFontKey);
std::map<std::wstring, std::wstring> GetAnnotFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList, Object* oAnnotRef);
std::vector<CAnnotFontInfo> GetAnnotFontInfos(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef);
std::map<std::wstring, std::wstring> GetFreeTextFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef, std::vector<CAnnotMarkup::CFontData*>& arrRC);
bool FindFonts(Object* oStream, int nDepth, Object* oResFonts);
void CollectFontWidths(GfxFont* gfxFont, Dict* pFontDict, std::map<unsigned int, unsigned int>& mGIDToWidth);

View File

@ -44,6 +44,7 @@
#include "../lib/xpdf/PDFDoc.h"
#include "../lib/xpdf/CharCodeToUnicode.h"
#include "../lib/xpdf/TextString.h"
#include "../lib/xpdf/Decrypt.h"
#include "XmlUtils.h"
#include "PdfFont.h"
@ -101,6 +102,125 @@ namespace PdfReader
{
return (p[2]*77 + p[1]*150 + p[0]*29) >> 8;
}
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);
}
}
class CMemoryFontStream
@ -1463,8 +1583,8 @@ namespace PdfReader
wsFontName = wsFontBaseName;
if (bNotFullName)
EraseSubsetTag(wsFontName);
else if (!bFontBase14)
wsFontName += (L" " + std::to_wstring(pFont->getID()->num));
else if (!bFontBase14 && !bFontSubstitution)
wsFontName += (L" " + ComputeFontHash(pXref, pFont));
pEntry->wsFilePath = wsFileName;
pEntry->wsFontName = wsFontName;

View File

@ -146,7 +146,7 @@ namespace PdfWriter
{
if (m_pObj->GetType() != object_type_UNKNOWN)
return m_pObj;
return new PdfWriter::CProxyObject(m_pObj, true);
return new PdfWriter::CProxyObject(m_pObj);
}
CObjectBase* CFontEmbedded::GetObj2()
{

View File

@ -153,7 +153,7 @@ namespace PdfWriter
typedef std::map<unsigned short, ByteList> UShortToByteList;
struct EncodingsInfo
{
EncodingsInfo() { mEncoding = NULL; }
EncodingsInfo() { mEncoding = NULL; mEncodingsCount = 0; }
long long mEncodingStart;
long long mEncodingEnd;
@ -4738,12 +4738,12 @@ namespace PdfWriter
// assuming that 0 is in the subset glyphs IDs, which does not require encoding
// get the encodings count
BYTE encodingGlyphsCount = std::min((BYTE)(inSubsetGlyphIDs.size() - 1), encodingInfo->mEncodingsCount);
BYTE encodingGlyphsCount = inSubsetGlyphIDs.empty() ? 0 : std::min((BYTE)(inSubsetGlyphIDs.size() - 1), encodingInfo->mEncodingsCount);
mPrimitivesWriter->WriteCard8(encodingGlyphsCount);
for (BYTE i = 0; i < encodingGlyphsCount; ++i)
{
if (inSubsetGlyphIDs[i + 1] < encodingInfo->mEncodingsCount)
if (inSubsetGlyphIDs[i + 1] < encodingInfo->mEncodingsCount && inSubsetGlyphIDs[i + 1] > 0)
mPrimitivesWriter->WriteCard8(encodingInfo->mEncoding[inSubsetGlyphIDs[i + 1] - 1]);
else
mPrimitivesWriter->WriteCard8(0);
@ -4778,7 +4778,7 @@ namespace PdfWriter
{
int i = 1;
for (; it != inSubsetGlyphIDs.end(); ++it, ++i)
mPrimitivesWriter->WriteSID(inCIDMapping ? (*inCIDMapping)[i] : i);
mPrimitivesWriter->WriteSID(inCIDMapping && i < (int)inCIDMapping->size() ? (*inCIDMapping)[i] : i);
}
else

View File

@ -5104,15 +5104,42 @@ void Gfx::opEndIgnoreUndef(Object args[], int numArgs) {
void Gfx::SkipBDC()
{
Object obj;
// Стек аргументов (как в основном цикле обработки)
Object args[maxArgs];
int numArgs = 0;
getContentObj(&obj);
while (!obj.isEOF()) {
if (obj.isCmd("BMC") || obj.isCmd("BDC"))
SkipBDC();
else if (obj.isCmd("EMC"))
break;
obj.free();
getContentObj(&obj);
if (obj.isCmd("BMC") || obj.isCmd("BDC")) {
// Сбрасываем накопленные аргументы перед рекурсией
for (int i = 0; i < numArgs; ++i) args[i].free();
numArgs = 0;
SkipBDC();
} else if (obj.isCmd("EMC")) {
break;
} else if (obj.isCmd("Tf")) {
if (numArgs == 2) {
opSetFont(args, numArgs);
out->updateFont(state);
}
for (int i = 0; i < numArgs; ++i) args[i].free();
numArgs = 0;
} else if (obj.isCmd()) {
// Любая другая команда — просто сбрасываем аргументы
for (int i = 0; i < numArgs; ++i) args[i].free();
numArgs = 0;
} else {
// Операнд — кладём в стек аргументов
if (numArgs < maxArgs) {
obj.copy(&args[numArgs]);
++numArgs;
}
}
obj.free();
getContentObj(&obj);
}
for (int i = 0; i < numArgs; ++i) args[i].free();
obj.free();
}
void Gfx::opBeginMarkedContent(Object args[], int numArgs) {