Fix bugs in html to OOXML/Markdown conversion

This commit is contained in:
Kirill Polyakov
2026-04-15 21:14:30 +03:00
parent 2c35b5f422
commit 75b3665ad5
9 changed files with 822 additions and 201 deletions

View File

@ -1,6 +1,5 @@
#include "CDocumentStyle.h"
#include <iostream>
#include <unordered_set>
#include <wchar.h>
#include <math.h>

View File

@ -29,8 +29,6 @@ namespace HTML
#define UNKNOWN_TAG GumboTag::GUMBO_TAG_UNKNOWN
#define HtmlTag GumboTag
#define TAGS_COUNT 32
const static std::map<std::wstring, HtmlTag> m_HTML_TAGS
{
ADD_TAG(L"a", A),
@ -344,6 +342,8 @@ void CHTMLReader::Clear()
if (nullptr != m_pTableElement)
delete m_pTableElement;
ClearStopTags();
}
bool CHTMLReader::InitOOXMLTags(THTMLParameters* pParametrs)
@ -382,6 +382,13 @@ bool CHTMLReader::InitOOXMLTags(THTMLParameters* pParametrs)
std::vector<NSCSS::CNode>& arSelectors)
{ GetSubClass(oReader, arSelectors, m_oCSSCalculator); };
oExternalData.ReadInside = [this](XmlUtils::CXmlLiteReader& oReader,
std::vector<NSCSS::CNode>& arSelectors)
{ return ReadInside(oReader, arSelectors); };
oExternalData.AddStopTag = [this](const std::wstring& wsTag){ AddStopTag(wsTag); };
oExternalData.ClearStopTags = [this]{ ClearStopTags(); };
oExternalData.m_pWriter = pWriter;
m_pTableElement = new COOXMLTable(oExternalData);
@ -418,11 +425,18 @@ bool CHTMLReader::InitMDTags(TMarkdownParameters* pParametrs)
oExternalData.ReadStream = [this](XmlUtils::CXmlLiteReader& oReader,
std::vector<NSCSS::CNode>& arSelectors)
{ return ReadStream(oReader, arSelectors, true); };
{ return ReadStream(oReader, arSelectors, true); };
oExternalData.GetSubClass = [this](XmlUtils::CXmlLiteReader& oReader,
std::vector<NSCSS::CNode>& arSelectors)
{ GetSubClass(oReader, arSelectors, m_oCSSCalculator); };
{ GetSubClass(oReader, arSelectors, m_oCSSCalculator); };
oExternalData.ReadInside = [this](XmlUtils::CXmlLiteReader& oReader,
std::vector<NSCSS::CNode>& arSelectors)
{ return ReadInside(oReader, arSelectors); };
oExternalData.AddStopTag = [this](const std::wstring& wsTag){ AddStopTag(wsTag); };
oExternalData.ClearStopTags = [this]{ ClearStopTags(); };
oExternalData.m_pWriter = pWriter;
@ -665,6 +679,9 @@ bool CHTMLReader::ReadStream(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCS
do
{
if (!m_arStopTags.empty() && m_arStopTags.end() != m_arStopTags.find(oReader.GetName()))
return bResult;
if (ReadInside(oReader, arSelectors))
bResult = true;
} while(oReader.ReadNextSiblingNode2(nDeath));
@ -688,10 +705,10 @@ bool CHTMLReader::ReadInside(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCS
GetSubClass(oReader, arSelectors, m_oCSSCalculator);
bool bResult{false};
const HtmlTag eHtmlTag{GetHtmlTag(wsName)};
bool bResult{false};
switch(eHtmlTag)
{
case HTML_TAG(A):
@ -1002,6 +1019,16 @@ bool CHTMLReader::ReadTable(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS
return true;
}
void CHTMLReader::AddStopTag(const std::wstring& wsTag)
{
m_arStopTags.insert(wsTag);
}
void CHTMLReader::ClearStopTags()
{
m_arStopTags.clear();
}
inline std::wstring GetArgumentValue(XmlUtils::CXmlLiteReader& oLiteReader, const std::wstring& wsArgumentName, const std::wstring& wsDefaultValue)
{
if (!oLiteReader.MoveToFirstAttribute())

View File

@ -1,9 +1,6 @@
#ifndef HTMLREADER_H
#define HTMLREADER_H
#include <queue>
#include <unordered_map>
#include "../Common/3dParty/html/css/src/CCssCalculator.h"
#include "../DesktopEditor/xml/include/xmlutils.h"
@ -14,6 +11,8 @@
#include "Tags/HTMLTags.h"
#include "Table.h"
#include <set>
namespace HTML
{
class CHTMLReader
@ -32,6 +31,8 @@ class CHTMLReader
CTableElement* m_pTableElement; // Table Converter
std::set<std::wstring> m_arStopTags;
THTMLTags m_oTags;
public:
CHTMLReader();
@ -81,6 +82,9 @@ private:
bool ReadText(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors);
bool ReadTable(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors);
void AddStopTag(const std::wstring& wsTag);
void ClearStopTags();
};
}

View File

@ -114,9 +114,23 @@ namespace HTML
// }
// }
ITableElementCell::ITableElementCell(size_t unColspan)
: m_unColspan(unColspan)
{}
CTableElementCell::CTableElementCell(bool bIsFilling)
: m_eType{(bIsFilling) ? ETableElement::FillingCell : ETableElement::Cell}
void ITableElementCell::SetColspan(size_t unColspan)
{
if (0 < unColspan)
m_unColspan = unColspan;
}
size_t ITableElementCell::GetColspan() const
{
return m_unColspan;
}
CTableElementCell::CTableElementCell(bool bIsFilling, size_t unColspan)
: ITableElementCell(unColspan), m_eType{(bIsFilling) ? ETableElement::FillingCell : ETableElement::Cell}
{}
CTableElementCell::~CTableElementCell()
@ -132,6 +146,16 @@ void CTableElementCell::IsFlatTable()
m_eType = ETableElement::FlatTable;
}
CTableElementCell* CTableElementCell::CreateCell(const size_t& unColspan)
{
return new CTableElementCell(false, unColspan);
}
CTableElementCell* CTableElementCell::CreateFillingCell(const size_t& unColspan)
{
return new CTableElementCell(true, unColspan);
}
CTableMatrix::CTableMatrix()
{}
@ -157,7 +181,7 @@ void CTableMatrix::Clear()
m_arCells.clear();
}
bool CTableMatrix::SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell)
bool CTableMatrix::SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell, bool bAutoClean)
{
if (nullptr == pCell)
return false;
@ -185,7 +209,12 @@ bool CTableMatrix::SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElemen
}
if (nullptr != m_arCells[unRowIndex][unColumnIndex])
delete m_arCells[unRowIndex][unColumnIndex];
{
pCell->SetColspan(m_arCells[unRowIndex][unColumnIndex]->GetColspan());
if (bAutoClean)
delete m_arCells[unRowIndex][unColumnIndex];
}
m_arCells[unRowIndex][unColumnIndex] = pCell;
@ -255,6 +284,17 @@ const ITableElementCell* CTableMatrix::GetCell(size_t unRowIndex, size_t unColum
return m_arCells[unRowIndex][unColumnIndex];
}
ITableElementCell* CTableMatrix::GetCell(size_t unRowIndex, size_t unColumnIndex)
{
if (unRowIndex >= m_arCells.size())
return nullptr;
if (unColumnIndex >= m_arCells[unRowIndex].size())
return nullptr;
return m_arCells[unRowIndex][unColumnIndex];
}
CTableElement::CTableElement(TExternalTableData &oExternalData)
: m_pCaption(nullptr), m_oExternalData(oExternalData)
{}
@ -282,11 +322,6 @@ void CTableElement::Clear()
m_oFoother.Clear();
}
ETableElement CTableElement::GetType() const
{
return ETableElement::Table;
}
bool CTableElement::Empty() const
{
return m_oHeader.Empty() && m_oBody.Empty() && m_oFoother.Empty();
@ -351,6 +386,33 @@ bool MoveToNextTableCell(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::C
return false;
}
CTableContainer::CTableContainer()
: ITableElementCell(1)
{}
CTableContainer::~CTableContainer()
{
for (const CTableElement* pTable : m_arTables)
if (nullptr != pTable)
delete pTable;
}
ETableElement CTableContainer::GetType() const
{
return ETableElement::TableContainer;
}
void CTableContainer::AddTable(CTableElement* pTable)
{
if (nullptr != pTable)
m_arTables.push_back(pTable);
}
const std::vector<CTableElement*>& CTableContainer::GetTables() const
{
return m_arTables;
}
CTableCol::CTableCol(NSCSS::CNode& oTableColNode)
: m_pStyle(oTableColNode.m_pCompiledStyle)
{

View File

@ -29,28 +29,46 @@ enum class ETableElement
FillingCell,
Cell,
FlatTable,
Table
TableContainer
};
// !TODO!:: At the moment, I don't really like the implementation where there is a table inside a cell,
// or when there are multiple nested tables inside a cell
// Perhaps it's worth doing it as follows:
// ITableCell -> CTableCell (A regular cell that does not contain nested tables)
// -> CTableCellContainer (Stores anything (like default, but has a container for storing nested tables))
class ITableElementCell
{
size_t m_unColspan;
protected:
ITableElementCell(size_t unColspan);
public:
ITableElementCell() = default;
virtual ~ITableElementCell() = default;
virtual ETableElement GetType() const = 0;
void SetColspan(size_t unColspan);
size_t GetColspan() const;
};
class CTableElementCell : public ITableElementCell
{
ETableElement m_eType;
protected:
CTableElementCell(bool bIsFilling, size_t unColspan);
public:
CTableElementCell(bool bIsFilling = false);
virtual ~CTableElementCell();
ETableElement GetType() const override;
void IsFlatTable();
static CTableElementCell* CreateCell(const size_t& unColspan = 1);
static CTableElementCell* CreateFillingCell(const size_t& unColspan = 1);
};
class ITag;
@ -60,9 +78,15 @@ struct TExternalTableData
{
typedef std::function<bool(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors)> FuncReadStream;
typedef std::function<void(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors)> FuncGetSubClass;
typedef std::function<bool(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors)> FuncReadInside;
typedef std::function<void(const std::wstring& wsTag)> FuncAddStopTag;
typedef std::function<void()> FuncClearStopTags;
FuncReadStream ReadStream;
FuncGetSubClass GetSubClass;
FuncReadStream ReadStream;
FuncGetSubClass GetSubClass;
FuncReadInside ReadInside;
FuncAddStopTag AddStopTag;
FuncClearStopTags ClearStopTags;
IWriter* m_pWriter{nullptr};
};
@ -82,7 +106,7 @@ public:
void Clear();
bool SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell);
bool SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell, bool bAutoClean = true);
void NormalizeNumberColumns(size_t unNumberColumns);
@ -94,7 +118,9 @@ public:
const Table& GetMatrixCells() const;
Table& GetMatrixCells();
const ITableElementCell* GetCell(size_t unRowIndex, size_t unColumnIndex) const;
ITableElementCell* GetCell(size_t unRowIndex, size_t unColumnIndex);
protected:
Table m_arCells;
};
@ -131,7 +157,7 @@ private:
UINT m_unTotalSpans;
};
class CTableElement : public ITableElementCell
class CTableElement
{
protected:
CTableElement(TExternalTableData &oExternalData);
@ -140,8 +166,6 @@ public:
void Clear();
ETableElement GetType() const override;
bool Empty() const;
bool HaveCaption() const;
@ -169,6 +193,31 @@ protected:
inline bool ParseMatrix(XmlUtils::CXmlLiteReader& oReader, CTableMatrix* pMatrix, size_t& unRowIndex, size_t& unColumnIndex);
};
class CTableContainer : public ITableElementCell
{
public:
CTableContainer();
virtual ~CTableContainer();
ETableElement GetType() const override;
void AddTable(CTableElement* pTable);
const std::vector<CTableElement*>& GetTables() const;
private:
std::vector<CTableElement*> m_arTables;
};
struct TTableData
{
CTableMatrix m_oHeader;
CTableMatrix m_oBody;
CTableMatrix m_oFoother;
XmlString *m_pCaption;
std::vector<CTableColgroup*> m_arColgroups;
};
template<typename T>
inline bool CTableElement::ParseTable(XmlUtils::CXmlLiteReader& oReader, T* pTable)
{
@ -208,7 +257,21 @@ inline bool CTableElement::ParseMatrix(XmlUtils::CXmlLiteReader& oReader, CTable
if (nullptr == pNewTable)
return false;
pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, pNewTable);
ITableElementCell* pTableElement{pMatrix->GetCell(unRowIndex - 1, unColumnIndex - 1)};
CTableContainer *pContainer{nullptr};
if (nullptr != pTableElement && ETableElement::TableContainer == pTableElement->GetType())
pContainer = dynamic_cast<CTableContainer*>(pTableElement);
else
{
pContainer = new CTableContainer();
pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, pContainer);
}
if (nullptr == pContainer)
return false;
pContainer->AddTable(pNewTable);
ParseTable<T>(oReader, pNewTable);
}
@ -233,26 +296,26 @@ inline bool CTableElement::ParseMatrix(XmlUtils::CXmlLiteReader& oReader, CTable
while (pMatrix->IsFillingCell(unRowIndex - 1, unColumnIndex - 1))
++unColumnIndex;
if (!pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, new CTableElementCell()))
return false;
size_t unRowspan{1}, unColspan{1};
if (oReader.MoveToFirstAttribute())
{
do
{
if (L"rowspan" == oReader.GetName())
{
const int nRowSpan{NSStringFinder::ToInt(oReader.GetText(), 1)};
for (int nRow = 1; nRow < nRowSpan; ++nRow)
pMatrix->SetCell(unRowIndex + nRow - 1, unColumnIndex - 1, new CTableElementCell(true));
}
unRowspan = NSStringFinder::ToInt(oReader.GetText(), 1);
else if (L"colspan" == oReader.GetName())
unColumnIndex += NSStringFinder::ToInt(oReader.GetText(), 1) - 1;
unColspan = NSStringFinder::ToInt(oReader.GetText(), 1);
}while (oReader.MoveToNextAttribute());
oReader.MoveToElement();
}
if (!pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, CTableElementCell::CreateCell(unColspan)))
return false;
for (size_t unRow = 1; unRow < unRowspan; ++unRow)
pMatrix->SetCell(unRowIndex + unRow - 1, unColumnIndex - 1, CTableElementCell::CreateFillingCell(unColspan));
const int nDepth{oReader.GetDepth()};
while (oReader.ReadNextSiblingNode(nDepth))
ParseMatrix<T>(oReader, pMatrix, unRowIndex, unColumnIndex);

