Compare commits

...

40 Commits

Author SHA1 Message Date
7043b36093 Merge branch hotfix/v9.3.1 into master 2026-03-03 11:57:42 +00:00
52f8640b10 Merge pull request 'fix prev' (#707) from fix/fix-bugs into hotfix/v9.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/707
2026-03-02 10:46:12 +00:00
8a1a45f403 fix prev 2026-03-02 10:04:34 +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
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
5428c28874 Fix bug 80220 2026-02-23 02:50:05 +03:00
e389b4d799 Merge pull request 'Fix memory init' (#696) from fix/pict into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/696
2026-02-22 19:59:27 +00:00
8c607a429a Fix memory init 2026-02-22 22:55:54 +03:00
00e80b68f6 Merge pull request 'For bug 79086' (#695) from fix/pict into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/695
2026-02-22 19:46:58 +00:00
377317892d For bug 79086 2026-02-22 22:41:43 +03:00
ef77688ea2 Merge pull request 'For bug 80280' (#694) from fix/bug-80280 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/694
2026-02-22 17:26:59 +00:00
eb756fab5c For bug 80280 2026-02-22 20:20:53 +03:00
2d2bb7cee7 Merge pull request 'Fix stroke with images' (#693) from fix/docx-renderer into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/693
2026-02-20 17:25:52 +00:00
1abe5b2dcc Fix stroke with images 2026-02-20 20:22:03 +03:00
2b4bdfdc05 Merge pull request 'Fix paragraph line spacing' (#692) from fix/docx-renderer into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/692
2026-02-20 16:13:46 +00:00
007c70bcc0 Fix paragraph line spacing 2026-02-20 19:11:20 +03:00
017b43dd66 Merge pull request 'fix bug #80155' (#691) from fix/fix-bugs into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/691
2026-02-20 14:40:36 +00:00
88a6760086 fix bug #80155 2026-02-20 17:39:07 +03:00
98a0551123 Merge pull request 'fix/bug80153' (#690) from fix/bug80153 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/690
2026-02-20 14:36:15 +00:00
49cb1cdbca fix/bug80153
fix bug #80153 and #80152
2026-02-20 14:39:17 +03:00
73e00a5096 Merge pull request 'fix bug #80011' (#689) from fix/bug80011 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/689
2026-02-20 11:11:46 +00:00
462eeda60c fix bug #80011 2026-02-20 12:22:26 +03:00
e1f81b6830 Merge pull request 'Fix bug 80240' (#688) from fix/bug-80240 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/688
2026-02-20 08:39:06 +00:00
d63a6e2ba4 Fix buf 80240 2026-02-20 11:35:44 +03:00
b9cf1bd204 Merge pull request 'Fix bug #80228' (#687) from fix/bug-80228 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/687
2026-02-20 08:04:03 +00:00
81ec569f59 Fix bug #80228 2026-02-20 03:55:40 +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
34 changed files with 494 additions and 161 deletions

View File

@ -188,7 +188,7 @@ namespace NSNetwork
else
{
NSURL* url = [NSURL URLWithString:escapedURL];
NSData* urlData = [NSData dataWithContentsOfURL:url];
NSData* urlData = [[NSData alloc] initWithContentsOfURL:url];
if ( urlData )
{
NSString* filePath = StringWToNSString(m_sDownloadFilePath);

View File

@ -985,6 +985,8 @@ bool CPictFile::DecodeData()
m_oImgData.m_pPixelData = (BYTE*)malloc(4 * m_oImgData.m_nHeight * m_oImgData.m_nWidth);
if (m_oFrame.get_Data())
memcpy(m_oImgData.m_pPixelData, m_oFrame.get_Data(), 4 * m_oImgData.m_nHeight * m_oImgData.m_nWidth);
else
memset(m_oImgData.m_pPixelData, 255, m_oImgData.m_nWidth * m_oImgData.m_nHeight * 4);
}
return true;
@ -1717,6 +1719,12 @@ void CPictFile::InitializeRenderer()
return;
m_pFrameData = new BYTE[4 * m_oImgData.m_nWidth * m_oImgData.m_nHeight];
unsigned int back = 0xffffff;
unsigned int *pData32 = (unsigned int*)m_pFrameData;
unsigned int *pData32End = pData32 + m_oImgData.m_nWidth * m_oImgData.m_nHeight;
while (pData32 < pData32End)
*pData32++ = back;
m_oFrame.put_Data(m_pFrameData);
m_oFrame.put_Width(m_oImgData.m_nWidth);
m_oFrame.put_Height(m_oImgData.m_nHeight);

View File

@ -93,6 +93,7 @@ int CDocxRenderer::Convert(IOfficeDrawingFile* pFile, const std::wstring& sDst,
m_pInternal->m_oDocument.m_oCurrentPage.m_bWriteStyleRaw = false;
m_pInternal->m_bIsSupportShapeCommands = false;
m_pInternal->m_oDocument.m_bIsRecord = true;
m_pInternal->m_oDocument.m_oCurrentPage.m_bFirstParagraphLineCorrection = true;
if (bIsOutCompress)
m_pInternal->m_oDocument.m_strTempDirectory = NSDirectory::CreateDirectoryWithUniqueName(m_pInternal->m_sTempDirectory);

View File

@ -195,12 +195,14 @@ namespace NSDocxRenderer
if (!m_arShapes.empty())
{
auto& last_shape = m_arShapes.back();
if (last_shape->IsEqual(top, bot, left, right) && rotation == last_shape->m_dRotation && lType != m_lLastType && m_lLastType != 0)
auto& last_shape = m_arShapes.back();
bool is_type_diff = lType == c_nStroke && (m_lLastType == c_nWindingFillMode || m_lLastType == c_nEvenOddFillMode);
is_type_diff = is_type_diff || (m_lLastType == c_nStroke && (lType == c_nWindingFillMode || m_lLastType == c_nEvenOddFillMode));
if (last_shape->IsEqual(top, bot, left, right) && rotation == last_shape->m_dRotation && is_type_diff && m_lLastType != 0)
{
set_fill_mode(last_shape);
// Reset stroke/fill logic
m_lLastType = 0;
if (pInfo) DrawImage(last_shape, pInfo, image_vector);
m_lLastType = 0; // reset stroke/fill logic
return;
}
}
@ -1659,7 +1661,11 @@ namespace NSDocxRenderer
paragraph->m_dRightBorder = m_dWidth - paragraph->m_dRight;
paragraph->m_dLeftBorder = min_left;
paragraph->m_dLineHeight = paragraph->m_dHeight / paragraph->m_arTextLines.size();
if (paragraph->m_arTextLines.size() == 1)
paragraph->m_dLineHeight = paragraph->m_dHeight;
else
paragraph->m_dLineHeight = (paragraph->m_dBot - firstLine->m_dBotWithMaxDescent) / (paragraph->m_arTextLines.size() - 1);
paragraph->m_bIsNeedFirstLineIndent = false;
paragraph->m_dFirstLine = 0;
paragraph->m_wsStyleId = m_oManagers.pParagraphStyleManager->GetDefaultParagraphStyleId(*paragraph);
@ -1674,7 +1680,6 @@ namespace NSDocxRenderer
{
double offset = paragraph->m_dLineHeight - firstLine_height;
paragraph->m_dTop -= offset;
paragraph->m_dBot -= offset;
}
else
{
@ -1682,9 +1687,10 @@ namespace NSDocxRenderer
double newAscent = ascent * paragraph->m_dLineHeight / firstLine_height;
double offset = ascent - newAscent;
paragraph->m_dTop += offset;
paragraph->m_dBot += offset;
}
paragraph->m_dHeight = paragraph->m_dBot - paragraph->m_dTop;
}
// setting TextAlignmentType
if (paragraph->m_arTextLines.size() > 1)
{

View File

@ -1,7 +1,6 @@
#include "HTMLReader.h"
#include "../Common/Network/FileTransporter/include/FileTransporter.h"
#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Path.h"
#include "../Common/3dParty/html/htmltoxhtml.h"
@ -19,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
@ -261,18 +263,22 @@ inline bool UnreadableNode(const std::wstring& wsNodeName);
inline bool TagIsUnprocessed(const std::wstring& wsTagName);
CHTMLReader::CHTMLReader()
: m_pWriter(nullptr)
: m_bIsTempDirOwner(true), m_pWriter(nullptr)
{}
CHTMLReader::~CHTMLReader()
{
if (nullptr != m_pWriter)
delete m_pWriter;
if (m_bIsTempDirOwner && !m_wsTempDirectory.empty())
NSDirectory::DeleteDirectory(m_wsTempDirectory);
}
void CHTMLReader::SetTempDirectory(const std::wstring& wsPath)
{
m_wsTempDirectory = wsPath;
m_bIsTempDirOwner = m_wsTempDirectory.empty();
}
void CHTMLReader::SetCoreDirectory(const std::wstring& wsPath)
@ -327,7 +333,6 @@ void CHTMLReader::Clear()
m_mTags.clear();
m_wsTempDirectory.clear();
m_wsSrcDirectory .clear();
m_wsDstDirectory .clear();
m_wsBaseDirectory.clear();
@ -503,6 +508,11 @@ bool CHTMLReader::Convert(const std::wstring& wsPath, Convert_Func Convertation)
if (nullptr == m_pWriter || !Convertation(wsPath, m_oLightReader) || !m_oLightReader.IsValid() || !IsHTML())
return false;
if (m_wsTempDirectory.empty())
{
m_wsTempDirectory = NSDirectory::CreateDirectoryWithUniqueName(NSDirectory::GetTempPath());
}
m_wsSrcDirectory = NSSystemPath::GetDirectoryName(wsPath);
m_oLightReader.MoveToStart();
@ -1009,6 +1019,19 @@ bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors)
if(m_oLightReader.IsEmptyNode())
return false;
if (nullptr != m_pWriter && !m_pWriter->SupportNestedTables())
{
//Временно разруливаем это тут, так как по текущей логике мы сначала
//читаем всю таблицу и её вложенные элементы, а потом приступаем к конвертации,
//поэтому конвертору уже приходят вложенные таблицы, что в MD запрещено
for (std::vector<NSCSS::CNode>::const_reverse_iterator itElement{arSelectors.crbegin() + 1}; itElement < arSelectors.crend(); ++itElement)
if (L"table" == itElement->m_wsName)
return false;
if (nullptr != dynamic_cast<CMDWriter*>(m_pWriter))
((CMDWriter*)m_pWriter)->EnteredTable();
}
CStorageTable oTable;
NSCSS::CCompiledStyle *pStyle = arSelectors.back().m_pCompiledStyle;
@ -1100,6 +1123,9 @@ bool CHTMLReader::ReadTable(std::vector<NSCSS::CNode>& arSelectors)
oTable.SetAlign(pStyle->m_oDisplay.GetHAlign().ToWString());
//------
//TODO:: переписать работу с таблицами без предварительной конвертации ячеек и хранения их внутренних данных
//Читаем содержимое таблицы -> считаем ячейки -> нормализуем таблицу -> конвертим таблицу
int nDeath = m_oLightReader.GetDepth();
while(m_oLightReader.ReadNextSiblingNode(nDeath))
{

View File

@ -20,6 +20,7 @@ class CHTMLReader
XmlUtils::CXmlLiteReader m_oLightReader; // SAX Reader
NSCSS::CCssCalculator m_oCSSCalculator; // Css калькулятор
bool m_bIsTempDirOwner;
std::wstring m_wsTempDirectory; // Temp папка
std::wstring m_wsSrcDirectory; // Директория источника
std::wstring m_wsDstDirectory; // Директория назначения

View File

@ -53,7 +53,7 @@ bool CBold<CMDWriter>::Open(const std::vector<NSCSS::CNode>& arSelectors, const
return true;
m_pWriter->WriteOpenSpecialString(L"**");
m_pWriter->EneteredBold();
m_pWriter->EnteredBold();
return true;
}
@ -97,7 +97,7 @@ bool CItalic<CMDWriter>::Open(const std::vector<NSCSS::CNode>& arSelectors, cons
return true;
m_pWriter->WriteOpenSpecialString(L"*");
m_pWriter->EneteredItalic();
m_pWriter->EnteredItalic();
return true;
}
@ -124,7 +124,7 @@ bool CStrike<CMDWriter>::Open(const std::vector<NSCSS::CNode>& arSelectors, cons
return true;
m_pWriter->WriteOpenSpecialString(L"~~");
m_pWriter->EneteredStrike();
m_pWriter->EnteredStrike();
return true;
}
@ -309,7 +309,7 @@ CTable<CMDWriter>::CTable(CMDWriter* pWriter)
bool CTable<CMDWriter>::Open(const std::vector<NSCSS::CNode>& arSelectors, const boost::any& oExtraData)
{
if (!ValidWriter() || m_pWriter->InTable()) //В MD не поддерживаются вложенные таблицы
if (!ValidWriter() /*|| m_pWriter->InTable()*/) //В MD не поддерживаются вложенные таблицы (пока разруливаем в парсере)
return false;
m_pWriter->WriteBreakLine();

View File

@ -28,6 +28,9 @@ public:
virtual void RevertDataOutput() = 0; // Возвращаем место вывода к исходному
virtual XmlString* GetCurrentDocument() const = 0;
//TODO:: перенести разруливание вложенных таблиц в конвертацию после изменения принципа работы с таблицами
virtual bool SupportNestedTables() const = 0;
};
}

View File

@ -202,6 +202,11 @@ XmlString* CMDWriter::GetCurrentDocument() const
return m_arStates.top().m_pCurrentDocument;
}
bool CMDWriter::SupportNestedTables() const
{
return false;
}
void CMDWriter::WriteBreakLine(bool bNeedChecked)
{
if (bNeedChecked && !m_arStates.top().m_bNeedBreakLine)
@ -229,7 +234,7 @@ void CMDWriter::WriteBreakLine(bool bNeedChecked)
m_arStates.top().m_wsLastSpecialString.clear();
}
void CMDWriter::EneteredBold()
void CMDWriter::EnteredBold()
{
m_arStates.top().m_bBold = true;
}
@ -244,7 +249,7 @@ bool CMDWriter::IsBold()
return m_arStates.top().m_bBold;
}
void CMDWriter::EneteredItalic()
void CMDWriter::EnteredItalic()
{
m_arStates.top().m_bItalic = true;
}
@ -259,7 +264,7 @@ bool CMDWriter::IsItalic()
return m_arStates.top().m_bItalic;
}
void CMDWriter::EneteredStrike()
void CMDWriter::EnteredStrike()
{
m_arStates.top().m_bStrike = true;
}

View File

@ -65,17 +65,19 @@ public:
XmlString* GetCurrentDocument() const override;
bool SupportNestedTables() const override;
void WriteBreakLine(bool bNeedChecked = true);
void EneteredBold();
void EnteredBold();
void OutBold();
bool IsBold();
void EneteredItalic();
void EnteredItalic();
void OutItalic();
bool IsItalic();
void EneteredStrike();
void EnteredStrike();
void OutStrike();
bool IsStrike();

View File

@ -1261,6 +1261,11 @@ XmlString* COOXMLWriter::GetCurrentDocument() const
return m_arStates.top().m_pCurrentDocument;
}
bool COOXMLWriter::SupportNestedTables() const
{
return true;
}
const NSCSS::NSProperties::CPage* COOXMLWriter::GetPageData() const
{
return &m_oPageData;

View File

@ -200,6 +200,8 @@ public:
XmlString& GetWebSettingsXml();
XmlString* GetCurrentDocument() const override;
bool SupportNestedTables() const override;
const NSCSS::NSProperties::CPage* GetPageData() const;
NSFonts::IApplicationFonts* GetFonts();

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%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;
std::wstring res = (braces ? L"{" : L"") + sstream.str() + (braces ? L"}" : L"");
return res;
}

View File

@ -366,6 +366,9 @@ void pptx_text_context::Impl::ApplyListProperties(odf_reader::paragraph_format_p
if (list_properties)
{
if(list_properties->text_min_label_width_.has_value() && list_properties->text_min_label_width_->get_value() > 0 && (!propertiesOut.fo_text_indent_.has_value() || (propertiesOut.fo_text_indent_.has_value() && (propertiesOut.fo_text_indent_->get_length().get_value() == 0 || (propertiesOut.fo_margin_left_.has_value() && propertiesOut.fo_margin_left_->get_length().get_value() == 0 && propertiesOut.fo_text_indent_->get_length().get_value() < 0)))))
propertiesOut.fo_text_indent_ = list_properties->text_min_label_width_;
if (list_properties->text_space_before_)
{
double spaceBeforeTwip;
@ -386,9 +389,6 @@ void pptx_text_context::Impl::ApplyListProperties(odf_reader::paragraph_format_p
else if(!propertiesOut.fo_margin_left_)
propertiesOut.fo_margin_left_ = odf_types::length(0, odf_types::length::pt);
if(list_properties->text_min_label_width_.has_value() && list_properties->text_min_label_width_->get_value() > 0 && (!propertiesOut.fo_text_indent_.has_value() || (propertiesOut.fo_text_indent_.has_value() && propertiesOut.fo_text_indent_->get_length().get_value() == 0)))
propertiesOut.fo_text_indent_ = list_properties->text_min_label_width_;
if (list_properties->fo_width_)
{

View File

@ -535,7 +535,7 @@ void style_tab_stop::docx_convert(oox::docx_conversion_context & Context, bool c
length def_tab = length(1.0, length::cm);// в ms значение 0.8 не корректно оО
//double tab_pos_offset = (!Context.get_paragraph_state() || Context.is_table_content()) ? margin_left : 0;
double tab_pos_offset = (!Context.get_paragraph_state() || Context.is_table_content()) ? margin_left : 0;
double tab_pos = 20.0 * style_position_.get_value_unit(length::pt);
double min_tab_pos = 20.0 * def_tab.get_value_unit(length::pt) ;
@ -586,15 +586,29 @@ void style_tab_stop::docx_convert(oox::docx_conversion_context & Context, bool c
}
}
if( style_type_.is_initialized() && style_type_->get_type() == style_type::Right )
if( style_type_.is_initialized() )
{
double available_width = PageWidthTwips - LeftPageMarginTwips - RightPageMarginTwips;
if ( available_width > 0 )
switch ( style_type_->get_type() )
{
tab_pos = available_width;
case style_type::Right:
{
int total_tabs = Context.get_tabs_context().tabs.size();
if( total_tabs <= 1 )
{
tab_pos += tab_pos_offset;
}
break;
}
}
}
double available_width = PageWidthTwips - LeftPageMarginTwips - RightPageMarginTwips;
if ( available_width > 0 && tab_pos > available_width )
{
tab_pos = available_width;
}
_pPr << L" w:val=\"" << val << "\"";
_pPr << L" w:pos=\"" << static_cast<int>(tab_pos) << "\"";

View File

@ -973,7 +973,7 @@ void table_table_cell::xlsx_convert(oox::xlsx_conversion_context & Context)
}
if (attr.office_value_type_)
{
if (attr.office_value_type_.get() != num_format_type)
if (attr.office_value_type_.get() != num_format_type && num_format_type != office_value_type::Custom)
{// reset from cell
num_format_type = attr.office_value_type_->get_type();
num_format.clear();

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,12 @@ 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();
pObj->SetRef(nFontRef.num, nFontRef.gen);
}
m_pWriter->GetDocument()->CreateFontEmbedded(wsFileName, 0, sFontKey, static_cast<PdfWriter::EFontType>(gfxFont->getType()), pObj, mCodeToWidth, mCodeToUnicode, mCodeToGID);
}
}
@ -4400,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())
@ -4426,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();
@ -4465,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

@ -2267,4 +2267,13 @@ namespace PdfWriter
{
m_pCurPage->ClearContentFull(m_pXref);
}
CObjectBase* CDocument::FindObjByID(unsigned int nObjectId)
{
TXrefEntry* pRes = NULL;
if (m_pLastXref)
pRes = m_pLastXref->GetEntryByObjectId(nObjectId);
if (!pRes)
pRes = m_pXref->GetEntryByObjectId(nObjectId);
return pRes? pRes->pObject : NULL;
}
}

View File

@ -226,6 +226,7 @@ namespace PdfWriter
void RemoveObj(CObjectBase* pObj);
void SetEncryption(CEncryptDict* pEncrypt, CObjectBase* pID);
void AddNameTree(CStringObject* pName, CDestination* pDest);
CObjectBase* FindObjByID(unsigned int nObjectId);
private:
char* GetTTFontTag();

View File

@ -142,4 +142,14 @@ namespace PdfWriter
return pair.first;
return 0;
}
CObjectBase* CFontEmbedded::GetObj()
{
if (m_pObj->GetType() != object_type_UNKNOWN)
return m_pObj;
return new PdfWriter::CProxyObject(m_pObj);
}
CObjectBase* CFontEmbedded::GetObj2()
{
return m_pObj;
}
}

View File

@ -71,7 +71,8 @@ namespace PdfWriter
unsigned int GetWidth(unsigned short ushCode);
unsigned int EncodeUnicode(const unsigned int& unGID, const unsigned int& unUnicode);
unsigned int EncodeGID(const unsigned int& unGID);
CObjectBase* GetObj() { return m_pObj; }
CObjectBase* GetObj();
CObjectBase* GetObj2();
const char* GetFontKey() const { return m_sFontKey.c_str(); }
void UpdateKey(const std::string& sFontKey) { m_sFontKey = sFontKey; }

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

@ -891,10 +891,7 @@ namespace PdfWriter
while (pXref)
{
if (pXref->m_arrEntries.size() + pXref->m_unStartOffset <= nObjectId)
return NULL;
if (pXref->m_unStartOffset <= nObjectId)
if (pXref->m_unStartOffset <= nObjectId && pXref->m_arrEntries.size() + pXref->m_unStartOffset > nObjectId)
{
for (unsigned int unIndex = 0, nCount = pXref->m_arrEntries.size(); unIndex < nCount; unIndex++)
{

View File

@ -1818,7 +1818,7 @@ namespace PdfWriter
return false;
m_vWords.push_back(pText);
double dShift = (pLastText->m_dCurX - dX) * 1000 / dSize;
double dShift = (pLastText->m_dCurX - dX) * 1000 / (dSize ? dSize : 1);
m_vShifts.push_back(dShift);
return true;
}

View File

@ -91,7 +91,7 @@ namespace PdfWriter
}
}
const char *sKey = m_pFonts->GetKey(pEmbedded ? pEmbedded->GetObj() : pFont);
const char *sKey = m_pFonts->GetKey(pEmbedded ? pEmbedded->GetObj2() : pFont);
if (!sKey)
{
// если фонт не зарегистрирован в ресурсах, тогда регистрируем его
@ -108,7 +108,7 @@ namespace PdfWriter
pPointer = (char*)StrCpy(sFontName, "F", pEndPointer);
ItoA(pPointer, m_unFontsCount, pEndPointer);
m_pFonts->Add(sFontName, pEmbedded ? pEmbedded->GetObj() : pFont);
sKey = m_pFonts->GetKey(pEmbedded ? pEmbedded->GetObj() : pFont);
sKey = m_pFonts->GetKey(pEmbedded ? pEmbedded->GetObj2() : pFont);
if (sEmbeddedFontKey)
pEmbedded->UpdateKey(sKey);
}

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) {

View File

@ -711,7 +711,7 @@ TEST_F(CPdfFileTest, EditPdfSign)
TEST_F(CPdfFileTest, PrintPdf)
{
//GTEST_SKIP();
GTEST_SKIP();
LoadFromFile(wsDstFile);