diff --git a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp index a1e048a816..5462e9085f 100644 --- a/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp +++ b/Common/3dParty/html/css/src/xhtml/CDocumentStyle.cpp @@ -1,6 +1,5 @@ #include "CDocumentStyle.h" -#include #include #include #include diff --git a/HtmlFile2/HTMLReader.cpp b/HtmlFile2/HTMLReader.cpp index 53d09c9dd4..eea0e0f94e 100644 --- a/HtmlFile2/HTMLReader.cpp +++ b/HtmlFile2/HTMLReader.cpp @@ -29,8 +29,6 @@ namespace HTML #define UNKNOWN_TAG GumboTag::GUMBO_TAG_UNKNOWN #define HtmlTag GumboTag -#define TAGS_COUNT 32 - const static std::map 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& arSelectors) { GetSubClass(oReader, arSelectors, m_oCSSCalculator); }; + oExternalData.ReadInside = [this](XmlUtils::CXmlLiteReader& oReader, + std::vector& 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& arSelectors) - { return ReadStream(oReader, arSelectors, true); }; + { return ReadStream(oReader, arSelectors, true); }; oExternalData.GetSubClass = [this](XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors) - { GetSubClass(oReader, arSelectors, m_oCSSCalculator); }; + { GetSubClass(oReader, arSelectors, m_oCSSCalculator); }; + + oExternalData.ReadInside = [this](XmlUtils::CXmlLiteReader& oReader, + std::vector& 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 -#include - #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 + namespace HTML { class CHTMLReader @@ -32,6 +31,8 @@ class CHTMLReader CTableElement* m_pTableElement; // Table Converter + std::set m_arStopTags; + THTMLTags m_oTags; public: CHTMLReader(); @@ -81,6 +82,9 @@ private: bool ReadText(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors); bool ReadTable(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors); + + void AddStopTag(const std::wstring& wsTag); + void ClearStopTags(); }; } diff --git a/HtmlFile2/Table.cpp b/HtmlFile2/Table.cpp index 0c0d7df8fa..0cd64f2380 100644 --- a/HtmlFile2/Table.cpp +++ b/HtmlFile2/Table.cpp @@ -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& CTableContainer::GetTables() const +{ + return m_arTables; +} + CTableCol::CTableCol(NSCSS::CNode& oTableColNode) : m_pStyle(oTableColNode.m_pCompiledStyle) { diff --git a/HtmlFile2/Table.h b/HtmlFile2/Table.h index 96c7b9d80b..41d2908415 100644 --- a/HtmlFile2/Table.h +++ b/HtmlFile2/Table.h @@ -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& arSelectors)> FuncReadStream; typedef std::function& arSelectors)> FuncGetSubClass; + typedef std::function& arSelectors)> FuncReadInside; + typedef std::function FuncAddStopTag; + typedef std::function 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& GetTables() const; +private: + std::vector m_arTables; +}; + +struct TTableData +{ + CTableMatrix m_oHeader; + CTableMatrix m_oBody; + CTableMatrix m_oFoother; + + XmlString *m_pCaption; + + std::vector m_arColgroups; +}; + template 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(pTableElement); + else + { + pContainer = new CTableContainer(); + pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, pContainer); + } + + if (nullptr == pContainer) + return false; + + pContainer->AddTable(pNewTable); ParseTable(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(oReader, pMatrix, unRowIndex, unColumnIndex); diff --git a/HtmlFile2/Tags/MDTags.cpp b/HtmlFile2/Tags/MDTags.cpp index 145b52fcb0..e6780a764d 100644 --- a/HtmlFile2/Tags/MDTags.cpp +++ b/HtmlFile2/Tags/MDTags.cpp @@ -15,8 +15,6 @@ #include "../../DesktopEditor/graphics/pro/Graphics.h" #include "../../DesktopEditor/raster/BgraFrame.h" -#include - namespace HTML { template<> @@ -637,9 +635,9 @@ void CCodeTag::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, NSStringUtils::CStringBuilder*>> NestedCells; + +typedef std::map, 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& 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& arSelectors, TCurentTablePosition& oPosition, NestedCells& arNestedCells, const Table& oCells, CMDWriter* pWriter, const TExternalTableData& oExternalTableData, NSStringUtils::CStringBuilder& oIntermediateData); + +void ReadNestedTable(XmlUtils::CXmlLiteReader& oReader, std::vector& 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 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& 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::vectorWrite(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 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 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 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 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> oInfos{unRows, std::vector{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(pCell)}; + + CTableContainer* pContainer{dynamic_cast(pCell)}; + std::vector 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(pTable)->m_oBody.GetMatrixCells()}; + Table oFlatChild{Flatten(std::move(oChild))}; - CTableElementCell* pFlatCell{dynamic_cast(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(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> expandedRows; +// expandedRows.reserve(unRows); +// size_t unColumns = 0; + +// for (Row& oRow : srcTable) +// { +// std::vector 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> oInfos(unRows, std::vector(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 arRowHeights(unRows, 1); +// std::vector 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 arRowStart(unRows + 1, 0); +// std::vector 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(pCell); +// std::vector arTables = pContainer->GetTables(); +// pCell = nullptr; // владение передано + +// size_t unCurrentRowOffset = 0; +// for (CTableElement* pTableElem : arTables) +// { +// // Предполагаем, что в контейнере лежат только CMarkdownTable +// CMarkdownTable* pMarkdownTable = dynamic_cast(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(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 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(pCell)}; + const CMarkdownTable *pMarkdownTable{dynamic_cast(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 arRowHeights(unRows, 1); std::vector 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); } } diff --git a/HtmlFile2/Tags/MDTags.h b/HtmlFile2/Tags/MDTags.h index d78eaa7918..bb9eb31189 100644 --- a/HtmlFile2/Tags/MDTags.h +++ b/HtmlFile2/Tags/MDTags.h @@ -55,8 +55,16 @@ void CCodeTag::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); }; } diff --git a/HtmlFile2/Tags/OOXMLTags.cpp b/HtmlFile2/Tags/OOXMLTags.cpp index e0f2bf4c1f..6109bf368a 100644 --- a/HtmlFile2/Tags/OOXMLTags.cpp +++ b/HtmlFile2/Tags/OOXMLTags.cpp @@ -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& 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""; - std::wstring wsValue; - - if (oCellNode.GetAttributeValue(L"colspan", wsValue)) - { - const int nColspan{NSStringFinder::ToInt(wsValue, 1)}; - - if (1 < nColspan) - oXmlString += L""; - } - - if (ETableElement::FillingCell == pCell->GetType()) - oXmlString += L""; - else if (oCellNode.GetAttributeValue(L"rowspan", wsValue)) - { - const int nRowspan{NSStringFinder::ToInt(wsValue, 1)}; - - if (1 < nRowspan) - oXmlString += L""; - } + if (1 != pCell->GetColspan()) + oXmlString += L"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"" + wsBorders + L""; } + std::wstring wsValue; + + if (ETableElement::FillingCell == pCell->GetType()) + oXmlString += L""; + else if (oCellNode.GetAttributeValue(L"rowspan", wsValue)) + { + const int nRowspan{NSStringFinder::ToInt(wsValue, 1)}; + + if (1 < nRowspan) + { + oXmlString += L""; + 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 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(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 arTables{((CTableContainer*)pTableCell)->GetTables()}; + std::vector::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) diff --git a/HtmlFile2/Tags/OOXMLTags.h b/HtmlFile2/Tags/OOXMLTags.h index 9f93c57fa9..faddf02aa3 100644 --- a/HtmlFile2/Tags/OOXMLTags.h +++ b/HtmlFile2/Tags/OOXMLTags.h @@ -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& mFillingColumn); void CloseCell(XmlString& oXmlString); void ConvertTable(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, const COOXMLTable& oTable, const NSCSS::CNode& oTableNode);