View File

@ -15,8 +15,6 @@
#include "../../DesktopEditor/graphics/pro/Graphics.h"
#include "../../DesktopEditor/raster/BgraFrame.h"
#include <queue>
namespace HTML
{
template<>
@ -637,9 +635,9 @@ void CCodeTag<CMDWriter>::Close()
m_pWriter->OutCode();
}
bool IsTable(const ITableElementCell* pCell)
bool IsTableContainer(const ITableElementCell* pCell)
{
return nullptr != pCell && ETableElement::Table == pCell->GetType();
return nullptr != pCell && ETableElement::TableContainer == pCell->GetType();
}
CMarkdownTable::CMarkdownTable(TExternalTableData &oExternalData)
@ -665,10 +663,10 @@ void CMarkdownTable::Normalize()
size_t unMaxColumns{m_oBody.GetColumnSize()};
if (!m_oHeader.Empty())
unMaxColumns = std::max(unMaxColumns, m_oHeader.GetColumnSize());
unMaxColumns = (std::max)(unMaxColumns, m_oHeader.GetColumnSize());
if (!m_oFoother.Empty())
unMaxColumns = std::max(unMaxColumns, m_oFoother.GetColumnSize());
unMaxColumns = (std::max)(unMaxColumns, m_oFoother.GetColumnSize());
#define NORMALIZE_NUMBER_COLUMN(table_variable)\
if (!table_variable.Empty() && unMaxColumns != table_variable.GetColumnSize())\
@ -679,7 +677,8 @@ void CMarkdownTable::Normalize()
NORMALIZE_NUMBER_COLUMN(m_oFoother);
}
typedef std::queue<std::pair<std::pair<size_t, size_t>, NSStringUtils::CStringBuilder*>> NestedCells;
typedef std::map<std::pair<size_t, size_t>, NSStringUtils::CStringBuilder*> NestedCells;
inline void WriteRowStart(CMDWriter& oWriter)
{
@ -697,21 +696,47 @@ inline void WriteCellSeparator(CMDWriter& oWriter)
oWriter.WriteOpenSpecialString(L" | ");
}
void ReadNestedCells(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors, TCurentTablePosition& oPosition, NestedCells& arNestedCells, const Table& oCells, CMDWriter* pWriter, const TExternalTableData& oExternalTableData)
// TODO: Instead of handling the different parsing behavior for nested tables
// (when a cell contains both a table and data),
// you can set a specific parsing method in HTMLReader and replace
// it with the desired one. As a result,
// everything except the table will be parsed using HTMLReader,
// while reading the nested table will be done using the newly set method.
void ReadNestedCells(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors, TCurentTablePosition& oPosition, NestedCells& arNestedCells, const Table& oCells, CMDWriter* pWriter, const TExternalTableData& oExternalTableData, NSStringUtils::CStringBuilder& oIntermediateData);
void ReadNestedTable(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors, TCurentTablePosition& oPosition, NestedCells& arNestedCells, const Table& oCells, CMDWriter* pWriter, const TExternalTableData& oExternalTableData, NSStringUtils::CStringBuilder& oIntermediateData)
{
TCurentTablePosition oNestedPosition{oPosition};
oNestedPosition.m_unStartRowIndex = oPosition.m_unRowIndex;
oNestedPosition.m_unStartColumnIndex = oPosition.m_unColumnIndex;
std::vector<NSCSS::CNode> arNestedSelectors{arSelectors.back()};
ReadNestedCells(oReader, arNestedSelectors, oNestedPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
oPosition.m_unRowIndex = oNestedPosition.m_unRowIndex;
}
void ReadNestedCells(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode>& arSelectors, TCurentTablePosition& oPosition, NestedCells& arNestedCells, const Table& oCells, CMDWriter* pWriter, const TExternalTableData& oExternalTableData, NSStringUtils::CStringBuilder& oIntermediateData)
{
const int nDepth{oReader.GetDepth()};
while(oReader.ReadNextSiblingNode(nDepth))
while(oReader.ReadNextSiblingNode2(nDepth))
{
const std::wstring wsName = oReader.GetName();
oExternalTableData.GetSubClass(oReader, arSelectors);
if (L"td" == wsName || L"th" == wsName)
{
const ITableElementCell* pCell{oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex]};
if ((oPosition.m_unRowIndex != oPosition.m_unStartRowIndex || oPosition.m_unColumnIndex != oPosition.m_unStartColumnIndex) &&
ETableElement::FlatTable == oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex]->GetType())
(nullptr != pCell && ETableElement::FlatTable == pCell->GetType()))
{
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData);
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
continue;
}
@ -719,14 +744,33 @@ void ReadNestedCells(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode
if (nullptr != pCellData)
{
pCellData->Write(oIntermediateData);
oIntermediateData.Clear();
pWriter->SetDataOutput(pCellData);
oExternalTableData.ReadStream(oReader, arSelectors);
if (oPosition.m_unColumnIndex != oCells[oPosition.m_unRowIndex].size() - 1)
WriteCellSeparator(*pWriter);
pWriter->RevertDataOutput();
}
arNestedCells.push(std::make_pair(std::make_pair(oPosition.m_unRowIndex, oPosition.m_unColumnIndex), pCellData));
while (nullptr != oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex])
{
if (ETableElement::FillingCell == oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex]->GetType())
oPosition.m_unColumnIndex += oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex]->GetColspan();
else
break;
}
arNestedCells[{oPosition.m_unRowIndex, oPosition.m_unColumnIndex}] = pCellData;
if (oReader.MoveToFirstAttribute())
{
do
{
if (L"colspan" == oReader.GetName())
oPosition.m_unColumnIndex += NSStringFinder::ToInt(oReader.GetText(), 1) - 1;
}while (oReader.MoveToNextAttribute());
oReader.MoveToElement();
}
//READ and add to nested cells
++oPosition.m_unColumnIndex;
@ -735,25 +779,47 @@ void ReadNestedCells(XmlUtils::CXmlLiteReader& oReader, std::vector<NSCSS::CNode
{
oPosition.m_unColumnIndex = oPosition.m_unStartColumnIndex;
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData);
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
++oPosition.m_unRowIndex;
}
else if (L"table" == wsName)
{
TCurentTablePosition oNestedPosition{oPosition};
oNestedPosition.m_unStartRowIndex = oPosition.m_unRowIndex;
oNestedPosition.m_unStartColumnIndex = oPosition.m_unColumnIndex;
std::vector<NSCSS::CNode> arNestedSelectors{arSelectors.back()};
ReadNestedCells(oReader, arNestedSelectors, oNestedPosition, arNestedCells, oCells, pWriter, oExternalTableData);
oPosition.m_unRowIndex = oNestedPosition.m_unRowIndex;
}
ReadNestedTable(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
else if (L"caption" == wsName)
continue;
else if (L"tbody" == wsName || L"thead" == wsName || L"tfoot" == wsName)
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
else
ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData);
{
const size_t unCurrentIntermediateDataSize{oIntermediateData.GetCurSize()};
pWriter->SetDataOutput(&oIntermediateData);
oExternalTableData.ReadInside(oReader, arSelectors);
pWriter->RevertDataOutput();
if (unCurrentIntermediateDataSize != oIntermediateData.GetCurSize())
oIntermediateData.WriteString(L" ");
if (L"table" != oReader.GetName())
continue;
ReadNestedTable(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData, oIntermediateData);
pWriter->SetDataOutput(&oIntermediateData);
const int nNewDepth{oReader.GetDepth()};
while (oReader.ReadNextSiblingNode2(nNewDepth - 1))
oExternalTableData.ReadInside(oReader, arSelectors);
pWriter->RevertDataOutput();
if (0 != oIntermediateData.GetCurSize())
{
arNestedCells.rbegin()->second->WriteString(L" ");
arNestedCells.rbegin()->second->Write(oIntermediateData);
oIntermediateData.Clear();
}
}
arSelectors.pop_back();
}
@ -771,7 +837,7 @@ bool CMarkdownTable::Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNo
std::vector<NSCSS::CNode> arTableSelectors{oTableNode};
pWriter->WriteBreakLine();
pWriter->WriteBreakLine(false);
if (HaveCaption())
WriteToStringBuilder(*m_pCaption, *pWriter->GetCurrentDocument());
@ -806,6 +872,18 @@ bool CMarkdownTable::Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNo
WriteCellSeparator(*pWriter);
WriteRowEnd(*pWriter);
WriteRowStart(*pWriter);
for (size_t unColumnIndex = 0; unColumnIndex < m_oBody.GetColumnSize(); ++unColumnIndex)
{
pWriter->WriteString(L"-", true);
if (unColumnIndex != m_oBody.GetColumnSize() - 1)
WriteCellSeparator(*pWriter);
}
WriteRowEnd(*pWriter);
}
//Convert body
ConvertMatrix(oReader, arTableSelectors, m_oBody.GetMatrixCells(), pWriter);
@ -861,46 +939,137 @@ bool CMarkdownTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, std::vecto
{
WriteRowStart(*pWriter);
for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size(); ++unColumnIndex)
for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size();)
{
pTableCell = oMatrix[unRowIndex][unColumnIndex];
if (nullptr != pTableCell && ETableElement::FillingCell != pTableCell->GetType())
{
if (!arNestedCells.empty() && arNestedCells.front().first == std::make_pair(unRowIndex, unColumnIndex))
if (!arNestedCells.empty())
{
if (nullptr != arNestedCells.front().second)
NestedCells::const_iterator itElement{arNestedCells.find({unRowIndex, unColumnIndex})};
if (arNestedCells.end() != itElement)
{
m_oExternalData.m_pWriter->GetCurrentDocument()->Write(*arNestedCells.front().second);
delete arNestedCells.front().second;
if (nullptr != itElement->second)
{
m_oExternalData.m_pWriter->GetCurrentDocument()->Write(*itElement->second);
delete itElement->second;
if (oMatrix[unRowIndex].size() - 1 != unColumnIndex)
WriteCellSeparator(*pWriter);
}
arNestedCells.erase(itElement);
++unColumnIndex;
continue;
}
}
MoveToNextTableCell(oReader, arSelectors, arDepths, m_oExternalData.GetSubClass);
if (ETableElement::FlatTable == pTableCell->GetType())
{
TCurentTablePosition oPosition{unRowIndex, unColumnIndex, unRowIndex, unColumnIndex};
std::vector<NSCSS::CNode> arNestedSelectors;
const int nDepth{oReader.GetDepth()};
NSStringUtils::CStringBuilder oTempData(100);
bool bSetedDataOutput{false};
const size_t unDepthSize{arDepths.size()};
arDepths.push(oReader.GetDepth());
m_oExternalData.AddStopTag(L"table");
NSStringUtils::CStringBuilder oIntermediateData(100);
while(oReader.ReadNextSiblingNode2(nDepth))
{
if (L"table" == oReader.GetName())
{
if (bSetedDataOutput)
{
if (!arNestedCells.empty() && 0 != oTempData.GetCurSize())
{
arNestedCells.rbegin()->second->WriteString(L" ");
arNestedCells.rbegin()->second->Write(oTempData);
oTempData.Clear();
}
pWriter->RevertDataOutput();
bSetedDataOutput = false;
}
m_oExternalData.GetSubClass(oReader, arNestedSelectors);
ReadNestedCells(oReader, arNestedSelectors, oPosition, arNestedCells, oMatrix, pWriter, m_oExternalData, oIntermediateData);
oPosition.m_unColumnIndex = oPosition.m_unStartColumnIndex;
oPosition.m_unStartRowIndex = oPosition.m_unRowIndex;
if (0 != oTempData.GetCurSize() && !arNestedCells.empty())
{
oTempData.WriteString(L" ");
arNestedCells.begin()->second->WriteBefore(oTempData);
oTempData.Clear();
}
if (0 != oIntermediateData.GetCurSize() && !arNestedCells.empty())
{
arNestedCells.rbegin()->second->WriteString(L" ");
arNestedCells.rbegin()->second->Write(oIntermediateData);
oIntermediateData.Clear();
}
}
else
{
if (!bSetedDataOutput)
{
pWriter->SetDataOutput(&oTempData);
bSetedDataOutput = true;
}
m_oExternalData.ReadInside(oReader, arSelectors);
}
}
while (arDepths.size() > unDepthSize)
{
arDepths.pop();
arSelectors.pop_back();
}
m_oExternalData.ClearStopTags();
if (bSetedDataOutput)
{
if (!arNestedCells.empty())
{
arNestedCells.rbegin()->second->WriteString(L" ");
arNestedCells.rbegin()->second->Write(oTempData);
}
pWriter->RevertDataOutput();
}
arNestedCells.pop();
}
else
{
MoveToNextTableCell(oReader, arSelectors, arDepths, m_oExternalData.GetSubClass);
m_oExternalData.ReadStream(oReader, arSelectors);
if (oMatrix[unRowIndex].size() - 1 != unColumnIndex)
WriteCellSeparator(*pWriter);
if (ETableElement::FlatTable == pTableCell->GetType())
{
TCurentTablePosition oPosition{unRowIndex, unColumnIndex, unRowIndex, unColumnIndex};
std::vector<NSCSS::CNode> arNestedSelectors;
ReadNestedCells(oReader, arNestedSelectors, oPosition, arNestedCells, oMatrix, pWriter, m_oExternalData);
--unColumnIndex;
}
else
{
m_oExternalData.ReadStream(oReader, arSelectors);
if (oMatrix[unRowIndex].size() - 1 != unColumnIndex)
WriteCellSeparator(*pWriter);
}
arSelectors.pop_back();
++unColumnIndex;
}
arSelectors.pop_back();
}
else
{
if (oMatrix[unRowIndex].size() - 1 != unColumnIndex)
WriteCellSeparator(*pWriter);
++unColumnIndex;
}
}
@ -910,8 +1079,13 @@ bool CMarkdownTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, std::vecto
{
WriteRowStart(*pWriter);
for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size() - 1; ++unColumnIndex)
pWriter->WriteString(L"-|-", true);
for (size_t unColumnIndex = 0; unColumnIndex < m_oBody.GetColumnSize(); ++unColumnIndex)
{
pWriter->WriteString(L"-", true);
if (unColumnIndex != m_oBody.GetColumnSize() - 1)
WriteCellSeparator(*pWriter);
}
WriteRowEnd(*pWriter);
}
@ -926,7 +1100,39 @@ Table CMarkdownTable::Flatten(Table&& srcTable)
return {};
const size_t unRows{srcTable.size()};
const size_t unColumns{srcTable[0].size()};
size_t unColumns{0}, unRowColumns{0};
for (const Row& oRow : srcTable)
{
for (ITableElementCell* pCell : oRow)
unRowColumns += (nullptr != pCell) ? ((CTableElementCell*)pCell)->GetColspan() : 1;
unColumns = (std::max)(unColumns, unRowColumns);
unRowColumns = 0;
}
size_t unColspan{1};
for (Row& oRow : srcTable)
{
for (size_t unColumnIndex = 0; unColumnIndex < oRow.size(); ++ unColumnIndex)
{
if (nullptr == oRow[unColumnIndex])
continue;
unColspan = oRow[unColumnIndex]->GetColspan();
if (1 != unColspan)
oRow.insert(oRow.begin() + unColumnIndex + 1, unColspan - 1, nullptr);
unColumnIndex += unColspan - 1;
}
if (oRow.size() != unColumns)
oRow.resize(unColumns);
}
std::vector<std::vector<TElementInfo>> oInfos{unRows, std::vector<TElementInfo>{unColumns}};
@ -941,8 +1147,8 @@ Table CMarkdownTable::Flatten(Table&& srcTable)
{
for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex)
{
arRowHeights[unRowIndex] = (std::max)(arRowHeights[unRowIndex], oInfos[unRowIndex][unColumnIndex].unRows);
arColumnWidths[unColumnIndex] = (std::max)(arColumnWidths[unColumnIndex], oInfos[unRowIndex][unColumnIndex].unColumns);
arRowHeights[unRowIndex] = (std::max)(arRowHeights[unRowIndex], oInfos[unRowIndex][unColumnIndex].m_unRows);
arColumnWidths[unColumnIndex] = (std::max)(arColumnWidths[unColumnIndex], oInfos[unRowIndex][unColumnIndex].m_unColumns);
}
}
@ -971,68 +1177,229 @@ Table CMarkdownTable::Flatten(Table&& srcTable)
if (nullptr == pCell)
continue;
const TElementInfo oInfo{oInfos[unRowIndex][unColumnIndex]};
unBaseRow = arRowStart[unRowIndex];
unBaseColumn = arColumnStart[unColumnIndex];
if (!IsTable(pCell))
if (!IsTableContainer(pCell))
{
oResult[unBaseRow][unBaseColumn] = pCell;
pCell = nullptr;
continue;
}
CMarkdownTable *pTable{dynamic_cast<CMarkdownTable*>(pCell)};
CTableContainer* pContainer{dynamic_cast<CTableContainer*>(pCell)};
std::vector<CTableElement*> arTables{pContainer->GetTables()};
size_t unCurrentRowOffset{0};
pCell = nullptr;
Table& oChild{pTable->m_oBody.GetMatrixCells()};
Table oFlatChild{Flatten(std::move(oChild))};
for (CTableElement* pTable : arTables)
{
TElementInfo oSubInfo{ComputeInfo(pTable)};
for (size_t unChildRowIndex = 0; unChildRowIndex < oInfo.unRows; ++unChildRowIndex)
for (size_t unChildColumnIndex = 0; unChildColumnIndex < oInfo.unColumns; ++unChildColumnIndex)
oResult[unBaseRow + unChildRowIndex][unBaseColumn + unChildColumnIndex] = oFlatChild[unChildRowIndex][unChildColumnIndex];
Table& oChild{dynamic_cast<CMarkdownTable*>(pTable)->m_oBody.GetMatrixCells()};
Table oFlatChild{Flatten(std::move(oChild))};
CTableElementCell* pFlatCell{dynamic_cast<CTableElementCell*>(oResult[unBaseRow][unBaseColumn])};
for (size_t unChildRowIndex = 0; unChildRowIndex < oSubInfo.m_unRows; ++unChildRowIndex)
for (size_t unChildColumnIndex = 0; unChildColumnIndex < oSubInfo.m_unColumns; ++unChildColumnIndex)
oResult[unBaseRow + unCurrentRowOffset + unChildRowIndex][unBaseColumn + unChildColumnIndex] = oFlatChild[unChildRowIndex][unChildColumnIndex];
if(nullptr != pFlatCell)
pFlatCell->IsFlatTable();
CTableElementCell* pFlatCell{dynamic_cast<CTableElementCell*>(oResult[unBaseRow + unCurrentRowOffset][unBaseColumn])};
delete pTable;
if(nullptr != pFlatCell)
pFlatCell->IsFlatTable();
unCurrentRowOffset += oSubInfo.m_unRows;
}
delete pContainer;
}
}
return oResult;
}
// Table CMarkdownTable::Flatten(Table&& srcTable)
// {
// if (srcTable.empty())
// return {};
// const size_t unRows = srcTable.size();
// // ---------- 1. Разворачиваем строки с учётом colspan ----------
// std::vector<std::vector<ITableElementCell*>> expandedRows;
// expandedRows.reserve(unRows);
// size_t unColumns = 0;
// for (Row& oRow : srcTable)
// {
// std::vector<ITableElementCell*> expandedRow;
// for (ITableElementCell* pCell : oRow)
// {
// if (pCell != nullptr)
// {
// expandedRow.push_back(pCell);
// size_t colspan = pCell->GetColspan();
// // Добавляем (colspan - 1) пустых ячеек справа
// for (size_t i = 1; i < colspan; ++i)
// expandedRow.push_back(nullptr);
// }
// else
// {
// expandedRow.push_back(nullptr);
// }
// }
// unColumns = std::max(unColumns, expandedRow.size());
// expandedRows.push_back(std::move(expandedRow));
// }
// // Приводим все строки к одинаковой длине
// for (auto& row : expandedRows)
// row.resize(unColumns, nullptr);
// // ---------- 2. Вычисляем информацию о размерах каждой ячейки ----------
// std::vector<std::vector<TElementInfo>> oInfos(unRows, std::vector<TElementInfo>(unColumns));
// for (size_t r = 0; r < unRows; ++r)
// for (size_t c = 0; c < unColumns; ++c)
// oInfos[r][c] = ComputeInfo(expandedRows[r][c]);
// // ---------- 3. Вычисляем высоты строк и ширины столбцов ----------
// std::vector<size_t> arRowHeights(unRows, 1);
// std::vector<size_t> arColumnWidths(unColumns, 1);
// for (size_t r = 0; r < unRows; ++r)
// {
// for (size_t c = 0; c < unColumns; ++c)
// {
// arRowHeights[r] = std::max(arRowHeights[r], oInfos[r][c].m_unRows);
// arColumnWidths[c] = std::max(arColumnWidths[c], oInfos[r][c].m_unColumns);
// }
// }
// // ---------- 4. Вычисляем смещения строк и столбцов в результирующей сетке ----------
// std::vector<size_t> arRowStart(unRows + 1, 0);
// std::vector<size_t> arColumnStart(unColumns + 1, 0);
// for (size_t r = 0; r < unRows; ++r)
// arRowStart[r + 1] = arRowStart[r] + arRowHeights[r];
// for (size_t c = 0; c < unColumns; ++c)
// arColumnStart[c + 1] = arColumnStart[c] + arColumnWidths[c];
// const size_t unTotalRows = arRowStart[unRows];
// const size_t unTotalColumns = arColumnStart[unColumns];
// Table oResult(unTotalRows, Row(unTotalColumns, nullptr));
// // ---------- 5. Заполняем результат ----------
// for (size_t r = 0; r < unRows; ++r)
// {
// for (size_t c = 0; c < unColumns; ++c)
// {
// ITableElementCell*& pCell = expandedRows[r][c];
// if (pCell == nullptr)
// continue;
// const size_t unBaseRow = arRowStart[r];
// const size_t unBaseColumn = arColumnStart[c];
// // Обычная ячейка (не контейнер)
// if (!IsTableContainer(pCell))
// {
// oResult[unBaseRow][unBaseColumn] = pCell;
// pCell = nullptr;
// continue;
// }
// // Контейнер таблиц
// CTableContainer* pContainer = dynamic_cast<CTableContainer*>(pCell);
// std::vector<CTableElement*> arTables = pContainer->GetTables();
// pCell = nullptr; // владение передано
// size_t unCurrentRowOffset = 0;
// for (CTableElement* pTableElem : arTables)
// {
// // Предполагаем, что в контейнере лежат только CMarkdownTable
// CMarkdownTable* pMarkdownTable = dynamic_cast<CMarkdownTable*>(pTableElem);
// if (pMarkdownTable == nullptr)
// continue;
// TElementInfo oSubInfo = ComputeInfo(pMarkdownTable);
// Table& oChild = pMarkdownTable->m_oBody.GetMatrixCells();
// Table oFlatChild = Flatten(std::move(oChild));
// for (size_t cr = 0; cr < oSubInfo.m_unRows; ++cr)
// for (size_t cc = 0; cc < oSubInfo.m_unColumns; ++cc)
// oResult[unBaseRow + unCurrentRowOffset + cr][unBaseColumn + cc] = oFlatChild[cr][cc];
// // Если первая ячейка сплющенной таблицы является CTableElementCell, помечаем её
// if (auto* pFlatCell = dynamic_cast<CTableElementCell*>(oResult[unBaseRow + unCurrentRowOffset][unBaseColumn]))
// pFlatCell->IsFlatTable();
// unCurrentRowOffset += oSubInfo.m_unRows;
// }
// delete pContainer;
// }
// }
// return oResult;
// }
TElementInfo CMarkdownTable::ComputeInfo(const ITableElementCell* pCell)
{
if (!IsTable(pCell))
if (IsTableContainer(pCell))
{
const std::vector<CTableElement*> arTables{((CTableContainer*)pCell)->GetTables()};
TElementInfo oElementInfo, oTempInfo;
for (CTableElement* pTable : arTables)
{
oTempInfo = ComputeInfo(pTable);
oElementInfo.m_unColumns = std::max(oElementInfo.m_unColumns, oTempInfo.m_unColumns);
oElementInfo.m_unRows += oTempInfo.m_unRows;
}
return oElementInfo;
}
return {1, 1};
}
TElementInfo CMarkdownTable::ComputeInfo(const CTableElement* pTable)
{
if (nullptr == pTable)
return {1, 1};
const CMarkdownTable *pMarkdownTable{dynamic_cast<const CMarkdownTable*>(pCell)};
const CMarkdownTable *pMarkdownTable{dynamic_cast<const CMarkdownTable*>(pTable)};
if (nullptr == pMarkdownTable)
return {1, 1};
const CTableMatrix *pTable{&pMarkdownTable->m_oBody};
const CTableMatrix *pTableMaxtix{&pMarkdownTable->m_oBody};
const size_t unRows{pTable->GetRowSize()};
const size_t unColumns{pTable->GetColumnSize()};
const size_t unRows{pTableMaxtix->GetRowSize()};
const size_t unColumns{pTableMaxtix->GetColumnSize()};
std::vector<size_t> arRowHeights(unRows, 1);
std::vector<size_t> arColumnWidths(unColumns, 1);
const Table& arCells{pTable->GetMatrixCells()};
const Table& arCells{pTableMaxtix->GetMatrixCells()};
for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex)
{
for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex)
{
TElementInfo oInfo{ComputeInfo(arCells[unRowIndex][unColumnIndex])};
TElementInfo oInfo{1, arCells[unRowIndex][unColumnIndex]->GetColspan()};
arRowHeights[unRowIndex] = (std::max)(arRowHeights[unRowIndex], oInfo.unRows);
arColumnWidths[unColumnIndex] = (std::max)(arColumnWidths[unColumnIndex], oInfo.unColumns);
if (IsTableContainer(arCells[unRowIndex][unColumnIndex]))
oInfo = ComputeInfo(arCells[unRowIndex][unColumnIndex]);
arRowHeights[unRowIndex] = (std::max)(arRowHeights[unRowIndex], oInfo.m_unRows);
arColumnWidths[unColumnIndex] = (std::max)(arColumnWidths[unColumnIndex], oInfo.m_unColumns);
}
}

View File

@ -55,8 +55,16 @@ void CCodeTag<CMDWriter>::Close();
struct TElementInfo
{
size_t unRows;
size_t unColumns;
size_t m_unRows;
size_t m_unColumns;
TElementInfo()
: m_unRows{0}, m_unColumns{0}
{}
TElementInfo(const size_t& unRows, const size_t& m_unColumns)
: m_unRows{unRows}, m_unColumns{m_unColumns}
{}
};
class CMarkdownTable : public CTableElement
@ -76,6 +84,7 @@ private:
static Table Flatten(Table&& srcTable);
static TElementInfo ComputeInfo(const ITableElementCell* pCell);
static TElementInfo ComputeInfo(const CTableElement* pTable);
};
}

View File

@ -1403,14 +1403,14 @@ void COOXMLTable::CloseRow(XmlString& oXmlString)
oXmlString.WriteNodeEnd(L"w:tr");
}
void COOXMLTable::OpenCell(XmlString& oXmlString, size_t unColumnIndex)
void COOXMLTable::OpenCell(XmlString& oXmlString)
{
oXmlString.WriteNodeBegin(L"w:tc");
}
void COOXMLTable::OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode, CTableElementCell* pCell, size_t unColumnIndex, ERowParseMode eRowParseMode, ERowPosition eRowPosition, size_t& unHeight, const TTableStyles& oTableStyles)
void COOXMLTable::OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode, ITableElementCell* pCell, size_t unColumnIndex, ERowParseMode eRowParseMode, ERowPosition eRowPosition, size_t& unHeight, const TTableStyles& oTableStyles, std::unordered_map<size_t, NSCSS::CNode>& mFillingColumn)
{
OpenCell(oXmlString, unColumnIndex);
OpenCell(oXmlString);
if (nullptr == pCell)
return;
@ -1450,25 +1450,8 @@ void COOXMLTable::OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode,
else
oXmlString += L"<w:tcW w:w=\"0\" w:type=\"auto\"/>";
std::wstring wsValue;
if (oCellNode.GetAttributeValue(L"colspan", wsValue))
{
const int nColspan{NSStringFinder::ToInt(wsValue, 1)};
if (1 < nColspan)
oXmlString += L"<w:gridSpan w:val=\"" + std::to_wstring(nColspan) + L"\"/>";
}
if (ETableElement::FillingCell == pCell->GetType())
oXmlString += L"<w:vMerge w:val=\"continue\"/>";
else if (oCellNode.GetAttributeValue(L"rowspan", wsValue))
{
const int nRowspan{NSStringFinder::ToInt(wsValue, 1)};
if (1 < nRowspan)
oXmlString += L"<w:vMerge w:val=\"restart\"/>";
}
if (1 != pCell->GetColspan())
oXmlString += L"<w:gridSpan w:val=\"" + std::to_wstring(pCell->GetColspan()) + L"\"/>";
const NSCSS::NSProperties::CBorder* pBorder{&oCellNode.m_pCompiledStyle->m_oBorder};
@ -1493,6 +1476,22 @@ void COOXMLTable::OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode,
oXmlString += L"<w:tcBorders>" + wsBorders + L"</w:tcBorders>";
}
std::wstring wsValue;
if (ETableElement::FillingCell == pCell->GetType())
oXmlString += L"<w:vMerge w:val=\"continue\"/>";
else if (oCellNode.GetAttributeValue(L"rowspan", wsValue))
{
const int nRowspan{NSStringFinder::ToInt(wsValue, 1)};
if (1 < nRowspan)
{
oXmlString += L"<w:vMerge w:val=\"restart\"/>";
mFillingColumn[unColumnIndex] = oCellNode;
mFillingColumn[unColumnIndex].m_pCompiledStyle = new NSCSS::CCompiledStyle(*oCellNode.m_pCompiledStyle);
}
}
const NSCSS::NSProperties::CColor* pBackground{&oCellNode.m_pCompiledStyle->m_oBackground.GetColor()};
if (nullptr != pColStyle && pBackground->LessSignificantThen(pColStyle->m_oBackground.GetColor()))
@ -1594,65 +1593,122 @@ void COOXMLTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter&
ITableElementCell* pTableCell{nullptr};
size_t unCellHeight{0}, unMaxCellHeight{0};
std::unordered_map<size_t, NSCSS::CNode> mFillingColumn;
for (size_t unRowIndex = 0; unRowIndex < oMatrix.size(); ++unRowIndex)
{
XmlString oCellsData;
oWriter.SetDataOutput(&oCellsData);
bool bEmptyRow{true};
for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size(); ++unColumnIndex)
{
pTableCell = oMatrix[unRowIndex][unColumnIndex];
if (nullptr != pTableCell)
{
MoveToNextTableCell(oReader, arSelectors, arDepths, m_oExternalData.GetSubClass);
if (ETableElement::Table == pTableCell->GetType())
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
OpenCell(oCellsData, unColumnIndex);
const COOXMLTable* pTable{dynamic_cast<const COOXMLTable*>(pTableCell)};
if (nullptr == pTableCell)
oWriter.WriteEmptyParagraph();
else
{
arDepths.push(oReader.GetDepth());
MoveToNextTableCell(oReader, arSelectors, arDepths, m_oExternalData.GetSubClass);
ConvertTable(oReader, oWriter, *pTable, arSelectors.back());
}
}
else
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
OpenCell(oCellsData, arSelectors.back(), (CTableElementCell*)pTableCell, unColumnIndex, eRowParseMode,
((0 == unRowIndex) ? ERowPosition::First : ((oMatrix.size() - 1 == unRowIndex) ? ERowPosition::Last : ERowPosition::Middle)),
unCellHeight, oTableStyles);
unMaxCellHeight = (std::max)(unMaxCellHeight, unCellHeight);
m_oExternalData.ReadStream(oReader, arSelectors);
}
oWriter.CloseP();
if (unColumnIndex < MAX_COLUMNS_IN_TABLE - 1)
CloseCell(oCellsData);
arSelectors.pop_back();
}
else
if (nullptr == pTableCell)
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
OpenCell(oCellsData, unColumnIndex);
OpenCell(oCellsData);
oWriter.WriteEmptyParagraph();
if (unColumnIndex < MAX_COLUMNS_IN_TABLE - 1)
CloseCell(oCellsData);
continue;
}
if (ETableElement::FillingCell == pTableCell->GetType())
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
{
OpenCell(oCellsData, mFillingColumn.at(unColumnIndex), (CTableElementCell*)pTableCell, unColumnIndex, eRowParseMode,
((0 == unRowIndex) ? ERowPosition::First : ((oMatrix.size() - 1 == unRowIndex) ? ERowPosition::Last : ERowPosition::Middle)),
unCellHeight, oTableStyles, mFillingColumn);
}
oWriter.WriteEmptyParagraph();
if (unColumnIndex < MAX_COLUMNS_IN_TABLE - 1)
CloseCell(oCellsData);
continue;
}
MoveToNextTableCell(oReader, arSelectors, arDepths, m_oExternalData.GetSubClass);
bEmptyRow = false;
if (ETableElement::TableContainer == pTableCell->GetType())
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
OpenCell(oCellsData, arSelectors.back(), (CTableElementCell*)pTableCell, unColumnIndex, eRowParseMode,
((0 == unRowIndex) ? ERowPosition::First : ((oMatrix.size() - 1 == unRowIndex) ? ERowPosition::Last : ERowPosition::Middle)),
unCellHeight, oTableStyles, mFillingColumn);
std::vector<CTableElement*> arTables{((CTableContainer*)pTableCell)->GetTables()};
std::vector<CTableElement*>::const_iterator itTable = arTables.cbegin();
const size_t unDepthSize{arDepths.size()};
arDepths.push(oReader.GetDepth());
m_oExternalData.AddStopTag(L"table");
#define READ_TABLE()\
m_oExternalData.GetSubClass(oReader, arSelectors);\
oWriter.CloseP();\
ConvertTable(oReader, oWriter, *(COOXMLTable*)*itTable++, arSelectors.back());\
oWriter.WriteEmptyParagraph(true);\
arSelectors.pop_back()
while (oReader.ReadNextSiblingNode2(arDepths.top()))
{
if (L"table" == oReader.GetName())
{
READ_TABLE();
}
else
{
m_oExternalData.ReadInside(oReader, arSelectors);
if (L"table" == oReader.GetName())
{
READ_TABLE();
}
}
}
while (arDepths.size() > unDepthSize)
{
arDepths.pop();
arSelectors.pop_back();
}
m_oExternalData.ClearStopTags();
}
else
{
if (unColumnIndex < MAX_COLUMNS_IN_TABLE)
OpenCell(oCellsData, arSelectors.back(), (CTableElementCell*)pTableCell, unColumnIndex, eRowParseMode,
((0 == unRowIndex) ? ERowPosition::First : ((oMatrix.size() - 1 == unRowIndex) ? ERowPosition::Last : ERowPosition::Middle)),
unCellHeight, oTableStyles, mFillingColumn);
unMaxCellHeight = (std::max)(unMaxCellHeight, unCellHeight);
const size_t unSelectorsSize{arSelectors.size()};
m_oExternalData.ReadStream(oReader, arSelectors);
arSelectors.resize(unSelectorsSize);
arSelectors.pop_back();
}
oWriter.CloseP();
if (unColumnIndex < MAX_COLUMNS_IN_TABLE - 1)
CloseCell(oCellsData);
}
if (oMatrix[unRowIndex].size() >= MAX_COLUMNS_IN_TABLE)
@ -1664,34 +1720,68 @@ void COOXMLTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter&
WriteToStringBuilder(oCellsData, *oWriter.GetCurrentDocument());
CloseRow(*oWriter.GetCurrentDocument());
arSelectors.pop_back();
arDepths.pop();
if (!bEmptyRow)
{
arSelectors.pop_back();
arDepths.pop();
}
}
arSelectors.pop_back();
arDepths.pop();
}
inline void CalculateMaxRowSize(const Table& oMatrix, size_t& unMaxRowSize)
{
for (const Row& oRow : oMatrix)
{
size_t unCurrentSize{0};
for (ITableElementCell* pCell : oRow)
{
if (nullptr == pCell || ETableElement::TableContainer == pCell->GetType())
unCurrentSize += 1;
else
unCurrentSize += ((CTableElementCell*)pCell)->GetColspan();
}
unMaxRowSize = (std::max)(unMaxRowSize, unCurrentSize);
}
}
inline void NormalizeMatrix(Table& oMatrix, const size_t& unMaxMatrixColumns, const size_t& unMaxColumns)
{
for (Row& oRow : oMatrix)
{
for (ITableElementCell* pCell : oRow)
{
if (nullptr == pCell)
continue;
if (ETableElement::TableContainer == pCell->GetType())
{
for (CTableElement* pTable : ((CTableContainer*)pCell)->GetTables())
if (nullptr != pTable)
pTable->Normalize();
}
}
if (unMaxMatrixColumns != unMaxColumns)
oRow.resize(oRow.size() + (unMaxColumns - unMaxMatrixColumns));
}
}
void COOXMLTable::Normalize()
{
size_t unMaxColumn{0};
size_t unMaxColumns{0}, unHeaderRowSize{0}, unBodyRowSize{0}, unFootherRowSize{0};
#define CHECK_TABLE(table_variable)\
for (const Row& oRow : table_variable.GetMatrixCells())\
unMaxColumn = (std::max)(unMaxColumn, oRow.size())
CalculateMaxRowSize(m_oHeader.GetMatrixCells(), unHeaderRowSize);
CalculateMaxRowSize(m_oBody.GetMatrixCells(), unBodyRowSize);
CalculateMaxRowSize(m_oFoother.GetMatrixCells(), unFootherRowSize);
CHECK_TABLE(m_oHeader);
CHECK_TABLE(m_oBody);
CHECK_TABLE(m_oFoother);
unMaxColumns = (std::max)((std::max)(unHeaderRowSize, unBodyRowSize), unFootherRowSize);
#define NORMALIZE_TABLE(table_variable)\
for (Row& oRow : table_variable.GetMatrixCells())\
if (oRow.size() != unMaxColumn)\
oRow.resize(unMaxColumn)
NORMALIZE_TABLE(m_oHeader);
NORMALIZE_TABLE(m_oBody);
NORMALIZE_TABLE(m_oFoother);
NormalizeMatrix(m_oHeader.GetMatrixCells(), unHeaderRowSize, unMaxColumns);
NormalizeMatrix(m_oBody.GetMatrixCells(), unBodyRowSize, unMaxColumns);
NormalizeMatrix(m_oFoother.GetMatrixCells(), unFootherRowSize, unMaxColumns);
}
bool COOXMLTable::Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode)

View File

@ -117,7 +117,7 @@ private:
NSCSS::NSProperties::CIndent m_oPadding;
TTableStyles()
: m_unCellSpacing{0}, m_enRules{ETableRules::All}, m_pTable{nullptr}
: m_unCellSpacing{0}, m_enRules{ETableRules::All}, m_pTable{nullptr}
{}
};
@ -141,8 +141,8 @@ private:
void OpenRow(XmlString& oXmlString, bool bIsHeader, size_t unMaxHeight, size_t unCellSpacing);
void CloseRow(XmlString& oXmlString);
void OpenCell(XmlString& oXmlString, size_t unColumnIndex);
void OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode, CTableElementCell* pCell, size_t unColumnIndex, ERowParseMode eRowParseMode, ERowPosition eRowPosition, size_t& unHeight, const TTableStyles& oTableStyles);
void OpenCell(XmlString& oXmlString);
void OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode, ITableElementCell* pCell, size_t unColumnIndex, ERowParseMode eRowParseMode, ERowPosition eRowPosition, size_t& unHeight, const TTableStyles& oTableStyles, std::unordered_map<size_t, NSCSS::CNode>& mFillingColumn);
void CloseCell(XmlString& oXmlString);
void ConvertTable(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, const COOXMLTable& oTable, const NSCSS::CNode& oTableNode);