diff --git a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp index 49d4d29bf2..5cb274feeb 100644 --- a/Common/3dParty/html/css/src/CCssCalculator_Private.cpp +++ b/Common/3dParty/html/css/src/CCssCalculator_Private.cpp @@ -525,6 +525,7 @@ namespace NSCSS m_mDefaultStyleData[L"ul"] = new CElement(L"ul", {{L"margin-top", L"100tw"}, {L"margin-bottom", L"100tw"}}); m_mDefaultStyleData[L"textarea"] = new CElement(L"textarea", {{L"border", L"1px solid black"}}); + m_mDefaultStyleData[L"th"] = new CElement(L"b", {{L"font-weight", L"bold"}}); } CCssCalculator_Private::CCssCalculator_Private() diff --git a/Common/3dParty/html/css/src/StyleProperties.h b/Common/3dParty/html/css/src/StyleProperties.h index 8d62be17b6..30b573f937 100644 --- a/Common/3dParty/html/css/src/StyleProperties.h +++ b/Common/3dParty/html/css/src/StyleProperties.h @@ -118,6 +118,11 @@ namespace NSCSS return *this; } + + bool LessSignificantThen(const CValueBase& oValue) const + { + return oValue.m_unLevel >= m_unLevel && (!m_bImportant || oValue.m_bImportant) && !oValue.Empty(); + } }; template diff --git a/HtmlFile2/HTMLReader.cpp b/HtmlFile2/HTMLReader.cpp index ee082e3800..5b8382de5d 100644 --- a/HtmlFile2/HTMLReader.cpp +++ b/HtmlFile2/HTMLReader.cpp @@ -261,6 +261,7 @@ inline bool CheckArgumentMath(const std::wstring& wsNodeName, const std::wstring inline HtmlTag GetHtmlTag(const std::wstring& wsStrTag); inline bool UnreadableNode(const std::wstring& wsNodeName); inline bool TagIsUnprocessed(const std::wstring& wsTagName); +inline void GetSubClass(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, NSCSS::CCssCalculator& oCSSCalculator); CHTMLReader::CHTMLReader() : m_bIsTempDirOwner(true), m_pWriter(nullptr) @@ -375,9 +376,6 @@ void CHTMLReader::InitOOXMLTags(THTMLParameters* pParametrs) m_mTags[HTML_TAG(OL)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(LI)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(CAPTION)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TABLE)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TR)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TD)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(HTML)] = std::make_shared>(pWriter); std::shared_ptr oIgnoredTag{std::make_shared()}; @@ -424,9 +422,6 @@ void CHTMLReader::InitMDTags(TMarkdownParameters* pParametrs) m_mTags[HTML_TAG(IMG)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(HR)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(BLOCKQUOTE)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TABLE)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TR)] = std::make_shared>(pWriter); - m_mTags[HTML_TAG(TD)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(OL)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(LI)] = std::make_shared>(pWriter); m_mTags[HTML_TAG(PRE)] = std::make_shared>(pWriter); @@ -454,9 +449,9 @@ void CHTMLReader::InitMDTags(TMarkdownParameters* pParametrs) m_mTags[HTML_TAG(HTML)] = oIgnoredTag; } -bool CHTMLReader::IsHTML() +bool CHTMLReader::IsHTML(XmlUtils::CXmlLiteReader& oReader) { - return ((m_oLightReader.MoveToStart() && m_oLightReader.ReadNextNode()) ? m_oLightReader.GetName() == L"html" : false); + return ((oReader.MoveToStart() && oReader.ReadNextNode()) ? oReader.GetName() == L"html" : false); } HRESULT CHTMLReader::InitAndConvert2OOXML(const std::vector& arPaths, const std::wstring& wsDirectory, Convert_Func Convertation, THTMLParameters* pParameters) @@ -505,7 +500,9 @@ HRESULT CHTMLReader::InitAndConvert2Markdown(const std::vector& ar bool CHTMLReader::Convert(const std::wstring& wsPath, Convert_Func Convertation) { - if (nullptr == m_pWriter || !Convertation(wsPath, m_oLightReader) || !m_oLightReader.IsValid() || !IsHTML()) + XmlUtils::CXmlLiteReader oReader; + + if (nullptr == m_pWriter || !Convertation(wsPath, oReader) || !oReader.IsValid() || !IsHTML(oReader)) return false; if (m_wsTempDirectory.empty()) @@ -515,79 +512,79 @@ bool CHTMLReader::Convert(const std::wstring& wsPath, Convert_Func Convertation) m_wsSrcDirectory = NSSystemPath::GetDirectoryName(wsPath); - m_oLightReader.MoveToStart(); - m_oLightReader.ReadNextNode(); - ReadStyle(); + oReader.MoveToStart(); + oReader.ReadNextNode(); + ReadStyle(oReader); // Переходим в начало - if(!m_oLightReader.MoveToStart()) + if(!oReader.MoveToStart()) return S_FALSE; - ReadDocument(); + ReadDocument(oReader); return true; } -void CHTMLReader::ReadStyle() +void CHTMLReader::ReadStyle(XmlUtils::CXmlLiteReader& oReader) { - if(m_oLightReader.IsEmptyNode()) + if(oReader.IsEmptyNode()) return; - const int nDeath = m_oLightReader.GetDepth(); + const int nDeath = oReader.GetDepth(); std::wstring sName; - while(m_oLightReader.ReadNextSiblingNode(nDeath)) + while(oReader.ReadNextSiblingNode(nDeath)) { - sName = m_oLightReader.GetName(); + sName = oReader.GetName(); if(sName == L"body") - ReadStyle2(); + ReadStyle2(oReader); else { // Стиль по ссылке if(sName == L"link") { - while(m_oLightReader.MoveToNextAttribute()) - ReadStyleFromNetwork(); + while(oReader.MoveToNextAttribute()) + ReadStyleFromNetwork(oReader); - m_oLightReader.MoveToElement(); + oReader.MoveToElement(); } // тэг style содержит стили для styles.xml else if(sName == L"style") - m_oCSSCalculator.AddStyles(m_oLightReader.GetText2()); + m_oCSSCalculator.AddStyles(oReader.GetText2()); else - ReadStyle(); + ReadStyle(oReader); } } } -void CHTMLReader::ReadStyle2() +void CHTMLReader::ReadStyle2(XmlUtils::CXmlLiteReader& oReader) { - const std::wstring wsName = m_oLightReader.GetName(); + const std::wstring wsName = oReader.GetName(); // Стиль по ссылке if(wsName == L"link") { - while(m_oLightReader.MoveToNextAttribute()) - ReadStyleFromNetwork(); - m_oLightReader.MoveToElement(); + while(oReader.MoveToNextAttribute()) + ReadStyleFromNetwork(oReader); + oReader.MoveToElement(); } // тэг style содержит стили для styles.xml else if(wsName == L"style") - m_oCSSCalculator.AddStyles(m_oLightReader.GetText2()); + m_oCSSCalculator.AddStyles(oReader.GetText2()); - const int nDeath = m_oLightReader.GetDepth(); - while(m_oLightReader.ReadNextSiblingNode(nDeath)) + const int nDeath = oReader.GetDepth(); + while(oReader.ReadNextSiblingNode(nDeath)) { - if(!m_oLightReader.IsEmptyNode()) - ReadStyle2(); + if(!oReader.IsEmptyNode()) + ReadStyle2(oReader); } } -void CHTMLReader::ReadStyleFromNetwork() +void CHTMLReader::ReadStyleFromNetwork(XmlUtils::CXmlLiteReader& oReader) { - if(m_oLightReader.GetName() != L"href") + if(oReader.GetName() != L"href") return; - std::wstring sRef = m_oLightReader.GetText(); + std::wstring sRef = oReader.GetText(); if(NSFile::GetFileExtention(sRef) != L"css") return; std::wstring sFName = NSFile::GetFileName(sRef); @@ -610,44 +607,44 @@ void CHTMLReader::ReadStyleFromNetwork() } } -void CHTMLReader::ReadDocument() +void CHTMLReader::ReadDocument(XmlUtils::CXmlLiteReader& oReader) { - m_oLightReader.ReadNextNode(); + oReader.ReadNextNode(); - int nDeath = m_oLightReader.GetDepth(); - while(m_oLightReader.ReadNextSiblingNode(nDeath)) + int nDeath = oReader.GetDepth(); + while(oReader.ReadNextSiblingNode(nDeath)) { - const std::wstring wsName = m_oLightReader.GetName(); + const std::wstring wsName = oReader.GetName(); if(wsName == L"head") - ReadHead(); + ReadHead(oReader); else if(wsName == L"body") - ReadBody(); + ReadBody(oReader); } } -void CHTMLReader::ReadHead() +void CHTMLReader::ReadHead(XmlUtils::CXmlLiteReader& oReader) { - if(m_oLightReader.IsEmptyNode()) + if(oReader.IsEmptyNode()) return; - int nDeath = m_oLightReader.GetDepth(); - while (m_oLightReader.ReadNextSiblingNode(nDeath)) + int nDeath = oReader.GetDepth(); + while (oReader.ReadNextSiblingNode(nDeath)) { - const std::wstring wsName = m_oLightReader.GetName(); + const std::wstring wsName = oReader.GetName(); // Базовый адрес if (L"base" == wsName) - m_wsBaseDirectory = GetArgumentValue(m_oLightReader, L"href"); + m_wsBaseDirectory = GetArgumentValue(oReader, L"href"); } - m_oLightReader.MoveToElement(); + oReader.MoveToElement(); } -void CHTMLReader::ReadBody() +void CHTMLReader::ReadBody(XmlUtils::CXmlLiteReader& oReader) { std::vector arSelectors; arSelectors.push_back(NSCSS::CNode(L"html", L"", L"")); - GetSubClass(arSelectors); + GetSubClass(oReader, arSelectors, m_oCSSCalculator); if (!m_mTags[HTML_TAG(HTML)]->Open(arSelectors)) return; @@ -657,22 +654,22 @@ void CHTMLReader::ReadBody() if (arSelectors.back().m_mAttributes.end() != itFound) arSelectors.back().m_mAttributes.erase(itFound); - m_oLightReader.MoveToElement(); + oReader.MoveToElement(); - ReadStream(arSelectors); + ReadStream(oReader, arSelectors); m_mTags[HTML_TAG(HTML)]->Close(arSelectors); } -bool CHTMLReader::ReadStream(std::vector& arSelectors, bool bInsertEmptyP) +bool CHTMLReader::ReadStream(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, bool bInsertEmptyP) { if (nullptr == m_pWriter) return false; bool bResult{false}; - const int nDeath = m_oLightReader.GetDepth(); - if(m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath)) + const int nDeath = oReader.GetDepth(); + if(oReader.IsEmptyNode() || !oReader.ReadNextSiblingNode2(nDeath)) { if (!bInsertEmptyP) return false; @@ -683,9 +680,9 @@ bool CHTMLReader::ReadStream(std::vector& arSelectors, bool bInser do { - if (ReadInside(arSelectors)) + if (ReadInside(oReader, arSelectors)) bResult = true; - } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); + } while(oReader.ReadNextSiblingNode2(nDeath)); if (!bResult && bInsertEmptyP) m_pWriter->WriteEmptyParagraph(); @@ -693,18 +690,18 @@ bool CHTMLReader::ReadStream(std::vector& arSelectors, bool bInser return bResult; } -bool CHTMLReader::ReadInside(std::vector& arSelectors) +bool CHTMLReader::ReadInside(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors) { - const std::wstring wsName{m_oLightReader.GetName()}; + const std::wstring wsName{oReader.GetName()}; if(wsName == L"#text") - return ReadText(arSelectors); + return ReadText(oReader, arSelectors); //TODO:: обработать все варианты return'а if (UnreadableNode(wsName) || TagIsUnprocessed(wsName)) return false; - GetSubClass(arSelectors); + GetSubClass(oReader, arSelectors, m_oCSSCalculator); bool bResult = true; @@ -715,24 +712,24 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(A): case HTML_TAG(AREA): { - bResult = ReadDefaultTag(HTML_TAG(A), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(A), arSelectors); break; } case HTML_TAG(ABBR): { - bResult = ReadDefaultTag(HTML_TAG(ABBR), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(ABBR), arSelectors); break; } case HTML_TAG(B): case HTML_TAG(STRONG): { - bResult = ReadDefaultTag(HTML_TAG(B), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(B), arSelectors); break; } case HTML_TAG(BDO): case HTML_TAG(BDI): { - bResult = ReadDefaultTag(HTML_TAG(BDO), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(BDO), arSelectors); break; } case HTML_TAG(BR): @@ -742,7 +739,7 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) } case HTML_TAG(CENTER): { - bResult = ReadDefaultTag(HTML_TAG(CENTER), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(CENTER), arSelectors); break; } case HTML_TAG(CITE): @@ -751,7 +748,7 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(I): case HTML_TAG(VAR): { - bResult = ReadDefaultTag(HTML_TAG(I), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(I), arSelectors); break; } case HTML_TAG(CODE): @@ -759,62 +756,61 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(TT): case HTML_TAG(OUTPUT): { - bResult = ReadDefaultTag(HTML_TAG(CODE), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(CODE), arSelectors); break; } case HTML_TAG(KBD): { - bResult = ReadDefaultTag(HTML_TAG(KBD), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(KBD), arSelectors); break; } case HTML_TAG(DEL): case HTML_TAG(S): case HTML_TAG(STRIKE): { - bResult = ReadDefaultTag(HTML_TAG(S), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(S), arSelectors); break; } case HTML_TAG(FONT): { - bResult = ReadDefaultTag(HTML_TAG(FONT), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(FONT), arSelectors); break; } case HTML_TAG(IMG): - { bResult = ReadEmptyTag(HTML_TAG(IMG), arSelectors); break; } case HTML_TAG(SVG): { - bResult = ReadSVG(arSelectors); + bResult = ReadSVG(oReader, arSelectors); break; } case HTML_TAG(INS): case HTML_TAG(U): { - bResult = ReadDefaultTag(HTML_TAG(U), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(U), arSelectors); break; } case HTML_TAG(MARK): { - bResult = ReadDefaultTag(HTML_TAG(MARK), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(MARK), arSelectors); break; } case HTML_TAG(Q): { - bResult = ReadDefaultTag(HTML_TAG(Q), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(Q), arSelectors); break; } case HTML_TAG(SUP): case HTML_TAG(SUB): { - bResult = ReadDefaultTag(HTML_TAG(SUP), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(SUP), arSelectors); break; } case HTML_TAG(INPUT): { - bResult = ReadDefaultTag(HTML_TAG(INPUT), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(INPUT), arSelectors); break; } case HTML_TAG(CANVAS): @@ -838,17 +834,17 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) } case HTML_TAG(SPAN): { - bResult = ReadDefaultTag(HTML_TAG(SPAN), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(SPAN), arSelectors); break; } case HTML_TAG(NOBR): { - bResult = ReadDefaultTag(HTML_TAG(PRE), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(PRE), arSelectors); break; } case HTML_TAG(BASEFONT): { - bResult = ReadDefaultTag(HTML_TAG(BASEFONT), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(BASEFONT), arSelectors); break; } case HTML_TAG(BUTTON): @@ -864,7 +860,7 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(ACRONYM): case HTML_TAG(BIG): { - bResult = ReadStream(arSelectors); + bResult = ReadStream(oReader, arSelectors); break; } default: @@ -875,12 +871,12 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) { case HTML_TAG(ADDRESS): { - bResult = ReadDefaultTag(HTML_TAG(I), arSelectors);; + bResult = ReadDefaultTag(oReader, HTML_TAG(I), arSelectors);; break; } case HTML_TAG(DD): { - bResult = ReadDefaultTag(HTML_TAG(DD), arSelectors);; + bResult = ReadDefaultTag(oReader, HTML_TAG(DD), arSelectors);; break; } case HTML_TAG(H1): @@ -890,18 +886,18 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(H5): case HTML_TAG(H6): { - bResult = ReadDefaultTag(HTML_TAG(H1), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(H1), arSelectors); break; } case HTML_TAG(ASIDE): case HTML_TAG(DIV): { - bResult = ReadDefaultTag(HTML_TAG(DIV), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(DIV), arSelectors); break; } case HTML_TAG(BLOCKQUOTE): { - bResult = ReadDefaultTag(HTML_TAG(BLOCKQUOTE), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(BLOCKQUOTE), arSelectors); break; } case HTML_TAG(ARTICLE): @@ -921,7 +917,7 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(LEGEND): case HTML_TAG(MAP): { - bResult = ReadStream(arSelectors); + bResult = ReadStream(oReader, arSelectors); break; } case HTML_TAG(HR): @@ -931,13 +927,13 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) } case HTML_TAG(LI): { - bResult = ReadDefaultTag(HTML_TAG(LI), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(LI), arSelectors); break; } case HTML_TAG(OL): case HTML_TAG(UL): { - bResult = ReadDefaultTag(HTML_TAG(OL), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(OL), arSelectors); break; } // case HTML_TAG(MENU): @@ -951,12 +947,12 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) case HTML_TAG(PRE): case HTML_TAG(XMP): { - bResult = ReadDefaultTag(HTML_TAG(PRE), arSelectors); + bResult = ReadDefaultTag(oReader, HTML_TAG(PRE), arSelectors); break; } case HTML_TAG(TABLE): { - bResult = ReadTable(arSelectors); + bResult = ReadTable(oReader, arSelectors); break; } // case HTML_TAG(RUBY): @@ -977,7 +973,7 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) // } default: { - bResult = ReadStream(arSelectors); + bResult = ReadStream(oReader, arSelectors); break; } } @@ -990,23 +986,23 @@ bool CHTMLReader::ReadInside(std::vector& arSelectors) return bResult; } -bool CHTMLReader::ReadText(std::vector& arSelectors) +bool CHTMLReader::ReadText(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors) { if (nullptr == m_pWriter) return false; - GetSubClass(arSelectors); + GetSubClass(oReader, arSelectors, m_oCSSCalculator); - const bool bResult{m_pWriter->WriteText(m_oLightReader.GetText(), arSelectors)}; + const bool bResult{m_pWriter->WriteText(oReader.GetText(), arSelectors)}; arSelectors.pop_back(); return bResult; } -bool CHTMLReader::ReadSVG(const std::vector& arSelectors) +bool CHTMLReader::ReadSVG(XmlUtils::CXmlLiteReader& oReader, const std::vector& arSelectors) { - if (!m_mTags[HTML_TAG(IMG)]->Open(arSelectors, m_oLightReader.GetOuterXml())) + if (!m_mTags[HTML_TAG(IMG)]->Open(arSelectors, oReader.GetOuterXml())) return false; m_mTags[HTML_TAG(IMG)]->Close(arSelectors); @@ -1014,406 +1010,54 @@ bool CHTMLReader::ReadSVG(const std::vector& arSelectors) return true; } -bool CHTMLReader::ReadTable(std::vector& arSelectors) +bool CHTMLReader::ReadTable(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors) { - if(m_oLightReader.IsEmptyNode()) + if(oReader.IsEmptyNode()) return false; - if (nullptr != m_pWriter && !m_pWriter->SupportNestedTables()) - { - //Временно разруливаем это тут, так как по текущей логике мы сначала - //читаем всю таблицу и её вложенные элементы, а потом приступаем к конвертации, - //поэтому конвертору уже приходят вложенные таблицы, что в MD запрещено - for (std::vector::const_reverse_iterator itElement{arSelectors.crbegin() + 1}; itElement < arSelectors.crend(); ++itElement) - if (L"table" == itElement->m_wsName) - return false; + XmlUtils::CXmlLiteReader oTableReader; - if (nullptr != dynamic_cast(m_pWriter)) - ((CMDWriter*)m_pWriter)->EnteredTable(); - } - - CStorageTable oTable; - - NSCSS::CCompiledStyle *pStyle = arSelectors.back().m_pCompiledStyle; - - //Table styles - std::wstring wsFrame; - std::wstring wsValue; - - if (arSelectors.back().GetAttributeValue(L"border", wsValue)) - { - const int nWidth = NSStringFinder::ToInt(wsValue); - - if (0 < nWidth) - { - oTable.SetRules(L"all"); - - if (pStyle->m_oBorder.Empty()) - { - pStyle->m_oBorder.SetStyle(L"outset", 0, true); - pStyle->m_oBorder.SetWidth(nWidth, NSCSS::UnitMeasure::Point, 0, true); - pStyle->m_oBorder.SetColor(L"auto", 0, true); - } - } - else if (pStyle->m_oBorder.Empty()) - { - pStyle->m_oBorder.SetNone(0, true); - oTable.SetRules(L"none"); - } - } - - if (arSelectors.back().GetAttributeValue(L"cellpadding", wsValue)) - pStyle->m_oPadding.SetValues(wsValue + L"px", 0, true); - - if (arSelectors.back().GetAttributeValue(L"rules", wsValue)) - oTable.SetRules(wsValue); - - arSelectors.back().GetAttributeValue(L"frame", wsFrame); - - if (!wsFrame.empty() && pStyle->m_oBorder.Empty()) - { - #define SetDefaultBorderSide(side) \ - pStyle->m_oBorder.SetStyle##side(L"solid", 0, true); \ - pStyle->m_oBorder.SetWidth##side(1, NSCSS::UnitMeasure::Point, 0, true); \ - pStyle->m_oBorder.SetColor##side(L"black", 0, true) - - if (NSStringFinder::Equals(L"border", wsFrame)) - { - SetDefaultBorderSide(); - } - else if (NSStringFinder::Equals(L"above", wsFrame)) - { - SetDefaultBorderSide(TopSide); - } - else if (NSStringFinder::Equals(L"below", wsFrame)) - { - SetDefaultBorderSide(BottomSide); - } - else if (NSStringFinder::Equals(L"hsides", wsFrame)) - { - SetDefaultBorderSide(TopSide); - SetDefaultBorderSide(BottomSide); - } - else if (NSStringFinder::Equals(L"vsides", wsFrame)) - { - SetDefaultBorderSide(LeftSide); - SetDefaultBorderSide(RightSide); - } - else if (NSStringFinder::Equals(L"rhs", wsFrame)) - { - SetDefaultBorderSide(RightSide); - } - else if (NSStringFinder::Equals(L"lhs", wsFrame)) - { - SetDefaultBorderSide(LeftSide); - } - } - - if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Collapse) - oTable.SetCellSpacing(0); - else if (arSelectors.back().GetAttributeValue(L"cellspacing", wsValue)) - oTable.SetCellSpacing(NSStringFinder::ToInt(wsValue)); - else if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Separate) - oTable.SetCellSpacing(15); - - oTable.SetWidth(pStyle->m_oDisplay.GetWidth()); - oTable.SetBorder(pStyle->m_oBorder); - oTable.SetPadding(pStyle->m_oPadding); - oTable.SetMargin(pStyle->m_oMargin); - oTable.SetAlign(pStyle->m_oDisplay.GetHAlign().ToWString()); - //------ - - //TODO:: переписать работу с таблицами без предварительной конвертации ячеек и хранения их внутренних данных - //Читаем содержимое таблицы -> считаем ячейки -> нормализуем таблицу -> конвертим таблицу - - int nDeath = m_oLightReader.GetDepth(); - while(m_oLightReader.ReadNextSiblingNode(nDeath)) - { - const std::wstring sName = m_oLightReader.GetName(); - GetSubClass(arSelectors); - - if(sName == L"caption") - ReadTableCaption(oTable, arSelectors); - if(sName == L"thead") - ReadTableRows(oTable, arSelectors, ERowParseMode::Header); - if(sName == L"tbody") - ReadTableRows(oTable, arSelectors, ERowParseMode::Body); - else if(sName == L"tfoot") - ReadTableRows(oTable, arSelectors, ERowParseMode::Foother); - else if (sName == L"colgroup") - ReadTableColspan(oTable); - - arSelectors.pop_back(); - } - - oTable.Shorten(); - oTable.CompleteTable(); - - #define CONVERT_ROWS(rows, parse_mode)\ - {\ - const std::vector arRows{rows};\ - \ - for (UINT unRow = 0; unRow < arRows.size(); ++unRow)\ - {\ - ERowPosition eRowPosition{ERowPosition::Middle};\ - \ - if (0 == unRow)\ - eRowPosition = ERowPosition::First;\ - else if (arRows.size() - 1 == unRow)\ - eRowPosition = ERowPosition::Last;\ - \ - if (!m_mTags[HTML_TAG(TR)]->Open(arSelectors, boost::tuple(&arRows[unRow]->GetStyles(), oTable, parse_mode, eRowPosition)))\ - continue;\ - \ - const std::vector& arCells{arRows[unRow]->GetCells()};\ - \ - for (UINT unCol = 0; unCol < arCells.size(); ++unCol)\ - {\ - m_mTags[HTML_TAG(TD)]->Open(arSelectors, boost::tuple(*arCells[unCol], oTable, unCol, parse_mode, eRowPosition));\ - \ - if (0 != arCells[unCol]->GetData()->GetCurSize())\ - {\ - WriteToStringBuilder(*(arCells[unCol]->GetData()), *(m_pWriter->GetCurrentDocument()));\ - arCells[unCol]->GetData()->Clear();\ - }\ - else\ - m_pWriter->WriteEmptyParagraph();\ - \ - m_mTags[HTML_TAG(TD)]->Close(arSelectors);\ - }\ - \ - m_mTags[HTML_TAG(TR)]->Close(arSelectors);\ - }} - - if (!m_mTags[HTML_TAG(TABLE)]->Open(arSelectors, &oTable)) + if (!oTableReader.FromString(oReader.GetOuterXml()) || !oTableReader.ReadNextNode()) return false; - if (!oTable.HaveHeader()) + CTableElement* pTableElement{nullptr}; + TExternalTableData oExternalData; + + oExternalData.ReadStream = [this](XmlUtils::CXmlLiteReader& oReader, + std::vector& arSelectors) + { return ReadStream(oReader, arSelectors, true); }; + + oExternalData.GetSubClass = [this](XmlUtils::CXmlLiteReader& oReader, + std::vector& arSelectors) + { GetSubClass(oReader, arSelectors, m_oCSSCalculator); }; + + oExternalData.m_pWriter = m_pWriter; + + switch (m_pWriter->GetType()) { - if (m_mTags[HTML_TAG(TR)]->Open(arSelectors, boost::tuple(nullptr, oTable, ERowParseMode::Header, ERowPosition::First))) - m_mTags[HTML_TAG(TR)]->Close(arSelectors); - } - else - { - for (const std::vector& arHeader : oTable.GetHeaders()) - CONVERT_ROWS(arHeader, ERowParseMode::Header) + case EWriterType::Markdown: pTableElement = new CMarkdownTable(&oExternalData); break; + case EWriterType::OOXML: pTableElement = new COOXMLTable(&oExternalData); break; + default: + return false; } - CONVERT_ROWS(oTable.GetRows(), ERowParseMode::Body) - CONVERT_ROWS(oTable.GetFoothers(), ERowParseMode::Foother) + if (nullptr == pTableElement) + return false; - m_mTags[HTML_TAG(TABLE)]->Close(arSelectors); + if (!pTableElement->PreParse(oTableReader) || + !oTableReader.MoveToStart() || !oTableReader.ReadNextNode()) + { + delete pTableElement; + return false; + } + pTableElement->Normalize(); + pTableElement->Convert(oTableReader, arSelectors.back()); + + delete pTableElement; return true; } -void CHTMLReader::ReadTableCaption(CStorageTable& oTable, std::vector& arSelectors) -{ - if (nullptr == m_pWriter) - return; - - GetSubClass(arSelectors); - m_pWriter->SetDataOutput(oTable.GetCaptionData()); - - arSelectors.back().m_pCompiledStyle->m_oDisplay.SetVAlign(L"center", arSelectors.size()); - - ReadDefaultTag(HTML_TAG(CAPTION), arSelectors); - - m_pWriter->RevertDataOutput(); - arSelectors.pop_back(); -} - -void CalculateCellStyles(TTableCellStyle* pCellStyle, std::vector& arSelectors) -{ - if (NULL == pCellStyle) - return; - - pCellStyle->m_wsVAlign = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetVAlign().ToWString(); - pCellStyle->m_wsHAlign = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetHAlign().ToWString(); - pCellStyle->m_oBackground = arSelectors.back().m_pCompiledStyle->m_oBackground.GetColor(); - pCellStyle->m_oHeight = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetHeight(); - pCellStyle->m_oWidth = arSelectors.back().m_pCompiledStyle->m_oDisplay.GetWidth(); - pCellStyle->m_oPadding = arSelectors.back().m_pCompiledStyle->m_oPadding; - pCellStyle->m_oBorder = arSelectors.back().m_pCompiledStyle->m_oBorder; - - if (pCellStyle->m_wsHAlign.empty()) - pCellStyle->m_wsHAlign = arSelectors.back().m_pCompiledStyle->m_oText.GetAlign().ToWString(); -} - -struct TRowspanElement -{ - UINT m_unRowSpan; - UINT m_unColumnIndex; - const CStorageTableCell* m_pCell; - - TRowspanElement(UINT unRowSpan, UINT unColumnIndex, const CStorageTableCell* pCell) - : m_unRowSpan(unRowSpan), m_unColumnIndex(unColumnIndex), m_pCell(pCell) - {} -}; - -void CHTMLReader::ReadTableRows(CStorageTable& oTable, std::vector& arSelectors, ERowParseMode eMode) -{ - if (nullptr == m_pWriter) - return; - - std::vector arRowspanElements; - std::vector arRows; - - int nDeath = m_oLightReader.GetDepth(); - while (m_oLightReader.ReadNextSiblingNode(nDeath)) - { - if (L"tr" != m_oLightReader.GetName()) - continue; - - GetSubClass(arSelectors); - - CStorageTableRow *pRow = new CStorageTableRow(); - - for (std::vector::iterator itElement = arRowspanElements.begin(); itElement < arRowspanElements.end();) - { - pRow->InsertCell(CStorageTableCell::CreateEmpty(itElement->m_pCell->GetColspan(), true, itElement->m_pCell->GetStyles()), itElement->m_unColumnIndex); - - itElement->m_unRowSpan--; - if (1 == itElement->m_unRowSpan) - itElement = arRowspanElements.erase(itElement); - else - ++itElement; - } - - UINT unColumnIndex = 0; - int nTrDepth = m_oLightReader.GetDepth(); - while (m_oLightReader.ReadNextSiblingNode(nTrDepth)) - { - CStorageTableCell *pCell = new CStorageTableCell(); - - if (NULL == pCell) - continue; - - GetSubClass(arSelectors); - - std::vector arNewSelectors{(std::vector::const_iterator)std::find_if(arSelectors.begin(), arSelectors.end(), [](const NSCSS::CNode& oNode){ return L"table" == oNode.m_wsName; }), arSelectors.cend()}; - - CalculateCellStyles(pCell->GetStyles(), arNewSelectors); - - std::wstring wsValue; - - if (arSelectors.back().GetAttributeValue(L"colspan", wsValue)) - pCell->SetColspan(NSStringFinder::ToInt(wsValue, 1), pRow->GetIndex()); - - if (arSelectors.back().GetAttributeValue(L"rowspan", wsValue)) - { - pCell->SetRowspan(NSStringFinder::ToInt(wsValue, 1)); - - if (1 != pCell->GetRowspan()) - arRowspanElements.push_back({pCell->GetRowspan(), unColumnIndex, pCell}); - } - - // Читаем th. Ячейка заголовка таблицы. Выравнивание посередине. Выделяется полужирным - if(m_oLightReader.GetName() == L"th") - { - if (pCell->GetStyles()->m_wsHAlign.empty()) - arSelectors.back().m_pCompiledStyle->m_oText.SetAlign(L"center", arSelectors.size()); - - arSelectors.back().m_pCompiledStyle->m_oFont.SetWeight(L"bold", arSelectors.size()); - - m_pWriter->SetDataOutput(pCell->GetData()); - ReadStream(arSelectors, true); - m_pWriter->RevertDataOutput(); - } - // Читаем td. Ячейка таблицы - else if(m_oLightReader.GetName() == L"td") - { - m_pWriter->SetDataOutput(pCell->GetData()); - ReadStream(arSelectors, true); - m_pWriter->RevertDataOutput(); - } - - if (pRow->GetIndex() == MAXCOLUMNSINTABLE - 1) - { - while (m_oLightReader.ReadNextSiblingNode(nTrDepth)) - { - if (L"td" != m_oLightReader.GetName() && L"th" != m_oLightReader.GetName()) - continue; - - GetSubClass(arSelectors); - m_pWriter->SetDataOutput(pCell->GetData()); - ReadStream(arSelectors); - m_pWriter->RevertDataOutput(); - arSelectors.pop_back(); - } - } - - pRow->AddCell(pCell); - arSelectors.pop_back(); - - ++unColumnIndex; - - if (pRow->GetIndex() == MAXCOLUMNSINTABLE) - break; - } - - arSelectors.pop_back(); - arRows.push_back(pRow); - } - - oTable.AddRows(arRows, eMode); -} - -void CHTMLReader::ReadTableColspan(CStorageTable& oTable) -{ - std::vector arNodes; - GetSubClass(arNodes); - - CTableColgroup *pColgroup = new CTableColgroup(arNodes.back()); - - if (NULL == pColgroup) - return; - - oTable.AddColgroup(pColgroup); - - const int nDeath = m_oLightReader.GetDepth(); - if (!m_oLightReader.IsEmptyNode() && m_oLightReader.ReadNextSiblingNode2(nDeath)) - { - do - { - if (L"col" != m_oLightReader.GetName()) - continue; - - GetSubClass(arNodes); - - CTableCol *pCol = new CTableCol(arNodes.back()); - - if (NULL == pCol) - { - arNodes.pop_back(); - continue; - } - - CalculateCellStyles(pCol->GetStyle(), arNodes); - arNodes.pop_back(); - - if (NULL == pCol) - continue; - - pColgroup->AddCol(pCol); - } while(m_oLightReader.ReadNextSiblingNode2(nDeath)); - } - - if(pColgroup->Empty()) - { - std::map::const_iterator itFound = arNodes.begin()->m_mAttributes.find(L"span"); - - CTableCol *pCol = new CTableCol((arNodes.begin()->m_mAttributes.cend() != itFound) ? NSStringFinder::ToInt(itFound->second, 1) : 1); - - if (NULL == pCol) - return; - - CalculateCellStyles(pCol->GetStyle(), arNodes); - - pColgroup->AddCol(pCol); - } -} - bool CHTMLReader::ReadEmptyTag(UINT unTag, const std::vector& arSelectors) { if (!m_mTags[unTag]->Open(arSelectors)) @@ -1424,57 +1068,18 @@ bool CHTMLReader::ReadEmptyTag(UINT unTag, const std::vector& arSe return true; } -bool CHTMLReader::ReadDefaultTag(UINT unTag, std::vector& arSelectors) +bool CHTMLReader::ReadDefaultTag(XmlUtils::CXmlLiteReader& oReader, UINT unTag, std::vector& arSelectors) { if (!m_mTags[unTag]->Open(arSelectors)) return false; - const bool bResult{ReadStream(arSelectors)}; + const bool bResult{ReadStream(oReader, arSelectors)}; m_mTags[unTag]->Close(arSelectors); return bResult; } -void CHTMLReader::GetSubClass(std::vector& arSelectors) -{ - NSCSS::CNode oNode; - - oNode.m_wsName = m_oLightReader.GetName(); - // Стиль по атрибуту - std::wstring wsAttributeName; - - if (m_oLightReader.MoveToFirstAttribute()) - { - do - { - wsAttributeName = m_oLightReader.GetName(); - if(wsAttributeName == L"class") - oNode.m_wsClass = EncodeXmlString(m_oLightReader.GetText()); - else if(wsAttributeName == L"id") - { - oNode.m_wsId = EncodeXmlString(m_oLightReader.GetText()); - // WriteEmptyBookmark(oXml, oNode.m_wsId); - - // if (!m_oStylesCalculator.HaveStylesById(oNode.m_wsId)) - // oNode.m_wsId.clear(); - } - else if(wsAttributeName == L"style") - oNode.m_wsStyle += m_oLightReader.GetText(); - else - { - if (CheckArgumentMath(oNode.m_wsName, wsAttributeName)) - oNode.m_mAttributes[wsAttributeName] = m_oLightReader.GetText(); - } - }while(m_oLightReader.MoveToNextAttribute()); - } - - m_oLightReader.MoveToElement(); - arSelectors.push_back(oNode); - - m_oCSSCalculator.CalculateCompiledStyle(arSelectors); -} - inline std::wstring GetArgumentValue(XmlUtils::CXmlLiteReader& oLiteReader, const std::wstring& wsArgumentName, const std::wstring& wsDefaultValue) { if (!oLiteReader.MoveToFirstAttribute()) @@ -1530,4 +1135,43 @@ inline bool TagIsUnprocessed(const std::wstring& wsTagName) { return L"xml" == wsTagName; } + +inline void GetSubClass(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, NSCSS::CCssCalculator& oCSSCalculator) +{ + NSCSS::CNode oNode; + + oNode.m_wsName = oReader.GetName(); + // Стиль по атрибуту + std::wstring wsAttributeName; + + if (oReader.MoveToFirstAttribute()) + { + do + { + wsAttributeName = oReader.GetName(); + if(wsAttributeName == L"class") + oNode.m_wsClass = EncodeXmlString(oReader.GetText()); + else if(wsAttributeName == L"id") + { + oNode.m_wsId = EncodeXmlString(oReader.GetText()); + // WriteEmptyBookmark(oXml, oNode.m_wsId); + + // if (!m_oStylesCalculator.HaveStylesById(oNode.m_wsId)) + // oNode.m_wsId.clear(); + } + else if(wsAttributeName == L"style") + oNode.m_wsStyle += oReader.GetText(); + else + { + if (CheckArgumentMath(oNode.m_wsName, wsAttributeName)) + oNode.m_mAttributes[wsAttributeName] = oReader.GetText(); + } + }while(oReader.MoveToNextAttribute()); + } + + oReader.MoveToElement(); + arSelectors.push_back(oNode); + + oCSSCalculator.CalculateCompiledStyle(arSelectors); +} } diff --git a/HtmlFile2/HTMLReader.h b/HtmlFile2/HTMLReader.h index dce2cc1b85..4ccef4917e 100644 --- a/HtmlFile2/HTMLReader.h +++ b/HtmlFile2/HTMLReader.h @@ -1,6 +1,7 @@ #ifndef HTMLREADER_H #define HTMLREADER_H +#include #include #include "../Common/3dParty/html/css/src/CCssCalculator.h" @@ -17,7 +18,7 @@ namespace HTML { class CHTMLReader { - XmlUtils::CXmlLiteReader m_oLightReader; // SAX Reader + // XmlUtils::CXmlLiteReader m_oLightReader; // SAX Reader NSCSS::CCssCalculator m_oCSSCalculator; // Css калькулятор bool m_bIsTempDirOwner; @@ -55,7 +56,7 @@ private: void InitOOXMLTags(THTMLParameters* pParametrs = nullptr); void InitMDTags(TMarkdownParameters* pParametrs = nullptr); - bool IsHTML(); + bool IsHTML(XmlUtils::CXmlLiteReader& oReader); typedef std::function Convert_Func; @@ -64,29 +65,24 @@ private: bool Convert(const std::wstring& wsPath, Convert_Func Convertation); - void ReadStyle(); - void ReadStyle2(); - void ReadStyleFromNetwork(); + void ReadStyle(XmlUtils::CXmlLiteReader& oReader); + void ReadStyle2(XmlUtils::CXmlLiteReader& oReader); + void ReadStyleFromNetwork(XmlUtils::CXmlLiteReader& oReader); - void ReadDocument(); - void ReadHead(); - void ReadBody(); + void ReadDocument(XmlUtils::CXmlLiteReader& oReader); + void ReadHead(XmlUtils::CXmlLiteReader& oReader); + void ReadBody(XmlUtils::CXmlLiteReader& oReader); - bool ReadStream(std::vector& arSelectors, bool bInsertEmptyP = false); - bool ReadInside(std::vector& arSelectors); + bool ReadStream(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, bool bInsertEmptyP = false); + bool ReadInside(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors); - bool ReadText(std::vector& arSelectors); + bool ReadText(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors); - bool ReadSVG(const std::vector& arSelectors); + bool ReadSVG(XmlUtils::CXmlLiteReader& oReader, const std::vector& arSelectors); bool ReadEmptyTag(UINT unTag, const std::vector& arSelectors); - bool ReadDefaultTag(UINT unTag, std::vector& arSelectors); + bool ReadDefaultTag(XmlUtils::CXmlLiteReader& oReader, UINT unTag, std::vector& arSelectors); - bool ReadTable(std::vector& arSelectors); - void ReadTableCaption(CStorageTable& oTable, std::vector& arSelectors); - void ReadTableRows(CStorageTable& oTable, std::vector& arSelectors, ERowParseMode eMode); - void ReadTableColspan(CStorageTable& oTable); - - void GetSubClass(std::vector& arSelectors); + bool ReadTable(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors); }; } diff --git a/HtmlFile2/Table.cpp b/HtmlFile2/Table.cpp index 7d3f3d260d..c801f80968 100644 --- a/HtmlFile2/Table.cpp +++ b/HtmlFile2/Table.cpp @@ -1,16 +1,12 @@ #include "Table.h" #include "Common.h" -#include "src/StringFinder.h" +#include "src/CCompiledStyle.h" namespace HTML { #define MAX_STRING_BLOCK_SIZE (size_t)10485760 -#define RELEASE_VECTOR_PTR(vector_object, object_type) \ - for (object_type* pElement : vector_object) \ - RELEASEOBJECT(pElement) - #define FIRST_ELEMENT 0x00000001 #define LAST_ELEMENT 0x00000002 #define MID_ELEMENT 0x00000004 @@ -26,342 +22,323 @@ namespace HTML #define DEFAULT_PAGE_WIDTH 12240 // Значение в Twips #define DEFAULT_PAGE_HEIGHT 15840 // Значение в Twips -TTableRowStyle::TTableRowStyle() - : m_unMaxIndex(0), m_unMaxHeight(0), m_bIsHeader(false) +// const TTableCellStyle* CStorageTable::GetColStyle(UINT unColumnNumber) const +// { +// if (m_arColgroups.empty()) +// return NULL; + +// UINT unCurrentNumber = 0; + +// for (const CTableColgroup* pColgroup : m_arColgroups) +// { +// for (const CTableCol* pCol : pColgroup->GetCols()) +// { +// unCurrentNumber += pCol->GetSpan(); + +// if (unCurrentNumber >= unColumnNumber) +// return pCol->GetStyle(); +// } +// } + +// return NULL; +// } + +// void CStorageTable::AddColgroup(CTableColgroup* pElement) +// { +// if (NULL != pElement) +// m_arColgroups.push_back(pElement); +// } + +// void CStorageTable::RecalculateMaxColumns() +// { +// for (const std::vector& arHeaders : m_arHeaders) +// for (const CStorageTableRow* pHeader : arHeaders) +// m_unMaxColumns = std::max(m_unMaxColumns, pHeader->GetIndex()); + +// for (const CStorageTableRow* pRow : m_arRows) +// m_unMaxColumns = std::max(m_unMaxColumns, pRow->GetIndex()); + +// for (const CStorageTableRow* pFoother : m_arFoother) +// m_unMaxColumns = std::max(m_unMaxColumns, pFoother->GetIndex()); +// } + +// void CStorageTable::Shorten() +// { +// UINT unIndex = 0; +// CStorageTableCell* pCell = NULL; + +// UINT unMaxIndex = 0; //Максимальный индекс без учета строк, где имеется только 1 ячейка + +// for (const CStorageTableRow* pRow : m_arRows) +// { +// if (1 < pRow->GetCount()) +// unMaxIndex = std::max(unMaxIndex, pRow->GetIndex()); +// } + +// while (unIndex < m_arMinColspan.size()) +// { +// for (CStorageTableRow* pRow : m_arRows) +// { +// if (0 != unMaxIndex && 1 == pRow->GetCount() && pRow->GetIndex() > unMaxIndex) +// { +// pCell = (*pRow)[unIndex]; + +// if (NULL == pCell) +// continue; + +// pCell->SetColspan(unMaxIndex, MAXCOLUMNSINTABLE); +// continue; +// } + +// if (1 == m_arMinColspan[unIndex]) +// break; + +// pCell = (*pRow)[unIndex]; + +// if (NULL == pCell) +// continue; + +// if (1 < pCell->GetColspan() && pCell->GetColspan() > m_arMinColspan[unIndex]) +// { +// pCell->SetColspan(m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); +// continue; +// } + +// if ((*pRow)[unIndex]->GetColspan() == m_arMinColspan[unIndex] + 1) +// (*pRow)[unIndex]->SetColspan(2, MAXCOLUMNSINTABLE); +// else if ((*pRow)[unIndex]->GetColspan() > m_arMinColspan[unIndex]) +// (*pRow)[unIndex]->SetColspan((*pRow)[unIndex]->GetColspan() - m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); +// } + +// ++unIndex; +// } +// } + + +CTableElementCell::CTableElementCell(bool bIsFilling) + : m_eType{(bIsFilling) ? ETableElement::FillingCell : ETableElement::Cell} {} -bool TTableRowStyle::Empty() const -{ - return 0 == m_unMaxHeight && false == m_bIsHeader; -} - -TTableCellStyle::TTableCellStyle() +CTableElementCell::~CTableElementCell() {} -bool TTableCellStyle::Empty() +ETableElement CTableElementCell::GetType() const { - return m_oWidth.Empty() && m_oHeight.Empty() && m_oBorder.Empty() && m_oPadding.Empty() && m_wsVAlign.empty() && m_wsVAlign.empty(); + return m_eType; } -void TTableCellStyle::Copy(const TTableCellStyle* pTableCellStyle) +void CTableElementCell::IsFlatTable() { - if (NULL == pTableCellStyle) - return; - - m_oWidth = pTableCellStyle->m_oWidth; - m_oHeight = pTableCellStyle->m_oHeight; - m_oBorder = pTableCellStyle->m_oBorder; - m_oPadding = pTableCellStyle->m_oPadding; - m_oBackground = pTableCellStyle->m_oBackground; - - m_wsHAlign = pTableCellStyle->m_wsHAlign; - m_wsVAlign = pTableCellStyle->m_wsVAlign; + m_eType = ETableElement::FlatTable; } -TTableCellStyle& TTableCellStyle::operator+=(const TTableCellStyle* pCellStyle) -{ - if (NULL == pCellStyle) - return *this; - - m_oWidth += pCellStyle->m_oWidth; - m_oHeight += pCellStyle->m_oHeight; - m_oBorder += pCellStyle->m_oBorder; - m_oPadding += pCellStyle->m_oPadding; - m_oBackground += pCellStyle->m_oBackground; - - if (m_wsHAlign.empty()) - m_wsHAlign = pCellStyle->m_wsHAlign; - - if (m_wsVAlign.empty()) - m_wsVAlign = pCellStyle->m_wsVAlign; - - return *this; -} - -CStorageTableCell::CStorageTableCell() - : m_unColspan(1), m_unRowSpan(1), m_bIsMerged(false), m_bIsEmpty(false), m_oData(30) +CTableMatrix::CTableMatrix() {} -CStorageTableCell::CStorageTableCell(UINT unColspan, UINT unRowspan, bool bIsMerged, bool bIsEmpty) - : m_unColspan(unColspan), m_unRowSpan(unRowspan), m_bIsMerged(bIsMerged), - m_bIsEmpty(bIsEmpty) -{} - -CStorageTableCell::CStorageTableCell(CStorageTableCell& oCell) - : m_unColspan(oCell.m_unColspan), m_unRowSpan(oCell.m_unRowSpan), m_bIsMerged(oCell.m_bIsMerged), - m_bIsEmpty(oCell.m_bIsEmpty), m_oStyles(oCell.m_oStyles) +CTableMatrix::~CTableMatrix() { - WriteToStringBuilder(oCell.m_oData, m_oData); -} - -bool CStorageTableCell::Empty() const -{ - return m_bIsEmpty; -} - -bool CStorageTableCell::Merged() const -{ - return m_bIsMerged; -} - -CStorageTableCell* CStorageTableCell::Copy() -{ - return new CStorageTableCell(*this); -} - -CStorageTableCell* CStorageTableCell::CreateEmpty(UINT unColspan, bool m_bIsMerged, const TTableCellStyle* pStyle) -{ - CStorageTableCell *pCell = new CStorageTableCell(unColspan, 1, m_bIsMerged, true); - - pCell->m_oStyles.Copy(pStyle); - - return pCell; -} - -CStorageTableCell* CStorageTableCell::CreateEmpty(const TTableCellStyle* pStyle) -{ - CStorageTableCell *pCell = new CStorageTableCell(1, 1, false, true); - - pCell->m_oStyles.Copy(pStyle); - - return pCell; -} - -void CStorageTableCell::SetColspan(UINT unColspan, UINT unCurrentIndex) -{ - if (MAXCOLUMNSINTABLE - 1 != unCurrentIndex) - m_unColspan = std::min(MAXCOLUMNSINTABLE - 1 - unCurrentIndex, unColspan); - else - m_unColspan = 1; -} - -UINT CStorageTableCell::GetColspan() const -{ - return m_unColspan; -} - -void CStorageTableCell::SetRowspan(UINT unRowspan) -{ - m_unRowSpan = unRowspan; -} - -UINT CStorageTableCell::GetRowspan() const -{ - return m_unRowSpan; -} - -NSStringUtils::CStringBuilder* CStorageTableCell::GetData() -{ - return &m_oData; -} - -const TTableCellStyle* CStorageTableCell::GetStyles() const -{ - return &m_oStyles; -} - -TTableCellStyle* CStorageTableCell::GetStyles() -{ - return &m_oStyles; -} - -void CStorageTableCell::SetWidth(const NSCSS::NSProperties::CDigit& oWidth) -{ - m_oStyles.m_oWidth = oWidth; -} - -void CStorageTableCell::SetHeight(const NSCSS::NSProperties::CDigit& oHeight) -{ - m_oStyles.m_oHeight = oHeight; -} - -UINT CStorageTableCell::GetWidth() const -{ - return m_oStyles.m_oWidth.ToInt(NSCSS::Twips, DEFAULT_PAGE_WIDTH); -} - -UINT CStorageTableCell::GetHeight() const -{ - return m_oStyles.m_oHeight.ToInt(NSCSS::Twips, DEFAULT_PAGE_HEIGHT); -} - -void CStorageTableCell::SetBorder(const NSCSS::NSProperties::CBorder& oBorder) -{ - m_oStyles.m_oBorder = oBorder; -} - -void CStorageTableCell::ClearTopBorder() -{ - m_oStyles.m_oBorder.SetTopSide(L"none", 0, true); -} - -void CStorageTableCell::ClearLeftBorder() -{ - m_oStyles.m_oBorder.SetLeftSide(L"none", 0, true); -} - -void CStorageTableCell::ClearBottomBorder() -{ - m_oStyles.m_oBorder.SetBottomSide(L"none", 0, true); -} - -void CStorageTableCell::ClearRightBorder() -{ - m_oStyles.m_oBorder.SetRightSide(L"none", 0, true); -} - -void CStorageTableCell::SetPadding(const NSCSS::NSProperties::CIndent& oPadding) -{ - m_oStyles.m_oPadding = oPadding; -} - -void CStorageTableCell::SetHAlign(const std::wstring& wsAlign) -{ - m_oStyles.m_wsHAlign = wsAlign; -} - -void CStorageTableCell::SetVAlign(const std::wstring& wsAlign) -{ - m_oStyles.m_wsVAlign = wsAlign; -} - -void CStorageTableCell::SetBackground(const NSCSS::NSProperties::CColor& oColor) -{ - m_oStyles.m_oBackground = oColor; -} - -CStorageTableRow::CStorageTableRow() -{} - -CStorageTableRow::~CStorageTableRow() -{ - for (CStorageTableCell* pCell : m_arCells) - RELEASEOBJECT(pCell); -} - -void CStorageTableRow::AddCell(CStorageTableCell* pCell) -{ - InsertCell(pCell, -1); -} - -void CStorageTableRow::InsertCell(CStorageTableCell* pCell, int nPosition) -{ - if (NULL == pCell) - return; - - if (nPosition < 0) + for (Row& oRow : m_arCells) { - std::vector::iterator itFoundEmpty = std::find_if(m_arCells.begin(), m_arCells.end(), [](CStorageTableCell* pCell) { return pCell->Empty() && !pCell->Merged(); }); - - if (m_arCells.end() != itFoundEmpty) + for (ITableElementCell* pCell : oRow) { - --m_oStyles.m_unMaxIndex; - delete *itFoundEmpty; - *itFoundEmpty = pCell; - - if (1 != pCell->GetColspan()) - { - ++itFoundEmpty; - UINT unColspan = pCell->GetColspan() - 1; - - while (m_arCells.end() != itFoundEmpty && (*itFoundEmpty)->Empty() && unColspan > 0) - { - --m_oStyles.m_unMaxIndex; - --unColspan; - delete (*itFoundEmpty); - itFoundEmpty = m_arCells.erase(itFoundEmpty); - } - - if (unColspan != 0) - pCell->SetColspan(pCell->GetColspan() - unColspan, MAXCOLUMNSINTABLE); - } - } - else - m_arCells.push_back(pCell); - } - else if (nPosition >= m_arCells.size()) - { - const UINT unMissingCount = nPosition - m_arCells.size(); - - for (UINT unIndex = 0; unIndex < unMissingCount; ++unIndex) - m_arCells.push_back(CStorageTableCell::CreateEmpty()); - - m_oStyles.m_unMaxIndex += unMissingCount; - - m_arCells.push_back(pCell); - } - else if (m_arCells[nPosition]->Empty()) - { - delete m_arCells[nPosition]; - --m_oStyles.m_unMaxIndex; - m_arCells[nPosition] = pCell; - - if (1 != pCell->GetColspan()) - { - ++nPosition; - UINT unDeleteCount = pCell->GetColspan() - 1; - while (nPosition < m_arCells.size() && m_arCells[nPosition]->Empty() && !m_arCells[nPosition]->Merged() && unDeleteCount > 0) - { - delete m_arCells[nPosition]; - --m_oStyles.m_unMaxIndex; - m_arCells.erase(m_arCells.begin() + nPosition); - --unDeleteCount; - } - - if (0 != unDeleteCount) - pCell->SetColspan(pCell->GetColspan() - unDeleteCount, MAXCOLUMNSINTABLE); + if (nullptr != pCell) + delete pCell; } } - else - m_arCells.insert(m_arCells.begin() + nPosition, pCell); - - m_oStyles.m_unMaxIndex += pCell->GetColspan(); - - if (1 == pCell->GetColspan() && 1 == pCell->GetRowspan()) - m_oStyles.m_unMaxHeight = std::max(m_oStyles.m_unMaxHeight, pCell->GetHeight()); } -UINT CStorageTableRow::GetIndex() const -{ - return m_oStyles.m_unMaxIndex; -} - -UINT CStorageTableRow::GetCount() const -{ - return m_arCells.size(); -} - -CStorageTableCell* CStorageTableRow::operator[](UINT unIndex) -{ - if (unIndex >= m_arCells.size()) - return NULL; - - return m_arCells[unIndex]; -} - -bool CStorageTableRow::Empty() const +bool CTableMatrix::Empty() const { return m_arCells.empty(); } -const TTableRowStyle& CStorageTableRow::GetStyles() const +bool CTableMatrix::SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell) { - return m_oStyles; + if (nullptr == pCell) + return false; + + if (m_arCells.size() <= unRowIndex) + { + if (!m_arCells.empty()) + { + const size_t unMaxColumn{m_arCells.back().size()}; + const size_t unOldSize{m_arCells.size()}; + m_arCells.resize(unRowIndex + 1); + + for (size_t unIndex = unOldSize; unIndex < m_arCells.size(); ++unIndex) + m_arCells[unIndex].resize(unMaxColumn); + } + else + m_arCells.resize(unRowIndex + 1); + } + + if (m_arCells[unRowIndex].size() <= unColumnIndex) + { + m_arCells[unRowIndex].resize(unColumnIndex + 1); + m_arCells[unRowIndex][unColumnIndex] = pCell; + return true; + } + + if (nullptr != m_arCells[unRowIndex][unColumnIndex]) + delete m_arCells[unRowIndex][unColumnIndex]; + + m_arCells[unRowIndex][unColumnIndex] = pCell; + + return true; } -const std::vector& CStorageTableRow::GetCells() const +bool CTableMatrix::IsFillingCell(size_t unRowIndex, size_t unColumnIndex) const +{ + if (unRowIndex >= m_arCells.size()) + return false; + + if (unColumnIndex >= m_arCells[unRowIndex].size()) + return false; + + return (nullptr != m_arCells[unRowIndex][unColumnIndex]) ? ETableElement::FillingCell == m_arCells[unRowIndex][unColumnIndex]->GetType() : false; +} + +bool CTableMatrix::IsNotNullCell(size_t unRowIndex, size_t unColumnIndex) const +{ + if (unRowIndex >= m_arCells.size()) + return false; + + if (unColumnIndex >= m_arCells[unRowIndex].size()) + return false; + + return nullptr != m_arCells[unRowIndex][unColumnIndex]; +} + +size_t CTableMatrix::GetRowSize() const +{ + return m_arCells.size(); +} + +size_t CTableMatrix::GetColumnSize() const +{ + return (m_arCells.empty()) ? 0 : m_arCells.front().size(); +} + +Table& CTableMatrix::GetMatrixCells() { return m_arCells; } -CTableCol::CTableCol(UINT unSpan) - : m_unSpan(unSpan) +const Table& CTableMatrix::GetMatrixCells() const +{ + return m_arCells; +} + +const ITableElementCell* CTableMatrix::GetCell(size_t unRowIndex, size_t unColumnIndex) const +{ + if (unRowIndex >= m_arCells.size()) + return nullptr; + + if (unColumnIndex >= m_arCells[unRowIndex].size()) + return nullptr; + + return m_arCells[unRowIndex][unColumnIndex]; +} + +bool TExternalTableData::IsValid() const +{ + return true; +} + +CTableElement::CTableElement(TExternalTableData *pExternalData) + : m_pCaption(nullptr), m_pExternalData(pExternalData) {} -CTableCol::CTableCol(const NSCSS::CNode& oTableColNode) - : m_unSpan(1) +CTableElement::~CTableElement() { - m_unSpan = NSStringFinder::ToInt(oTableColNode.GetAttributeValue(L"span")); + if (nullptr != m_pCaption) + delete m_pCaption; + + for (CTableColgroup* pColgroup : m_arColgroups) + if (nullptr != pColgroup) + delete pColgroup; +} + +ETableElement CTableElement::GetType() const +{ + return ETableElement::Table; +} + +bool CTableElement::Empty() const +{ + return m_oHeader.Empty() && m_oBody.Empty() && m_oFoother.Empty(); +} + +bool CTableElement::HaveCaption() const +{ + return nullptr != m_pCaption && 0 != m_pCaption->GetCurSize(); +} + +const NSCSS::CCompiledStyle* CTableElement::GetColStyle(size_t unColIndex) const +{ + if (m_arColgroups.empty()) + return nullptr; + + const NSCSS::CCompiledStyle* pColStyle{nullptr}; + size_t unSkipSpans{0}; + + for (const CTableColgroup* pColgroup : m_arColgroups) + { + pColStyle = pColgroup->GetColStyle(unColIndex - unSkipSpans); + + if (nullptr != pColStyle) + return pColStyle; + + unSkipSpans += pColgroup->GetTotalSpans(); + } + + return nullptr; +} + +bool MoveToNextTableCell(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, std::stack& arDepths, TExternalTableData::FuncGetSubClass& GetSubClass) +{ + while (!arDepths.empty() && !oReader.ReadNextSiblingNode2(arDepths.top())) + { + arDepths.pop(); + arSelectors.pop_back(); + } + + if (arDepths.empty()) + return false; + + do + { + const std::wstring wsName = oReader.GetName(); + + if (L"#text" == wsName || L"colgroup" == wsName || L"caption" == wsName) + return MoveToNextTableCell(oReader, arSelectors, arDepths, GetSubClass); + + GetSubClass(oReader, arSelectors); + + if (L"td" == wsName || L"th" == wsName) + return true; + else if (L"table" == wsName) + return false; + + arDepths.push(oReader.GetDepth()); + + return MoveToNextTableCell(oReader, arSelectors, arDepths, GetSubClass); + }while(oReader.ReadNextSiblingNode2(arDepths.top())); + + return false; +} + +CTableCol::CTableCol(NSCSS::CNode& oTableColNode) + : m_pStyle(oTableColNode.m_pCompiledStyle) +{ + oTableColNode.m_pCompiledStyle = nullptr; + m_unSpan = NSStringFinder::ToInt(oTableColNode.GetAttributeValue(L"span"), 1); +} + +CTableCol::~CTableCol() +{ + if (nullptr != m_pStyle) + delete m_pStyle; } UINT CTableCol::GetSpan() const @@ -369,25 +346,22 @@ UINT CTableCol::GetSpan() const return m_unSpan; } -TTableCellStyle* CTableCol::GetStyle() +const NSCSS::CCompiledStyle* CTableCol::GetStyle() const { - return &m_oStyle; + return m_pStyle; } -const TTableCellStyle* CTableCol::GetStyle() const -{ - return &m_oStyle; -} - -CTableColgroup::CTableColgroup(NSCSS::CNode& oTableColgroupNode) - : m_unWidth(0) +CTableColgroup::CTableColgroup(const NSCSS::CNode& oTableColgroupNode) + : m_unTotalSpans(0) { m_unWidth = NSStringFinder::ToInt(oTableColgroupNode.GetAttributeValue(L"width"), 0); } CTableColgroup::~CTableColgroup() { - RELEASE_VECTOR_PTR(m_arCols, CTableCol) + for (CTableCol* pCol : m_arCols) + if (nullptr != pCol) + delete pCol; } bool CTableColgroup::Empty() const @@ -397,8 +371,16 @@ bool CTableColgroup::Empty() const void CTableColgroup::AddCol(CTableCol* pCol) { - if (NULL != pCol) - m_arCols.push_back(pCol); + if (nullptr == pCol) + return; + + m_arCols.push_back(pCol); + m_unTotalSpans += pCol->GetSpan(); +} + +UINT CTableColgroup::GetTotalSpans() const +{ + return m_unTotalSpans; } const std::vector& CTableColgroup::GetCols() const @@ -406,310 +388,20 @@ const std::vector& CTableColgroup::GetCols() const return m_arCols; } -TTableStyles::TTableStyles() - : m_nCellSpacing(-1), m_enRules(None) -{} - -bool TTableStyles::Empty() const +const NSCSS::CCompiledStyle* CTableColgroup::GetColStyle(size_t unIndex) const { - return m_oPadding.Empty() && m_oMargin.Empty() && m_oBorder.Empty() && m_oWidth.Empty() && -1 == m_nCellSpacing && m_wsAlign.empty(); -} + if (m_arCols.empty()) + return nullptr; -CStorageTable::CStorageTable() - : m_unMaxColumns(0) -{} - -CStorageTable::~CStorageTable() -{ - for (std::vector& arHeaders : m_arHeaders) - RELEASE_VECTOR_PTR(arHeaders, CStorageTableRow) - - RELEASE_VECTOR_PTR(m_arFoother, CStorageTableRow) - RELEASE_VECTOR_PTR(m_arRows, CStorageTableRow) - RELEASE_VECTOR_PTR(m_arColgroups, CTableColgroup) -} - -CStorageTableRow* CStorageTable::operator[](UINT unIndex) -{ - if (unIndex < m_arRows.size()) - return m_arRows[unIndex]; - - return NULL; -} - -bool CStorageTable::Empty() const -{ - return m_arHeaders.empty() && m_arRows.empty() && m_arFoother.empty(); -} - -bool CStorageTable::HaveCaption() -{ - return 0 != m_oCaption.GetCurSize(); -} - -bool CStorageTable::HaveColgroups() const -{ - return !m_arColgroups.empty(); -} - -bool CStorageTable::HaveHeader() const -{ - return !m_arHeaders.empty(); -} - -UINT CStorageTable::GetRowCount() const -{ - return m_arRows.size(); -} - -const TTableStyles& CStorageTable::GetTableStyles() const -{ - return m_oStyles; -} - -const TTableCellStyle* CStorageTable::GetColStyle(UINT unColumnNumber) const -{ - if (m_arColgroups.empty()) - return NULL; - - UINT unCurrentNumber = 0; - - for (const CTableColgroup* pColgroup : m_arColgroups) + size_t unCurrentIndex{0}; + for (const CTableCol* pCol : m_arCols) { - for (const CTableCol* pCol : pColgroup->GetCols()) - { - unCurrentNumber += pCol->GetSpan(); + if (unCurrentIndex <= unIndex && (unCurrentIndex + pCol->GetSpan()) > unIndex) + return pCol->GetStyle(); - if (unCurrentNumber >= unColumnNumber) - return pCol->GetStyle(); - } + unCurrentIndex += pCol->GetSpan(); } - return NULL; + return nullptr; } - -void CStorageTable::AddRows(std::vector& arRows, ERowParseMode eParseMode) -{ - if (arRows.empty()) - return; - - if (ERowParseMode::Foother == eParseMode && !m_arFoother.empty()) - eParseMode = ERowParseMode::Header; - - if (ERowParseMode::Header == eParseMode) - m_arHeaders.push_back({}); - - for (CStorageTableRow* pRow : arRows) - AddRow(pRow, eParseMode); -} - -void CStorageTable::AddRow(CStorageTableRow* pRow, ERowParseMode eParseMode) -{ - if (NULL == pRow) - return; - - for (UINT unIndex = 0; unIndex < pRow->GetCount(); ++unIndex) - { - if (unIndex >= m_arMinColspan.size()) - m_arMinColspan.push_back((*pRow)[unIndex]->GetColspan()); - else if ((*pRow)[unIndex]->GetColspan() < m_arMinColspan[unIndex]) - m_arMinColspan[unIndex] = (*pRow)[unIndex]->GetColspan(); - } - - switch (eParseMode) - { - default: - case ERowParseMode::Body: - { - m_arRows.push_back(pRow); - break; - } - case ERowParseMode::Header: - { - if (m_arHeaders.empty()) - m_arHeaders.push_back({}); - - m_arHeaders.back().push_back(pRow); - break; - } - case ERowParseMode::Foother: - { - m_arFoother.push_back(pRow); - break; - } - } -} - -NSStringUtils::CStringBuilder* CStorageTable::GetCaptionData() -{ - return &m_oCaption; -} - -void CStorageTable::SetPadding(const NSCSS::NSProperties::CIndent& oPadding) -{ - m_oStyles.m_oPadding = oPadding; -} - -const NSCSS::NSProperties::CIndent& CStorageTable::GetPadding() const -{ - return m_oStyles.m_oPadding; -} - -void CStorageTable::SetMargin(const NSCSS::NSProperties::CIndent& oMargin) -{ - m_oStyles.m_oMargin = oMargin; -} - -void CStorageTable::SetBorder(const NSCSS::NSProperties::CBorder& oBorder) -{ - m_oStyles.m_oBorder = oBorder; -} - -void CStorageTable::SetWidth(const NSCSS::NSProperties::CDigit& oWidth) -{ - m_oStyles.m_oWidth = oWidth; -} - -void CStorageTable::SetCellSpacing(int nCellSpacing) -{ - m_oStyles.m_nCellSpacing = nCellSpacing; -} - -void CStorageTable::SetAlign(const std::wstring& wsValue) -{ - m_oStyles.m_wsAlign = wsValue; -} - -void CStorageTable::SetRules(const std::wstring& wsValue) -{ - if (wsValue.empty()) - return; - - if (NSStringFinder::Equals(wsValue, L"all")) - m_oStyles.m_enRules = TTableStyles::ETableRules::All; - else if (NSStringFinder::Equals(wsValue, L"groups")) - m_oStyles.m_enRules = TTableStyles::ETableRules::Groups; - else if (NSStringFinder::Equals(wsValue, L"cols")) - m_oStyles.m_enRules = TTableStyles::ETableRules::Cols; - else if (NSStringFinder::Equals(wsValue, L"none")) - m_oStyles.m_enRules = TTableStyles::ETableRules::None; - else if (NSStringFinder::Equals(wsValue, L"rows")) - m_oStyles.m_enRules = TTableStyles::ETableRules::Rows; -} - -void CStorageTable::AddColgroup(CTableColgroup* pElement) -{ - if (NULL != pElement) - m_arColgroups.push_back(pElement); -} - -void CStorageTable::RecalculateMaxColumns() -{ - for (const std::vector& arHeaders : m_arHeaders) - for (const CStorageTableRow* pHeader : arHeaders) - m_unMaxColumns = std::max(m_unMaxColumns, pHeader->GetIndex()); - - for (const CStorageTableRow* pRow : m_arRows) - m_unMaxColumns = std::max(m_unMaxColumns, pRow->GetIndex()); - - for (const CStorageTableRow* pFoother : m_arFoother) - m_unMaxColumns = std::max(m_unMaxColumns, pFoother->GetIndex()); -} - -void CStorageTable::Shorten() -{ - UINT unIndex = 0; - CStorageTableCell* pCell = NULL; - - UINT unMaxIndex = 0; //Максимальный индекс без учета строк, где имеется только 1 ячейка - - for (const CStorageTableRow* pRow : m_arRows) - { - if (1 < pRow->GetCount()) - unMaxIndex = std::max(unMaxIndex, pRow->GetIndex()); - } - - while (unIndex < m_arMinColspan.size()) - { - for (CStorageTableRow* pRow : m_arRows) - { - if (0 != unMaxIndex && 1 == pRow->GetCount() && pRow->GetIndex() > unMaxIndex) - { - pCell = (*pRow)[unIndex]; - - if (NULL == pCell) - continue; - - pCell->SetColspan(unMaxIndex, MAXCOLUMNSINTABLE); - continue; - } - - if (1 == m_arMinColspan[unIndex]) - break; - - pCell = (*pRow)[unIndex]; - - if (NULL == pCell) - continue; - - if (1 < pCell->GetColspan() && pCell->GetColspan() > m_arMinColspan[unIndex]) - { - pCell->SetColspan(m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); - continue; - } - - if ((*pRow)[unIndex]->GetColspan() == m_arMinColspan[unIndex] + 1) - (*pRow)[unIndex]->SetColspan(2, MAXCOLUMNSINTABLE); - else if ((*pRow)[unIndex]->GetColspan() > m_arMinColspan[unIndex]) - (*pRow)[unIndex]->SetColspan((*pRow)[unIndex]->GetColspan() - m_arMinColspan[unIndex], MAXCOLUMNSINTABLE); - } - - ++unIndex; - } -} - -void CStorageTable::CompleteTable() -{ - UINT unMaxIndex = 0; - - for (CStorageTableRow* pRow : m_arRows) - unMaxIndex = std::max(unMaxIndex, pRow->GetIndex()); - - for (CStorageTableRow* pRow : m_arRows) - { - if (NULL == pRow || 0 == pRow->GetCount()) - continue; - - for (UINT unIndex = pRow->GetIndex(); unIndex < unMaxIndex; ++unIndex) - pRow->InsertCell(CStorageTableCell::CreateEmpty(), unIndex); - } - - RecalculateMaxColumns(); -} - -const std::vector>& CStorageTable::GetHeaders() const -{ - return m_arHeaders; -} - -const std::vector& CStorageTable::GetFoothers() const -{ - return m_arFoother; -} - -const std::vector& CStorageTable::GetRows() const -{ - return m_arRows; -} - -const std::vector CStorageTable::GetColgroups() const -{ - return m_arColgroups; -} - -UINT CStorageTable::GetMaxColumns() const -{ - return m_unMaxColumns; -} - } diff --git a/HtmlFile2/Table.h b/HtmlFile2/Table.h index b3c828ece6..b8b6f51c50 100644 --- a/HtmlFile2/Table.h +++ b/HtmlFile2/Table.h @@ -2,9 +2,13 @@ #define TABLE_H #include "../DesktopEditor/common/StringBuilder.h" -#include "../Common/3dParty/html/css/src/StyleProperties.h" -#include "../Common/3dParty/html/css/src/CNode.h" +#include "../DesktopEditor/xml/include/xmlutils.h" +#include "../Common/3dParty/html/css/src/CCssCalculator.h" +#include "Writers/IWriter.h" +#include "src/StringFinder.h" + +#include #include namespace HTML @@ -12,244 +16,260 @@ namespace HTML #define MAXCOLUMNSINTABLE 63 #define MAXROWSINTABLE 32767 -enum class ERowParseMode +struct TCurentTablePosition { - Header, - Body, - Foother + size_t m_unRowIndex{0}; + size_t m_unColumnIndex{0}; + + size_t m_unStartRowIndex{0}; + size_t m_unStartColumnIndex{0}; }; -enum class ERowPosition + +enum class ETableElement { - First, - Middle, - Last + FillingCell, + Cell, + FlatTable, + Table }; -struct TTableRowStyle -{ - UINT m_unMaxIndex; - UINT m_unMaxHeight; - bool m_bIsHeader; - - TTableRowStyle(); - - bool Empty() const; -}; - -struct TTableCellStyle -{ - NSCSS::NSProperties::CDigit m_oWidth; - NSCSS::NSProperties::CDigit m_oHeight; - NSCSS::NSProperties::CBorder m_oBorder; - NSCSS::NSProperties::CIndent m_oPadding; - NSCSS::NSProperties::CColor m_oBackground; - - std::wstring m_wsHAlign; - std::wstring m_wsVAlign; - - TTableCellStyle(); - - bool Empty(); - void Copy(const TTableCellStyle* pTableCellStyle); - - TTableCellStyle& operator+=(const TTableCellStyle* pCellStyle); -}; - -class CStorageTable; - -class CStorageTableCell +class ITableElementCell { public: - CStorageTableCell(); - CStorageTableCell(UINT unColspan, UINT unRowspan, bool bIsMerged, bool bIsEmpty); - CStorageTableCell(CStorageTableCell& oCell); + ITableElementCell() = default; + virtual ~ITableElementCell() = default; - bool Empty() const; - bool Merged() const; - - CStorageTableCell* Copy(); - - static CStorageTableCell* CreateEmpty(UINT unColspan = 1, bool m_bIsMerged = false, const TTableCellStyle* pStyle = NULL); - static CStorageTableCell* CreateEmpty(const TTableCellStyle* pStyle); - - void SetColspan(UINT unColspan, UINT unCurrentIndex); - UINT GetColspan() const; - - void SetRowspan(UINT unRowspan); - UINT GetRowspan() const; - - NSStringUtils::CStringBuilder* GetData(); - - const TTableCellStyle* GetStyles() const; - TTableCellStyle* GetStyles(); - - void SetWidth(const NSCSS::NSProperties::CDigit& oWidth); - void SetHeight(const NSCSS::NSProperties::CDigit& oHeight); - - UINT GetWidth() const; - UINT GetHeight() const; - - void SetBorder(const NSCSS::NSProperties::CBorder& oBorder); - - void ClearTopBorder(); - void ClearLeftBorder(); - void ClearBottomBorder(); - void ClearRightBorder(); - - void SetPadding(const NSCSS::NSProperties::CIndent& oPadding); - void SetHAlign(const std::wstring& wsAlign); - void SetVAlign(const std::wstring& wsAlign); - void SetBackground(const NSCSS::NSProperties::CColor& oColor); -private: - UINT m_unColspan; - UINT m_unRowSpan; - - bool m_bIsMerged; - bool m_bIsEmpty; - - TTableCellStyle m_oStyles; - NSStringUtils::CStringBuilder m_oData; + virtual ETableElement GetType() const = 0; }; -class CStorageTableRow +class CTableElementCell : public ITableElementCell +{ + ETableElement m_eType; +public: + CTableElementCell(bool bIsFilling = false); + virtual ~CTableElementCell(); + + ETableElement GetType() const override; + + void IsFlatTable(); +}; + +class ITag; +class IWriter; + +struct TExternalTableData +{ + typedef std::function& arSelectors)> FuncReadStream; + typedef std::function& arSelectors)> FuncGetSubClass; + + FuncReadStream ReadStream; + FuncGetSubClass GetSubClass; + + IWriter* m_pWriter{nullptr}; + + bool IsValid() const; +}; + +typedef std::vector Row; +typedef std::vector Table; + +class IWriter; + +class CTableMatrix { public: - CStorageTableRow(); - ~CStorageTableRow(); - - void AddCell(CStorageTableCell* pCell); - void InsertCell(CStorageTableCell *pCell, int nPosition); - - UINT GetIndex() const; - UINT GetCount() const; - - CStorageTableCell* operator[](UINT unIndex); + CTableMatrix(); + virtual ~CTableMatrix(); bool Empty() const; - const TTableRowStyle& GetStyles() const; - const std::vector& GetCells() const; -private: - TTableRowStyle m_oStyles; - std::vector m_arCells; + + bool SetCell(size_t unRowIndex, size_t unColumnIndex, ITableElementCell* pCell); + + bool IsFillingCell(size_t unRowIndex, size_t unColumnIndex) const; + bool IsNotNullCell(size_t unRowIndex, size_t unColumnIndex) const; + + size_t GetRowSize() const; + size_t GetColumnSize() const; + + const Table& GetMatrixCells() const; + Table& GetMatrixCells(); + const ITableElementCell* GetCell(size_t unRowIndex, size_t unColumnIndex) const; +protected: + Table m_arCells; }; class CTableCol { public: - CTableCol(UINT unSpan); - CTableCol(const NSCSS::CNode& oTableColNode); + CTableCol(NSCSS::CNode& oTableColNode); + ~CTableCol(); UINT GetSpan() const; - TTableCellStyle* GetStyle(); - const TTableCellStyle* GetStyle() const; + const NSCSS::CCompiledStyle* GetStyle() const; private: UINT m_unSpan; - TTableCellStyle m_oStyle; + NSCSS::CCompiledStyle* m_pStyle; }; class CTableColgroup { public: - CTableColgroup(NSCSS::CNode& oTableColgroupNode); + CTableColgroup(const NSCSS::CNode& oTableColgroupNode); ~CTableColgroup(); bool Empty() const; void AddCol(CTableCol* pCol); + UINT GetTotalSpans() const; const std::vector& GetCols() const; + const NSCSS::CCompiledStyle* GetColStyle(size_t unIndex) const; private: std::vector m_arCols; UINT m_unWidth; + UINT m_unTotalSpans; }; -//Необходимые стили таблицы -struct TTableStyles -{ - NSCSS::NSProperties::CIndent m_oPadding; - NSCSS::NSProperties::CIndent m_oMargin; - NSCSS::NSProperties::CBorder m_oBorder; - NSCSS::NSProperties::CDigit m_oWidth; - - int m_nCellSpacing; - - std::wstring m_wsAlign; - - enum ETableRules - { - All, - Groups, - Cols, - None, - Rows - } m_enRules; - - TTableStyles(); - - bool Empty() const; -}; - -class CStorageTable +class CTableElement : public ITableElementCell { +protected: + CTableElement(TExternalTableData *pExternalData); public: - CStorageTable(); - ~CStorageTable(); + virtual ~CTableElement(); - CStorageTableRow* operator[](UINT unIndex); + ETableElement GetType() const override; bool Empty() const; - bool HaveCaption(); - bool HaveColgroups() const; - bool HaveHeader() const; - UINT GetRowCount() const; - const TTableStyles& GetTableStyles() const; - const TTableCellStyle* GetColStyle(UINT unColumnNumber) const; + bool HaveCaption() const; - void AddRows(std::vector& m_arRows, ERowParseMode eParseMode = ERowParseMode::Body); - void AddRow(CStorageTableRow* pRow, ERowParseMode eParseMode = ERowParseMode::Body); + virtual bool PreParse(XmlUtils::CXmlLiteReader& oReader) = 0; + virtual void Normalize() = 0; + virtual bool Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode) = 0; +protected: + CTableMatrix m_oHeader; + CTableMatrix m_oBody; + CTableMatrix m_oFoother; - NSStringUtils::CStringBuilder* GetCaptionData(); - - void SetPadding(const NSCSS::NSProperties::CIndent& oPadding); - const NSCSS::NSProperties::CIndent& GetPadding() const; - - void SetMargin(const NSCSS::NSProperties::CIndent& oMargin); - void SetBorder(const NSCSS::NSProperties::CBorder& oBorder); - - void SetWidth(const NSCSS::NSProperties::CDigit& oWidth); - void SetCellSpacing(int nCellSpacing); - void SetAlign(const std::wstring& wsValue); - void SetRules(const std::wstring& wsValue); - - void AddColgroup(CTableColgroup* pElement); - - void RecalculateMaxColumns(); - void Shorten(); - void CompleteTable(); - - //TODO:: переделать на const std::vector Get...() const; - const std::vector>& GetHeaders() const; - const std::vector& GetFoothers() const; - const std::vector& GetRows() const; - const std::vector GetColgroups() const; - - UINT GetMaxColumns() const; -private: - std::vector> m_arHeaders; - std::vector m_arFoother; - std::vector m_arRows; - - std::vector m_arMinColspan; - - NSStringUtils::CStringBuilder m_oCaption; + XmlString *m_pCaption; std::vector m_arColgroups; - TTableStyles m_oStyles; + TExternalTableData *m_pExternalData; - UINT m_unMaxColumns; + const NSCSS::CCompiledStyle* GetColStyle(size_t unColIndex) const; + + virtual bool ParseCaption(XmlUtils::CXmlLiteReader& oReader, XmlString*& pCaption) = 0; + virtual bool ParseColgroup(XmlUtils::CXmlLiteReader& oReader, std::vector& arColgroups) = 0; + template + inline bool ParseTable(XmlUtils::CXmlLiteReader& oReader, T* pTable); + template + inline bool ParseMatrix(XmlUtils::CXmlLiteReader& oReader, CTableMatrix* pMatrix, size_t& unRowIndex, size_t& unColumnIndex, size_t& unMaxColumns); }; + +template +inline bool CTableElement::ParseTable(XmlUtils::CXmlLiteReader& oReader, T* pTable) +{ + const int nDeath{oReader.GetDepth()}; + std::wstring wsName; + + while(oReader.ReadNextSiblingNode(nDeath)) + { + wsName = oReader.GetName(); + + size_t unRowIndex{0}, unColumnIndex{0}, unMaxColumnIndex{0}; + + if(L"thead" == wsName) + ParseMatrix(oReader, &pTable->m_oHeader, unRowIndex, unColumnIndex, unMaxColumnIndex); + if(L"tbody" == wsName) + ParseMatrix(oReader, &pTable->m_oBody, unRowIndex, unColumnIndex, unMaxColumnIndex); + else if(L"tfoot" == wsName) + ParseMatrix(oReader, &pTable->m_oFoother, unRowIndex, unColumnIndex, unMaxColumnIndex); + else if (L"caption" == wsName) + ParseCaption(oReader, pTable->m_pCaption); + else if (L"colgroup" == wsName) + ParseColgroup(oReader, pTable->m_arColgroups); + } + + return true; +} + +template +inline bool CTableElement::ParseMatrix(XmlUtils::CXmlLiteReader& oReader, CTableMatrix* pMatrix, size_t& unRowIndex, size_t& unColumnIndex, size_t& unMaxColumns) +{ + const std::wstring wsElementName{oReader.GetName()}; + + if (L"table" == wsElementName) + { + T* pNewTable{new T(m_pExternalData)}; + + if (nullptr == pNewTable) + return false; + + pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, pNewTable); + + ParseTable(oReader, pNewTable); + } + else if (L"tr" == wsElementName) + { + const int nDepth{oReader.GetDepth()}; + ++unRowIndex; + unColumnIndex = 0; + + while (oReader.ReadNextSiblingNode(nDepth)) + { + if (L"td" != oReader.GetName() && L"th" != oReader.GetName()) + continue; + + ParseMatrix(oReader, pMatrix, unRowIndex, unColumnIndex, unMaxColumns); + } + } + else if (L"td" == wsElementName || L"th" == wsElementName) + { + ++unColumnIndex; + + while (pMatrix->IsFillingCell(unRowIndex - 1, unColumnIndex - 1)) + ++unColumnIndex; + + unMaxColumns = (std::max)(unMaxColumns, unColumnIndex); + + if (!pMatrix->SetCell(unRowIndex - 1, unColumnIndex - 1, new CTableElementCell())) + return false; + + 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)); + } + else if (L"colspan" == oReader.GetName()) + unColumnIndex += NSStringFinder::ToInt(oReader.GetText(), 1) - 1; + }while (oReader.MoveToNextAttribute()); + oReader.MoveToElement(); + } + + const int nDepth{oReader.GetDepth()}; + while (oReader.ReadNextSiblingNode(nDepth)) + ParseMatrix(oReader, pMatrix, unRowIndex, unColumnIndex, unMaxColumns); + } + else if (oReader.IsEmptyNode()) + return false; + else + { + const int nDepth{oReader.GetDepth()}; + while (oReader.ReadNextSiblingNode(nDepth)) + ParseMatrix(oReader, pMatrix, unRowIndex, unColumnIndex, unMaxColumns); + } + + return true; +} + +bool MoveToNextTableCell(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, std::stack& arDepths, TExternalTableData::FuncGetSubClass& GetSubClass); } #endif // TABLE_H diff --git a/HtmlFile2/Tags/HTMLTags.h b/HtmlFile2/Tags/HTMLTags.h index a7a1a48e2f..7d4b80c469 100644 --- a/HtmlFile2/Tags/HTMLTags.h +++ b/HtmlFile2/Tags/HTMLTags.h @@ -6,6 +6,13 @@ namespace HTML { +// TODO:: думаю лучше перейти на интерфейсы к каждой ноде +// ISVGTag -> CMarkdownSVGTag +// -> COOXMLSVGTag +// Либо +// template CSVGTag -> template<> class CSVGTag +// -> template<> class CSVGTag + class ITag { public: @@ -80,9 +87,6 @@ CREATE_TAG(CHorizontalRule); CREATE_TAG(CList); CREATE_TAG(CListElement); CREATE_TAG(CCaption); -CREATE_TAG(CTable); -CREATE_TAG(CTableRow); -CREATE_TAG(CTableCell); CREATE_TAG(CCode); CREATE_TAG(CHTML); diff --git a/HtmlFile2/Tags/MDTags.cpp b/HtmlFile2/Tags/MDTags.cpp index 7352656702..61e1777566 100644 --- a/HtmlFile2/Tags/MDTags.cpp +++ b/HtmlFile2/Tags/MDTags.cpp @@ -1,9 +1,13 @@ #include "MDTags.h" +#include "../DesktopEditor/xml/include/xmlutils.h" + #include "../src/StringFinder.h" #include "../Table.h" +#include "../Common/3dParty/html/css/src/CCompiledStyle.h" #include +#include namespace HTML { @@ -301,126 +305,6 @@ void CBlockquote::Close(const std::vector& arSelectors) m_pWriter->WriteBreakLine(false); } -CTable::CTable(CMDWriter* pWriter) - : CTag(pWriter) -{} - -bool CTable::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - if (!ValidWriter() /*|| m_pWriter->InTable()*/) //В MD не поддерживаются вложенные таблицы (пока разруливаем в парсере) - return false; - - m_pWriter->WriteBreakLine(); - m_pWriter->EnteredTable(); - - if (m_pWriter->InCode()) - { - if (!m_pWriter->InPreformatted()) - m_pWriter->WriteCloseSpecialString(L"`"); - - m_pWriter->OutCode(); - } - - if (m_pWriter->InPreformatted()) - { - m_pWriter->WriteBreakLine(); - m_pWriter->WriteCloseSpecialString(L"```"); - m_pWriter->WriteBreakLine(false); - m_pWriter->OutPreformatted(); - } - - return true; -} - -void CTable::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - m_pWriter->OutTable(); - m_pWriter->WriteBreakLine(); -} - -CTableRow::CTableRow(CMDWriter* pWriter) - : CTag(pWriter), m_unLastRowType(static_cast(ERowParseMode::Foother)) -{} - -bool CTableRow::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - using DataForRow = boost::tuple; - - if (!ValidWriter() || oExtraData.empty() || typeid(DataForRow) != oExtraData.type()) - return false; - - m_pWriter->EnteredTable(); - - const DataForRow& oDataForRow(boost::any_cast(oExtraData)); - const CStorageTable& oStorageTable{boost::get<1>(oDataForRow)}; - - if (nullptr == boost::get<0>(oDataForRow) && ERowParseMode::Header == boost::get<2>(oDataForRow)) - { - for (UINT unIndex = 0; unIndex < oStorageTable.GetMaxColumns(); ++unIndex) - m_pWriter->WriteOpenSpecialString(L"| "); - - m_pWriter->WriteOpenSpecialString(L"|"); - m_pWriter->WriteBreakLine(); - m_unLastRowType = static_cast(ERowParseMode::Header); - return true; - } - - if (m_unLastRowType == static_cast(ERowParseMode::Header)) - { - for (UINT unIndex = 0; unIndex < oStorageTable.GetMaxColumns(); ++unIndex) - m_pWriter->WriteString(L"|-"); - - m_pWriter->WriteOpenSpecialString(L"|"); - m_pWriter->WriteBreakLine(false); - } - - m_pWriter->WriteOpenSpecialString(L"| "); - m_unLastRowType = static_cast(boost::get<2>(oDataForRow)); - - return true; -} - -void CTableRow::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - m_pWriter->WriteBreakLine(false); -} - -CTableCell::CTableCell(CMDWriter* pWriter) - : CTag(pWriter), m_unNeedEmptyCells(0) -{} - -bool CTableCell::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - using DataForCell = boost::tuple; - - if (!ValidWriter() || oExtraData.empty() || typeid(DataForCell) != oExtraData.type()) - return false; - - const DataForCell& oDataForCell{boost::any_cast(oExtraData)}; - - m_unNeedEmptyCells = boost::get<0>(oDataForCell).GetColspan() - 1; - - return true; -} - -void CTableCell::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - for (UINT unIndex = 0; unIndex < m_unNeedEmptyCells; ++unIndex) - m_pWriter->WriteOpenSpecialString(L" |"); - - m_unNeedEmptyCells = 0; - m_pWriter->WriteOpenSpecialString(L" | "); -} - CList::CList(CMDWriter* pWriter) : CTag(pWriter) {} @@ -515,4 +399,391 @@ void CCode::Close(const std::vector& arSelectors) m_pWriter->OutCode(); } + +bool IsTable(const ITableElementCell* pCell) +{ + return nullptr != pCell && ETableElement::Table == pCell->GetType(); +} + +CMarkdownTable::CMarkdownTable(TExternalTableData* pExternalData) + : CTableElement(pExternalData) +{} + +CMarkdownTable::~CMarkdownTable() +{} + +bool CMarkdownTable::PreParse(XmlUtils::CXmlLiteReader& oReader) +{ + return ParseTable(oReader, this); +} + +void CMarkdownTable::Normalize() +{ + m_oHeader .GetMatrixCells() = Flatten(std::move(m_oHeader .GetMatrixCells())); + m_oBody .GetMatrixCells() = Flatten(std::move(m_oBody .GetMatrixCells())); + m_oFoother.GetMatrixCells() = Flatten(std::move(m_oFoother.GetMatrixCells())); +} + +typedef std::queue, NSStringUtils::CStringBuilder*>> NestedCells; + +inline void WriteRowStart(CMDWriter& oWriter) +{ + oWriter.WriteOpenSpecialString(L"| "); +} + +inline void WriteRowEnd(CMDWriter& oWriter) +{ + oWriter.WriteOpenSpecialString(L" |"); + oWriter.WriteBreakLine(false); +} + +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) +{ + const int nDepth{oReader.GetDepth()}; + + while(oReader.ReadNextSiblingNode(nDepth)) + { + const std::wstring wsName = oReader.GetName(); + oExternalTableData.GetSubClass(oReader, arSelectors); + + if (L"td" == wsName || L"th" == wsName) + { + if ((oPosition.m_unRowIndex != oPosition.m_unStartRowIndex || oPosition.m_unColumnIndex != oPosition.m_unStartColumnIndex) && + ETableElement::FlatTable == oCells[oPosition.m_unRowIndex][oPosition.m_unColumnIndex]->GetType()) + { + ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData); + continue; + } + + NSStringUtils::CStringBuilder *pCellData = new NSStringUtils::CStringBuilder(20); + + if (nullptr != pCellData) + { + 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)); + + //READ and add to nested cells + ++oPosition.m_unColumnIndex; + } + else if (L"tr" == wsName) + { + oPosition.m_unColumnIndex = oPosition.m_unStartColumnIndex; + + ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData); + + ++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 arNestedSelectors{arSelectors.back()}; + + ReadNestedCells(oReader, arNestedSelectors, oNestedPosition, arNestedCells, oCells, pWriter, oExternalTableData); + + oPosition.m_unRowIndex = oNestedPosition.m_unRowIndex; + } + else + ReadNestedCells(oReader, arSelectors, oPosition, arNestedCells, oCells, pWriter, oExternalTableData); + + arSelectors.pop_back(); + } +} + +bool CMarkdownTable::Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode) +{ + if (nullptr == m_pExternalData || Empty()) + return false; + + CMDWriter *pWriter{dynamic_cast(m_pExternalData->m_pWriter)}; + + if (nullptr == pWriter) + return false; + + std::vector arTableSelectors{oTableNode}; + + pWriter->WriteBreakLine(); + WriteToStringBuilder(*m_pCaption, *pWriter->GetCurrentDocument()); + + //Open table + pWriter->WriteBreakLine(); + pWriter->EnteredTable(); + + if (pWriter->InCode()) + { + if (!pWriter->InPreformatted()) + pWriter->WriteCloseSpecialString(L"`"); + + pWriter->OutCode(); + } + + if (pWriter->InPreformatted()) + { + pWriter->WriteBreakLine(); + pWriter->WriteCloseSpecialString(L"```"); + pWriter->WriteBreakLine(false); + pWriter->OutPreformatted(); + } + //----- + + //Convert header + if (!ConvertMatrix(oReader, arTableSelectors, m_oHeader.GetMatrixCells(), pWriter)) + { + WriteRowStart(*pWriter); + + for (size_t unColumnIndex = 0; unColumnIndex < m_oBody.GetColumnSize() - 1; ++unColumnIndex) + WriteCellSeparator(*pWriter); + + WriteRowEnd(*pWriter); + } + + WriteRowStart(*pWriter); + for (size_t unColumnIndex = 0; unColumnIndex < m_oBody.GetColumnSize() - 1; ++unColumnIndex) + pWriter->WriteString(L"-|-", true); + WriteRowEnd(*pWriter); + //---- + + //Convert body + ConvertMatrix(oReader, arTableSelectors, m_oBody.GetMatrixCells(), pWriter); + //Convert foother + ConvertMatrix(oReader, arTableSelectors, m_oFoother.GetMatrixCells(), pWriter); + + //Close table + pWriter->OutTable(); + pWriter->WriteBreakLine(); + + return true; +} + +bool CMarkdownTable::ParseCaption(XmlUtils::CXmlLiteReader& oReader, XmlString*& pCaption) +{ + if (nullptr == m_pExternalData || nullptr == m_pExternalData->m_pWriter) + return false; + + if (nullptr == pCaption) + pCaption = new XmlString(50); + + std::vector arSelectors; + m_pExternalData->GetSubClass(oReader, arSelectors); + + arSelectors.back().m_pCompiledStyle->m_oText.SetAlign(L"center", 0, true); + + CMDWriter& oWriter{*(CMDWriter*)m_pExternalData->m_pWriter}; + + oWriter.SetDataOutput(m_pCaption); + m_pExternalData->ReadStream(oReader, arSelectors); + oWriter.WriteBreakLine(); + oWriter.RevertDataOutput(); + + return true; +} + +bool CMarkdownTable::ParseColgroup(XmlUtils::CXmlLiteReader& oReader, std::vector& arColgroups) +{ + return true; +} + +bool CMarkdownTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, const Table& oMatrix, CMDWriter* pWriter) +{ + if (oMatrix.empty() || nullptr == pWriter) + return false; + + std::stack arDepths({0}); + + NestedCells arNestedCells; + ITableElementCell* pTableCell{nullptr}; + + for (size_t unRowIndex = 0; unRowIndex < oMatrix.size(); ++unRowIndex) + { + WriteRowStart(*pWriter); + + for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size(); ++unColumnIndex) + { + pTableCell = oMatrix[unRowIndex][unColumnIndex]; + + if (nullptr != pTableCell && ETableElement::FillingCell != pTableCell->GetType()) + { + if (!arNestedCells.empty() && arNestedCells.front().first == std::make_pair(unRowIndex, unColumnIndex)) + { + if (nullptr != arNestedCells.front().second) + { + m_pExternalData->m_pWriter->GetCurrentDocument()->Write(*arNestedCells.front().second); + delete arNestedCells.front().second; + } + arNestedCells.pop(); + } + else + { + MoveToNextTableCell(oReader, arSelectors, arDepths, m_pExternalData->GetSubClass); + + if (ETableElement::FlatTable == pTableCell->GetType()) + { + TCurentTablePosition oPosition{unRowIndex, unColumnIndex, unRowIndex, unColumnIndex}; + std::vector arNestedSelectors; + + ReadNestedCells(oReader, arNestedSelectors, oPosition, arNestedCells, oMatrix, pWriter, *m_pExternalData); + --unColumnIndex; + } + else + { + m_pExternalData->ReadStream(oReader, arSelectors); + if (oMatrix[unRowIndex].size() - 1 != unColumnIndex) + WriteCellSeparator(*pWriter); + } + arSelectors.pop_back(); + } + } + else + { + if (oMatrix[unRowIndex].size() - 1 != unColumnIndex) + WriteCellSeparator(*pWriter); + } + } + + WriteRowEnd(*pWriter); + } + + return true; +} + +Table CMarkdownTable::Flatten(Table&& srcTable) +{ + if (srcTable.empty()) + return {}; + + const size_t unRows{srcTable.size()}; + const size_t unColumns{srcTable[0].size()}; + + std::vector> oInfos{unRows, std::vector{unColumns}}; + + for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex) + for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex) + oInfos[unRowIndex][unColumnIndex] = ComputeInfo(srcTable[unRowIndex][unColumnIndex]); + + std::vector arRowHeights(unRows, 1); + std::vector arColumnWidths(unColumns, 1); + + for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex) + { + 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); + } + } + + std::vector arRowStart (unRows + 1, 0); + std::vector arColumnStart(unColumns + 1, 0); + + for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex) + arRowStart[unRowIndex + 1] = arRowStart[unRowIndex] + arRowHeights[unRowIndex]; + + for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex) + arColumnStart[unColumnIndex + 1] = arColumnStart[unColumnIndex] + arColumnWidths[unColumnIndex]; + + const size_t unTotalRows {arRowStart[unRows]}; + const size_t unTotalColumns{arColumnStart[unColumns]}; + + Table oResult{unTotalRows, Row{unTotalColumns, nullptr}}; + + size_t unBaseRow, unBaseColumn; + + for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex) + { + for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex) + { + ITableElementCell*& pCell{srcTable[unRowIndex][unColumnIndex]}; + + if (nullptr == pCell) + continue; + + const TElementInfo oInfo{oInfos[unRowIndex][unColumnIndex]}; + + unBaseRow = arRowStart[unRowIndex]; + unBaseColumn = arColumnStart[unColumnIndex]; + + if (!IsTable(pCell)) + { + oResult[unBaseRow][unBaseColumn] = pCell; + pCell = nullptr; + continue; + } + + CMarkdownTable *pTable{dynamic_cast(pCell)}; + pCell = nullptr; + + Table& oChild{pTable->m_oBody.GetMatrixCells()}; + Table oFlatChild{Flatten(std::move(oChild))}; + + 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]; + + CTableElementCell* pFlatCell{dynamic_cast(oResult[unBaseRow][unBaseColumn])}; + + if(nullptr != pFlatCell) + pFlatCell->IsFlatTable(); + + delete pTable; + } + } + + return oResult; +} + +TElementInfo CMarkdownTable::ComputeInfo(const ITableElementCell* pCell) +{ + if (!IsTable(pCell)) + return {1, 1}; + + const CMarkdownTable *pMarkdownTable{dynamic_cast(pCell)}; + + if (nullptr == pMarkdownTable) + return {1, 1}; + + const CTableMatrix *pTable{&pMarkdownTable->m_oBody}; + + const size_t unRows{pTable->GetRowSize()}; + const size_t unColumns{pTable->GetColumnSize()}; + + std::vector arRowHeights(unRows, 1); + std::vector arColumnWidths(unColumns, 1); + + const Table& arCells{pTable->GetMatrixCells()}; + + for (size_t unRowIndex = 0; unRowIndex < unRows; ++unRowIndex) + { + for (size_t unColumnIndex = 0; unColumnIndex < unColumns; ++unColumnIndex) + { + TElementInfo oInfo{ComputeInfo(arCells[unRowIndex][unColumnIndex])}; + + arRowHeights[unRowIndex] = (std::max)(arRowHeights[unRowIndex], oInfo.unRows); + arColumnWidths[unColumnIndex] = (std::max)(arColumnWidths[unColumnIndex], oInfo.unColumns); + } + } + + size_t unTotalRows{0}, unTotalColumns{0}; + + for (size_t unHeight : arRowHeights) + unTotalRows += unHeight; + + for (size_t unWidth : arColumnWidths) + unTotalColumns += unWidth; + + return {unTotalRows, unTotalColumns}; +} } diff --git a/HtmlFile2/Tags/MDTags.h b/HtmlFile2/Tags/MDTags.h index 5849c47f7f..65452f867a 100644 --- a/HtmlFile2/Tags/MDTags.h +++ b/HtmlFile2/Tags/MDTags.h @@ -3,6 +3,7 @@ #include "HTMLTags.h" #include "../Writers/MDWriter.h" +#include "../Table.h" namespace HTML { @@ -105,35 +106,6 @@ public: virtual void Close(const std::vector& arSelectors) override; }; -template<> -class CTable : public CTag -{ -public: - CTable(CMDWriter* pWriter); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - -template<> -class CTableRow : public CTag -{ - UINT m_unLastRowType; -public: - CTableRow(CMDWriter* pWriter); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - -template<> -class CTableCell : public CTag -{ - UINT m_unNeedEmptyCells; -public: - CTableCell(CMDWriter* pWriter); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - template<> class CList : public CTag { @@ -160,6 +132,31 @@ public: virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector& arSelectors) override; }; + +struct TElementInfo +{ + size_t unRows; + size_t unColumns; +}; + +class CMarkdownTable : public CTableElement +{ +public: + CMarkdownTable(TExternalTableData* pExternalData); + virtual ~CMarkdownTable(); + + bool PreParse(XmlUtils::CXmlLiteReader& oReader) override; + void Normalize() override; + bool Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode) override; +private: + bool ParseCaption(XmlUtils::CXmlLiteReader& oReader, XmlString*& pCaption) override; + bool ParseColgroup(XmlUtils::CXmlLiteReader& oReader, std::vector& arColgroups) override; + + bool ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, std::vector& arSelectors, const Table& oMatrix, CMDWriter* pWriter); + + static Table Flatten(Table&& srcTable); + static TElementInfo ComputeInfo(const ITableElementCell* pCell); +}; } #endif // MDTAGS_H diff --git a/HtmlFile2/Tags/OOXMLTags.cpp b/HtmlFile2/Tags/OOXMLTags.cpp index c29c32b9bb..1bbdb32e7c 100644 --- a/HtmlFile2/Tags/OOXMLTags.cpp +++ b/HtmlFile2/Tags/OOXMLTags.cpp @@ -675,10 +675,10 @@ bool CHorizontalRule::Open(const std::vector& arSele if (arSelectors.back().GetAttributeValue(L"width", wsValue)) oWidth.SetValue(wsValue); - XmlString& oCurrentDocument{*m_pWriter->GetCurrentDocument()}; + XmlString& oXmlString{*m_pWriter->GetCurrentDocument()}; m_pWriter->OpenP(); - oCurrentDocument.WriteString(L""); + oXmlString.WriteString(L""); m_pWriter->OpenR(); const NSCSS::NSProperties::CPage *pPageData{m_pWriter->GetPageData()}; @@ -699,18 +699,18 @@ bool CHorizontalRule::Open(const std::vector& arSele if (!oSize.Empty()) wsHeight = std::to_wstring(static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oSize.ToDouble(), NSCSS::Inch, 96) * 914400.)); - oCurrentDocument.WriteString(L""); - oCurrentDocument.WriteString(L""); - oCurrentDocument.WriteString(L""); - oCurrentDocument.WriteString(L""); - oCurrentDocument.WriteString(L"" + oXmlString.WriteString(L""); + oXmlString.WriteString(L""); + oXmlString.WriteString(L""); + oXmlString.WriteString(L""); + oXmlString.WriteString(L"" "" "" "" "" "" ""); - oCurrentDocument.WriteString(L"" + oXmlString.WriteString(L"" "" "" "" @@ -721,9 +721,9 @@ bool CHorizontalRule::Open(const std::vector& arSele ""); if (bShade) - oCurrentDocument.WriteString(L""); + oXmlString.WriteString(L""); - oCurrentDocument.WriteString(L""); + oXmlString.WriteString(L""); m_pWriter->CloseP(); @@ -1079,7 +1079,7 @@ void CCaption::Close(const std::vector& arSelectors) m_pWriter->CloseP(); } -std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NSCSS::NSProperties::CIndent* pPadding = NULL, bool bAddIntermediateLines = false, TTableStyles::ETableRules enTableRule = TTableStyles::ETableRules::None) +std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NSCSS::NSProperties::CIndent* pPadding = NULL, bool bAddIntermediateLines = false, ETableRules enTableRule = ETableRules::None) { std::wstring wsTable; @@ -1108,7 +1108,7 @@ std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NS if (!bAddIntermediateLines) return wsTable; - if (TTableStyles::ETableRules::Rows == enTableRule || TTableStyles::ETableRules::All == enTableRule) + if (ETableRules::Rows == enTableRule || ETableRules::All == enTableRule) { NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetBottomBorder()); oNewSide.SetWidth(L"1pt", 0, true); @@ -1116,7 +1116,7 @@ std::wstring CreateBorders(const NSCSS::NSProperties::CBorder& oBorder, const NS wsTable += L""; } - if (TTableStyles::ETableRules::Cols == enTableRule || TTableStyles::ETableRules::All == enTableRule) + if (ETableRules::Cols == enTableRule || ETableRules::All == enTableRule) { NSCSS::NSProperties::CBorderSide oNewSide(oBorder.GetRightBorder()); oNewSide.SetWidth(L"1pt", 0, true); @@ -1145,165 +1145,6 @@ std::wstring CreateDefaultBorder(std::wstring wsSideName) return L""; } -CTable::CTable(COOXMLWriter* pWriter) - : CTag(pWriter) -{} - -bool CTable::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - if (!ValidWriter() || oExtraData.empty() || typeid(CStorageTable*) != oExtraData.type()) - return false; - - CStorageTable* pStorageTable{boost::any_cast(oExtraData)}; - - if (pStorageTable->Empty()) - return false; - - XmlString& oCurrentDocument{*m_pWriter->GetCurrentDocument()}; - - oCurrentDocument.WriteNodeBegin(L"w:tbl"); - oCurrentDocument.WriteNodeBegin(L"w:tblPr"); - - const TTableStyles& oTableStyles{pStorageTable->GetTableStyles()}; - - if (!oTableStyles.m_oWidth.Empty() && !oTableStyles.m_oWidth.Zero()) - { - if (NSCSS::UnitMeasure::Percent == oTableStyles.m_oWidth.GetUnitMeasure()) - oCurrentDocument += L""; - else - oCurrentDocument += L""; - } - else - oCurrentDocument += L""; - - if (!oTableStyles.m_oMargin.GetLeft().Empty() && !oTableStyles.m_oMargin.GetLeft().Zero()) - { - if (NSCSS::UnitMeasure::Percent == oTableStyles.m_oMargin.GetLeft().GetUnitMeasure()) - oCurrentDocument += L""; - else - oCurrentDocument += L""; - } - - if (!oTableStyles.m_wsAlign.empty()) - oCurrentDocument += L""; - - if (0 < oTableStyles.m_nCellSpacing && oTableStyles.m_oBorder.GetCollapse() != NSCSS::NSProperties::BorderCollapse::Collapse) - oCurrentDocument += L""; - - if (!oTableStyles.m_oBorder.Empty() && !oTableStyles.m_oBorder.Zero()) - oCurrentDocument += L"" + CreateBorders(oTableStyles.m_oBorder, NULL, true, (TTableStyles::ETableRules::Groups == oTableStyles.m_enRules && !pStorageTable->GetColgroups().empty()) ? TTableStyles::ETableRules::Cols : oTableStyles.m_enRules) + L""; - - if (!oTableStyles.m_oPadding.Empty() && !oTableStyles.m_oPadding.Zero()) - { - const int nTopPadding = std::max(0, oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nLeftPadding = std::max(0, oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); - const int nBottomPadding = std::max(0, oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nRightPadding = std::max(0, oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); - - oCurrentDocument.WriteNodeBegin(L"w:tblCellMar"); - - if (0 != nTopPadding) - oCurrentDocument += L""; - - if (0 != nLeftPadding) - oCurrentDocument += L""; - - if (0 != nBottomPadding) - oCurrentDocument += L""; - - if (0 != nRightPadding) - oCurrentDocument += L""; - - oCurrentDocument.WriteNodeEnd(L"w:tblCellMar"); - } - else - oCurrentDocument += L""; - - oCurrentDocument += L""; - oCurrentDocument.WriteNodeEnd(L"w:tblPr"); - - if (pStorageTable->HaveCaption()) - { - oCurrentDocument.WriteNodeBegin(L"w:tr"); - oCurrentDocument.WriteNodeBegin(L"w:tc"); - oCurrentDocument.WriteNodeBegin(L"w:tcPr"); - oCurrentDocument += L""; - oCurrentDocument += L"GetMaxColumns()) + L"\"/>"; - oCurrentDocument += L""; - oCurrentDocument += L""; - oCurrentDocument += L""; - oCurrentDocument.WriteNodeEnd(L"w:tcPr"); - WriteToStringBuilder(*(pStorageTable->GetCaptionData()), oCurrentDocument); - oCurrentDocument.WriteNodeEnd(L"w:tc"); - oCurrentDocument.WriteNodeEnd(L"w:tr"); - } - - return true; -} - -void CTable::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - m_pWriter->GetCurrentDocument()->WriteNodeEnd(L"w:tbl"); - m_pWriter->WriteEmptyParagraph(true); -} - -CTableRow::CTableRow(COOXMLWriter* pWriter) - : CTag(pWriter) -{} - -bool CTableRow::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - using DataForRow = boost::tuple; - - if (!ValidWriter() || oExtraData.empty() || typeid(DataForRow) != oExtraData.type()) - return false; - - const DataForRow& oDataForRow(boost::any_cast(oExtraData)); - const TTableRowStyle* pTableRowStyles{boost::get<0>(oDataForRow)}; - - if (nullptr == pTableRowStyles) - return false; - - XmlString& oCurrentDocument{*m_pWriter->GetCurrentDocument()}; - - oCurrentDocument.WriteNodeBegin(L"w:tr"); - - const TTableStyles& oTableStyles{boost::get<1>(oDataForRow).GetTableStyles()}; - - if (!pTableRowStyles->Empty() || 0 < oTableStyles.m_nCellSpacing) - { - oCurrentDocument.WriteNodeBegin(L"w:trPr"); - - if (pTableRowStyles->m_bIsHeader) - oCurrentDocument += L""; - - if (0 < pTableRowStyles->m_unMaxHeight) - oCurrentDocument += L"m_unMaxHeight) + L"\"/>"; - - if (0 < oTableStyles.m_nCellSpacing) - oCurrentDocument += L""; - - oCurrentDocument.WriteNodeEnd(L"w:trPr"); - } - - return true; -} - -void HTML::CTableRow::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - m_pWriter->GetCurrentDocument()->WriteNodeEnd(L"w:tr"); -} - -CTableCell::CTableCell(COOXMLWriter* pInterpretator) - : CTag(pInterpretator) -{} - std::wstring CalculateSidesToClean(UINT unColumnNumber, const std::vector& arColgroups, UINT unMaxColumns) { if (arColgroups.empty()) @@ -1330,132 +1171,6 @@ std::wstring CalculateSidesToClean(UINT unColumnNumber, const std::vector::Open(const std::vector& arSelectors, const boost::any& oExtraData) -{ - using DataForCell = boost::tuple; - - if (!ValidWriter() || oExtraData.empty() || typeid(DataForCell) != oExtraData.type()) - return false; - - const DataForCell& oDataForCell{boost::any_cast(oExtraData)}; - const CStorageTableCell& oStorageTableCell{boost::get<0>(oDataForCell)}; - - XmlString& oCurrentDocument{*m_pWriter->GetCurrentDocument()}; - - oCurrentDocument.WriteNodeBegin(L"w:tc"); - oCurrentDocument.WriteNodeBegin(L"w:tcPr"); - - const ERowParseMode eParseMode{boost::get<3>(oDataForCell)}; - - if (ERowParseMode::Header == eParseMode) - oCurrentDocument += L""; - - TTableCellStyle oCellStyle(*oStorageTableCell.GetStyles()); - - const CStorageTable& oStorageTable{boost::get<1>(oDataForCell)}; - const UINT unColumnNumber{boost::get<2>(oDataForCell)}; - const TTableCellStyle* pColStyle{oStorageTable.GetColStyle(unColumnNumber)}; - - if (NULL != pColStyle) - oCellStyle += pColStyle; - - if (!oCellStyle.m_oWidth.Empty()) - { - if (NSCSS::UnitMeasure::Percent == oCellStyle.m_oWidth.GetUnitMeasure()) - oCurrentDocument += L""; - else - { - if (!oCellStyle.m_oWidth.Zero()) - { - int nWidth; - if (NSCSS::UnitMeasure::None != oCellStyle.m_oWidth.GetUnitMeasure()) - nWidth = oCellStyle.m_oWidth.ToInt(NSCSS::UnitMeasure::Twips); - else - nWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(oCellStyle.m_oWidth.ToDouble(), NSCSS::UnitMeasure::Twips, 96) + 0.5); - - oCurrentDocument += L""; - } - else - oCurrentDocument += L""; - } - } - else - oCurrentDocument += L""; - - if (1 != oStorageTableCell.GetColspan()) - oCurrentDocument += L""; - - if (oStorageTableCell.Merged()) - oCurrentDocument += L""; - else if (1 < oStorageTableCell.GetRowspan()) - oCurrentDocument += L""; - - const TTableStyles oTableStyles{oStorageTable.GetTableStyles()}; - - if (!oCellStyle.m_oBorder.Empty() && !oCellStyle.m_oBorder.Zero() /*&& oCellStyle.m_oBorder != oTableStyles.m_oBorder*/) - oCurrentDocument += L"" + CreateBorders(oCellStyle.m_oBorder, &oCellStyle.m_oPadding) + L""; - else if (TTableStyles::ETableRules::Groups == oTableStyles.m_enRules) - { - std::wstring wsBorders; - - if (oStorageTable.HaveColgroups()) - wsBorders += CalculateSidesToClean(unColumnNumber, oStorageTable.GetColgroups(), oStorageTable.GetMaxColumns()); - - const ERowPosition eRowPosition{boost::get<4>(oDataForCell)}; - - if (ERowParseMode::Header == eParseMode && ERowPosition::Last == eRowPosition) - wsBorders += CreateDefaultBorder(L"bottom"); - else if (ERowParseMode::Foother == eParseMode && ERowPosition::First == eRowPosition) - wsBorders += CreateDefaultBorder(L"top"); - - if (!wsBorders.empty()) - oCurrentDocument += L"" + wsBorders + L""; - } - - if (!oCellStyle.m_oBackground.Empty()) - { - const std::wstring wsShdFill{(NSCSS::NSProperties::ColorNone == oCellStyle.m_oBackground.GetType()) ? L"auto" : oCellStyle.m_oBackground.ToWString()}; - oCurrentDocument += L""; - } - - if (!oCellStyle.m_wsVAlign.empty()) - oCurrentDocument += L""; - else - oCurrentDocument += L""; - - if (!oCellStyle.m_oPadding.Empty() && oTableStyles.m_oPadding != oCellStyle.m_oPadding) - { - const int nTopPadding = std::max(oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), - oCellStyle .m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nLeftPadding = std::max(oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), - oCellStyle .m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); - const int nBottomPadding = std::max(oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), - oCellStyle .m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); - const int nRightPadding = std::max(oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), - oCellStyle .m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); - - oCurrentDocument += L"" - "" - "" - "" - "" - ""; - } - - oCurrentDocument += L""; - oCurrentDocument.WriteNodeEnd(L"w:tcPr"); - - return true; -} - -void HTML::CTableCell::Close(const std::vector& arSelectors) -{ - if (!ValidWriter()) - return; - - m_pWriter->GetCurrentDocument()->WriteNodeEnd(L"w:tc"); -} - CHTML::CHTML(COOXMLWriter* pInterpretator) : CTag(pInterpretator) {} @@ -1489,4 +1204,577 @@ bool CHTML::Open(const std::vector& arSelectors, con void CHTML::Close(const std::vector& arSelectors) {} +COOXMLTable::COOXMLTable(TExternalTableData* pExternalData) + : CTableElement(pExternalData) +{} + +COOXMLTable::~COOXMLTable() +{} + +bool COOXMLTable::PreParse(XmlUtils::CXmlLiteReader& oReader) +{ + return ParseTable(oReader, this); +} + +bool COOXMLTable::HaveColgroups() const +{ + return !m_arColgroups.empty(); +} + +size_t COOXMLTable::GetColumnsCount() const +{ + return m_oBody.GetColumnSize(); +} + +void COOXMLTable::OpenTable(XmlString& oXmlString, const NSCSS::CNode& oTableNode, TTableStyles& oTableStyles, const COOXMLTable& oTable) +{ + oXmlString.WriteNodeBegin(L"w:tbl"); + oXmlString.WriteNodeBegin(L"w:tblPr"); + + const NSCSS::CCompiledStyle *pStyle{oTableNode.m_pCompiledStyle}; + + if (nullptr != pStyle) + { + const NSCSS::NSProperties::CDigit& oWidth{pStyle->m_oDisplay.GetWidth()}; + + if (!oWidth.Empty() && !oWidth.Zero()) + { + if (NSCSS::UnitMeasure::Percent == oWidth.GetUnitMeasure()) + oXmlString += L""; + else + oXmlString += L""; + } + else + oXmlString += L""; + + const NSCSS::NSProperties::CIndent& oMargin{pStyle->m_oMargin}; + + if (!oMargin.GetLeft().Empty() && !oMargin.GetLeft().Zero()) + { + if (NSCSS::UnitMeasure::Percent == oMargin.GetLeft().GetUnitMeasure()) + oXmlString += L""; + else + oXmlString += L""; + } + + if (!pStyle->m_oDisplay.GetHAlign().Empty()) + oXmlString += L"m_oDisplay.GetHAlign().ToWString() + L"\"/>"; + + std::wstring wsValue; + + if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Collapse) + oTableStyles.m_unCellSpacing = 0; + else if (oTableNode.GetAttributeValue(L"cellspacing", wsValue)) + oTableStyles.m_unCellSpacing = NSStringFinder::ToInt(wsValue); + else if (pStyle->m_oBorder.GetCollapse() == NSCSS::NSProperties::BorderCollapse::Separate) + oTableStyles.m_unCellSpacing = 15; + + if (0 < oTableStyles.m_unCellSpacing && pStyle->m_oBorder.GetCollapse() != NSCSS::NSProperties::BorderCollapse::Collapse) + oXmlString += L""; + + if (oTableNode.GetAttributeValue(L"border", wsValue)) + { + const int nWidth = NSStringFinder::ToInt(wsValue); + + if (0 < nWidth) + { + oTableStyles.m_enRules = ETableRules::All; + + if (oTableStyles.m_oBorder.Empty()) + { + oTableStyles.m_oBorder.SetStyle(L"outset", 0, true); + oTableStyles.m_oBorder.SetWidth(nWidth, NSCSS::UnitMeasure::Point, 0, true); + oTableStyles.m_oBorder.SetColor(L"auto", 0, true); + } + } + else if (pStyle->m_oBorder.Empty()) + { + oTableStyles.m_oBorder.SetNone(0, true); + oTableStyles.m_enRules = ETableRules::All; + } + } + else + oTableStyles.m_oBorder = pStyle->m_oBorder; + + if (oTableNode.GetAttributeValue(L"rules", wsValue)) + { + if (NSStringFinder::Equals(wsValue, L"all")) + oTableStyles.m_enRules = ETableRules::All; + else if (NSStringFinder::Equals(wsValue, L"groups")) + oTableStyles.m_enRules = ETableRules::Groups; + else if (NSStringFinder::Equals(wsValue, L"cols")) + oTableStyles.m_enRules = ETableRules::Cols; + else if (NSStringFinder::Equals(wsValue, L"none")) + oTableStyles.m_enRules = ETableRules::None; + else if (NSStringFinder::Equals(wsValue, L"rows")) + oTableStyles.m_enRules = ETableRules::Rows; + } + + std::wstring wsFrame; + oTableNode.GetAttributeValue(L"frame", wsFrame); + + if (!wsFrame.empty() && oTableStyles.m_oBorder.Empty()) + { + #define SetDefaultBorderSide(side) \ + oTableStyles.m_oBorder.SetStyle##side(L"solid", 0, true); \ + oTableStyles.m_oBorder.SetWidth##side(1, NSCSS::UnitMeasure::Point, 0, true); \ + oTableStyles.m_oBorder.SetColor##side(L"black", 0, true) + + if (NSStringFinder::Equals(L"border", wsFrame)) + { + SetDefaultBorderSide(); + } + else if (NSStringFinder::Equals(L"above", wsFrame)) + { + SetDefaultBorderSide(TopSide); + } + else if (NSStringFinder::Equals(L"below", wsFrame)) + { + SetDefaultBorderSide(BottomSide); + } + else if (NSStringFinder::Equals(L"hsides", wsFrame)) + { + SetDefaultBorderSide(TopSide); + SetDefaultBorderSide(BottomSide); + } + else if (NSStringFinder::Equals(L"vsides", wsFrame)) + { + SetDefaultBorderSide(LeftSide); + SetDefaultBorderSide(RightSide); + } + else if (NSStringFinder::Equals(L"rhs", wsFrame)) + { + SetDefaultBorderSide(RightSide); + } + else if (NSStringFinder::Equals(L"lhs", wsFrame)) + { + SetDefaultBorderSide(LeftSide); + } + } + + if (!oTableStyles.m_oBorder.Empty() && !oTableStyles.m_oBorder.Zero()) + oXmlString += L"" + CreateBorders(oTableStyles.m_oBorder, NULL, true, (ETableRules::Groups == oTableStyles.m_enRules && !HaveColgroups()) ? ETableRules::Cols : oTableStyles.m_enRules) + L""; + + oTableStyles.m_oPadding = pStyle->m_oPadding; + + if (oTableNode.GetAttributeValue(L"cellpadding", wsValue)) + oTableStyles.m_oPadding.SetValues(wsValue + L"px", 0, true); + + if (!oTableStyles.m_oPadding.Empty() && !oTableStyles.m_oPadding.Zero()) + { + const int nTopPadding = std::max(0, oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nLeftPadding = std::max(0, oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); + const int nBottomPadding = std::max(0, oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nRightPadding = std::max(0, oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH )); + + oXmlString.WriteNodeBegin(L"w:tblCellMar"); + + if (0 != nTopPadding) + oXmlString += L""; + + if (0 != nLeftPadding) + oXmlString += L""; + + if (0 != nBottomPadding) + oXmlString += L""; + + if (0 != nRightPadding) + oXmlString += L""; + + oXmlString.WriteNodeEnd(L"w:tblCellMar"); + } + else + oXmlString += L""; + + } + + oXmlString += L""; + oXmlString.WriteNodeEnd(L"w:tblPr"); + + if (oTable.HaveCaption()) + { + oXmlString.WriteNodeBegin(L"w:tr"); + oXmlString.WriteNodeBegin(L"w:tc"); + oXmlString.WriteNodeBegin(L"w:tcPr"); + oXmlString += L""; + oXmlString += L""; + oXmlString += L""; + oXmlString += L""; + oXmlString += L""; + oXmlString.WriteNodeEnd(L"w:tcPr"); + WriteToStringBuilder(*oTable.m_pCaption, oXmlString); + oXmlString.WriteNodeEnd(L"w:tc"); + oXmlString.WriteNodeEnd(L"w:tr"); + } +} + +void COOXMLTable::CloseTable(XmlString& oXmlString) +{ + oXmlString.WriteNodeEnd(L"w:tbl"); +} + +void COOXMLTable::OpenRow(XmlString& oXmlString, bool bIsHeader, size_t unMaxHeight, size_t unCellSpacing) +{ + oXmlString.WriteNodeBegin(L"w:tr"); + oXmlString.WriteNodeBegin(L"w:trPr"); + + if (bIsHeader) + oXmlString += L""; + + if (0 < unMaxHeight) + oXmlString += L""; + + if (0 < unCellSpacing) + oXmlString += L""; + + oXmlString.WriteNodeEnd(L"w:trPr"); +} + +void COOXMLTable::CloseRow(XmlString& oXmlString) +{ + oXmlString.WriteNodeEnd(L"w:tr"); +} + +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) +{ + OpenCell(oXmlString); + + if (nullptr == pCell) + return; + + oXmlString.WriteNodeBegin(L"w:tcPr"); + + if (ERowParseMode::Header == eRowParseMode) + oXmlString += L""; + + const NSCSS::CCompiledStyle *pColStyle{(nullptr != oTableStyles.m_pTable) ? oTableStyles.m_pTable->GetColStyle(unColumnIndex) : nullptr}; + + const NSCSS::NSProperties::CDigit* pWidth{&oCellNode.m_pCompiledStyle->m_oDisplay.GetWidth()}; + + if (nullptr != pColStyle && pWidth->LessSignificantThen(pColStyle->m_oDisplay.GetWidth())) + pWidth = &pColStyle->m_oDisplay.GetWidth(); + + if (!pWidth->Empty()) + { + if (NSCSS::UnitMeasure::Percent == pWidth->GetUnitMeasure()) + oXmlString += L"ToInt(NSCSS::UnitMeasure::Percent, 5000)) + L"\" w:type=\"pct\"/>"; + else + { + if (!pWidth->Zero()) + { + int nWidth; + if (NSCSS::UnitMeasure::None != pWidth->GetUnitMeasure()) + nWidth = pWidth->ToInt(NSCSS::UnitMeasure::Twips); + else + nWidth = static_cast(NSCSS::CUnitMeasureConverter::ConvertPx(pWidth->ToDouble(), NSCSS::UnitMeasure::Twips, 96) + 0.5); + + oXmlString += L""; + } + else + oXmlString += L""; + } + } + 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""; + } + + const NSCSS::NSProperties::CBorder* pBorder{&oCellNode.m_pCompiledStyle->m_oBorder}; + + if (nullptr != pColStyle && !pColStyle->m_oBorder.Empty() && !pColStyle->m_oBorder.Zero()) + pBorder = &pColStyle->m_oBorder; + + if (!pBorder->Empty() && !pBorder->Zero() && *pBorder != oTableStyles.m_oBorder) + oXmlString += L"" + CreateBorders(*pBorder, &oCellNode.m_pCompiledStyle->m_oPadding) + L""; + else if (ETableRules::Groups == oTableStyles.m_enRules) + { + std::wstring wsBorders; + + if (nullptr != oTableStyles.m_pTable && oTableStyles.m_pTable->HaveColgroups()) + wsBorders += CalculateSidesToClean(unColumnIndex, oTableStyles.m_pTable->m_arColgroups, oTableStyles.m_pTable->GetColumnsCount()); + + if (ERowParseMode::Header == eRowParseMode && ERowPosition::Last == eRowPosition) + wsBorders += CreateDefaultBorder(L"bottom"); + else if (ERowParseMode::Foother == eRowParseMode && ERowPosition::First == eRowPosition) + wsBorders += CreateDefaultBorder(L"top"); + + if (!wsBorders.empty()) + oXmlString += L"" + wsBorders + L""; + } + + const NSCSS::NSProperties::CColor* pBackground{&oCellNode.m_pCompiledStyle->m_oBackground.GetColor()}; + + if (nullptr != pColStyle && pBackground->LessSignificantThen(pColStyle->m_oBackground.GetColor())) + pBackground = &pColStyle->m_oBackground.GetColor(); + + if (!pBackground->Empty()) + { + const std::wstring wsShdFill{(NSCSS::NSProperties::ColorNone == pBackground->GetType()) ? L"auto" : pBackground->ToWString()}; + oXmlString += L""; + } + + if (!oCellNode.m_pCompiledStyle->m_oText.GetAlign().Empty()) + oXmlString += L"m_oText.GetAlign().ToWString() + L"\"/>"; + else + oXmlString += L""; + + NSCSS::NSProperties::CIndent oPadding{oCellNode.m_pCompiledStyle->m_oPadding}; + + if (nullptr != pColStyle && !pColStyle->Empty()) + oPadding += pColStyle->m_oPadding; + + if (!oPadding.Empty() && oTableStyles.m_oPadding != oPadding) + { + const int nTopPadding = std::max(oTableStyles.m_oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oPadding.GetTop() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nLeftPadding = std::max(oTableStyles.m_oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oPadding.GetLeft() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + const int nBottomPadding = std::max(oTableStyles.m_oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT), + oPadding.GetBottom().ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_HEIGHT)); + const int nRightPadding = std::max(oTableStyles.m_oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH), + oPadding.GetRight() .ToInt(NSCSS::UnitMeasure::Twips, DEFAULT_PAGE_WIDTH)); + + oXmlString += L"" + "" + "" + "" + "" + ""; + } + + oXmlString += L""; + oXmlString.WriteNodeEnd(L"w:tcPr"); +} + +void COOXMLTable::CloseCell(XmlString& oXmlString) +{ + oXmlString.WriteNodeEnd(L"w:tc"); +} + +void COOXMLTable::ConvertTable(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, const COOXMLTable& oTable, const NSCSS::CNode& oTableNode) +{ + std::vector arTableSelectors{oTableNode}; + + XmlString& oCurrentDocument{*oWriter.GetCurrentDocument()}; + TTableStyles oTableStyles; + + oTableStyles.m_pTable = &oTable; + + OpenTable(oCurrentDocument, oTableNode, oTableStyles, oTable); + + std::stack arDepths({oReader.GetDepth()}); + + while(oReader.ReadNextSiblingNode2(arDepths.top())) + { + const std::wstring sName = oReader.GetName(); + m_pExternalData->GetSubClass(oReader, arTableSelectors); + + if(sName == L"thead") + ConvertMatrix(oReader, oWriter, arTableSelectors, arDepths, oTable.m_oHeader.GetMatrixCells(), oTableStyles, ERowParseMode::Header); + else if(sName == L"tbody") + ConvertMatrix(oReader, oWriter, arTableSelectors, arDepths, oTable.m_oBody.GetMatrixCells(), oTableStyles, ERowParseMode::Body); + else if(sName == L"tfoot") + ConvertMatrix(oReader, oWriter, arTableSelectors, arDepths, oTable.m_oFoother.GetMatrixCells(), oTableStyles, ERowParseMode::Foother); + else + arTableSelectors.pop_back(); + } + + CloseTable(oCurrentDocument); + oWriter.WriteEmptyParagraph(true); +} + +void COOXMLTable::ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, std::vector& arSelectors, std::stack& arDepths, const Table& oMatrix, TTableStyles& oTableStyles, ERowParseMode eRowParseMode) +{ + if (oMatrix.empty()) + return; + + arDepths.push(oReader.GetDepth()); + + ITableElementCell* pTableCell{nullptr}; + size_t unCellHeight{0}, unMaxCellHeight{0}; + + for (size_t unRowIndex = 0; unRowIndex < oMatrix.size(); ++unRowIndex) + { + XmlString oCellsData; + oWriter.SetDataOutput(&oCellsData); + + for (size_t unColumnIndex = 0; unColumnIndex < oMatrix[unRowIndex].size(); ++unColumnIndex) + { + pTableCell = oMatrix[unRowIndex][unColumnIndex]; + + if (nullptr != pTableCell) + { + MoveToNextTableCell(oReader, arSelectors, arDepths, m_pExternalData->GetSubClass); + + if (ETableElement::Table == pTableCell->GetType()) + { + OpenCell(oCellsData); + + const COOXMLTable* pTable{dynamic_cast(pTableCell)}; + + if (nullptr == pTableCell) + oWriter.WriteEmptyParagraph(); + else + { + arDepths.push(oReader.GetDepth()); + MoveToNextTableCell(oReader, arSelectors, arDepths, m_pExternalData->GetSubClass); + ConvertTable(oReader, oWriter, *pTable, arSelectors.back()); + } + } + else + { + 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_pExternalData->ReadStream(oReader, arSelectors); + } + + oWriter.CloseP(); + CloseCell(oCellsData); + + arSelectors.pop_back(); + } + else + { + OpenCell(oCellsData); + oWriter.WriteEmptyParagraph(); + CloseCell(oCellsData); + } + } + + oWriter.RevertDataOutput(); + + OpenRow(*oWriter.GetCurrentDocument(), ERowParseMode::Header == eRowParseMode, unMaxCellHeight, 0); + WriteToStringBuilder(oCellsData, *oWriter.GetCurrentDocument()); + CloseRow(*oWriter.GetCurrentDocument()); + + arSelectors.pop_back(); + arDepths.pop(); + } + + arSelectors.pop_back(); + arDepths.pop(); + return; +} + +void COOXMLTable::Normalize() +{} + +bool COOXMLTable::Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode) +{ + if (nullptr == m_pExternalData || Empty()) + return false; + + COOXMLWriter* pWriter{dynamic_cast(m_pExternalData->m_pWriter)}; + + if (nullptr == pWriter) + return false; + + ConvertTable(oReader, *pWriter, *this, oTableNode); + + return true; +} + +bool COOXMLTable::ParseCaption(XmlUtils::CXmlLiteReader& oReader, XmlString*& pCaption) +{ + if (nullptr == m_pExternalData || nullptr == m_pExternalData->m_pWriter) + return false; + + if (nullptr == pCaption) + pCaption = new XmlString(200); + + std::vector arSelectors; + m_pExternalData->GetSubClass(oReader, arSelectors); + + arSelectors.back().m_pCompiledStyle->m_oText.SetAlign(L"center", 0, true); + + COOXMLWriter& oWriter{*(COOXMLWriter*)m_pExternalData->m_pWriter}; + + oWriter.SetDataOutput(m_pCaption); + oWriter.WritePPr(arSelectors); + m_pExternalData->ReadStream(oReader, arSelectors); + oWriter.CloseP(); + oWriter.RevertDataOutput(); + + return true; +} + +bool COOXMLTable::ParseColgroup(XmlUtils::CXmlLiteReader& oReader, std::vector& arColgroups) +{ + if (nullptr == m_pExternalData) + return false; + + std::vector arNodes; + + m_pExternalData->GetSubClass(oReader, arNodes); + + CTableColgroup *pColgroup = new CTableColgroup(arNodes.back()); + + if (NULL == pColgroup) + return false; + + const int nDeath = oReader.GetDepth(); + if (!oReader.IsEmptyNode() && oReader.ReadNextSiblingNode2(nDeath)) + { + do + { + if (L"col" != oReader.GetName()) + continue; + + m_pExternalData->GetSubClass(oReader, arNodes); + + CTableCol *pCol = new CTableCol(arNodes.back()); + + if (NULL == pCol) + continue; + + arNodes.pop_back(); + pColgroup->AddCol(pCol); + } while(oReader.ReadNextSiblingNode2(nDeath)); + } + + // if(pColgroup->Empty()) + // { + // std::map::const_iterator itFound = arNodes.begin()->m_mAttributes.find(L"span"); + + // CTableCol *pCol = new CTableCol((arNodes.begin()->m_mAttributes.cend() != itFound) ? NSStringFinder::ToInt(itFound->second, 1) : 1); + + // if (NULL == pCol) + // return; + + // CalculateCellStyles(pCol->GetStyle(), arNodes); + + // pColgroup->AddCol(pCol); + // } + + arColgroups.push_back(pColgroup); + + return true; +} } diff --git a/HtmlFile2/Tags/OOXMLTags.h b/HtmlFile2/Tags/OOXMLTags.h index 9d906090fe..c584a8bd9e 100644 --- a/HtmlFile2/Tags/OOXMLTags.h +++ b/HtmlFile2/Tags/OOXMLTags.h @@ -3,6 +3,7 @@ #include "HTMLTags.h" #include "../Writers/OOXMLWriter.h" +#include "../Table.h" namespace HTML { @@ -127,33 +128,6 @@ public: virtual void Close(const std::vector& arSelectors) override; }; -template<> -class CTable : public CTag -{ -public: - CTable(COOXMLWriter* pInterpretator); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - -template<> -class CTableRow : public CTag -{ -public: - CTableRow(COOXMLWriter* pInterpretator); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - -template<> -class CTableCell : public CTag -{ -public: - CTableCell(COOXMLWriter* pInterpretator); - virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; - virtual void Close(const std::vector& arSelectors) override; -}; - template<> class CHTML : public CTag { @@ -162,6 +136,70 @@ public: virtual bool Open(const std::vector& arSelectors, const boost::any& oExtraData = boost::any()) override; virtual void Close(const std::vector& arSelectors) override; }; + +enum class ETableRules +{ + All, + Groups, + Cols, + None, + Rows +}; + +class COOXMLTable : public CTableElement +{ +public: + COOXMLTable(TExternalTableData* pExternalData); + virtual ~COOXMLTable(); + + bool PreParse(XmlUtils::CXmlLiteReader& oReader) override; + void Normalize() override; + bool Convert(XmlUtils::CXmlLiteReader& oReader, const NSCSS::CNode& oTableNode) override; +private: + bool ParseCaption(XmlUtils::CXmlLiteReader& oReader, XmlString*& pCaption) override; + bool ParseColgroup(XmlUtils::CXmlLiteReader& oReader, std::vector& arColgroups) override; + + bool HaveColgroups() const; + size_t GetColumnsCount() const; + + struct TTableStyles + { + size_t m_unCellSpacing; + ETableRules m_enRules; + + const COOXMLTable *m_pTable; + + NSCSS::NSProperties::CBorder m_oBorder; + NSCSS::NSProperties::CIndent m_oPadding; + }; + + enum class ERowParseMode + { + Header, + Body, + Foother + }; + + enum class ERowPosition + { + First, + Middle, + Last + }; + + void OpenTable(XmlString& oXmlString, const NSCSS::CNode& oTableNode, TTableStyles& oTableStyles, const COOXMLTable& oTable); + void CloseTable(XmlString& oXmlString); + + void OpenRow(XmlString& oXmlString, bool bIsHeader, size_t unMaxHeight, size_t unCellSpacing); + void CloseRow(XmlString& oXmlString); + + void OpenCell(XmlString& oXmlString); + void OpenCell(XmlString& oXmlString, const NSCSS::CNode& oCellNode, CTableElementCell* pCell, size_t unColumnIndex, ERowParseMode eRowParseMode, ERowPosition eRowPosition, size_t& unHeight, const TTableStyles& oTableStyles); + void CloseCell(XmlString& oXmlString); + + void ConvertTable(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, const COOXMLTable& oTable, const NSCSS::CNode& oTableNode); + void ConvertMatrix(XmlUtils::CXmlLiteReader& oReader, COOXMLWriter& oWriter, std::vector& arSelectors, std::stack& arDepths, const Table& oMatrix, TTableStyles& oTableStyles, ERowParseMode eRowParseMode); +}; } #endif // OOXMLTAGS_H diff --git a/HtmlFile2/Writers/IWriter.h b/HtmlFile2/Writers/IWriter.h index efd46f749c..d36f70ae2c 100644 --- a/HtmlFile2/Writers/IWriter.h +++ b/HtmlFile2/Writers/IWriter.h @@ -6,6 +6,12 @@ namespace HTML { +enum class EWriterType +{ + OOXML, + Markdown +}; + class IWriter { public: @@ -29,6 +35,8 @@ public: virtual XmlString* GetCurrentDocument() const = 0; + virtual EWriterType GetType() const = 0; + //TODO:: перенести разруливание вложенных таблиц в конвертацию после изменения принципа работы с таблицами virtual bool SupportNestedTables() const = 0; }; diff --git a/HtmlFile2/Writers/MDWriter.cpp b/HtmlFile2/Writers/MDWriter.cpp index 51093f3dd5..5676a54585 100644 --- a/HtmlFile2/Writers/MDWriter.cpp +++ b/HtmlFile2/Writers/MDWriter.cpp @@ -277,6 +277,11 @@ XmlString* CMDWriter::GetCurrentDocument() const return m_arStates.top().m_pCurrentDocument; } +EWriterType CMDWriter::GetType() const +{ + return EWriterType::Markdown; +} + bool CMDWriter::SupportNestedTables() const { return false; diff --git a/HtmlFile2/Writers/MDWriter.h b/HtmlFile2/Writers/MDWriter.h index 413fb77029..341edd101d 100644 --- a/HtmlFile2/Writers/MDWriter.h +++ b/HtmlFile2/Writers/MDWriter.h @@ -65,6 +65,8 @@ public: XmlString* GetCurrentDocument() const override; + EWriterType GetType() const override; + bool SupportNestedTables() const override; void WriteBreakLine(bool bNeedChecked = true); diff --git a/HtmlFile2/Writers/OOXMLWriter.cpp b/HtmlFile2/Writers/OOXMLWriter.cpp index 5c1ec4379e..dc0096506b 100644 --- a/HtmlFile2/Writers/OOXMLWriter.cpp +++ b/HtmlFile2/Writers/OOXMLWriter.cpp @@ -1261,6 +1261,11 @@ XmlString* COOXMLWriter::GetCurrentDocument() const return m_arStates.top().m_pCurrentDocument; } +EWriterType COOXMLWriter::GetType() const +{ + return EWriterType::OOXML; +} + bool COOXMLWriter::SupportNestedTables() const { return true; diff --git a/HtmlFile2/Writers/OOXMLWriter.h b/HtmlFile2/Writers/OOXMLWriter.h index 1f9bd20fa0..7081a71bf9 100644 --- a/HtmlFile2/Writers/OOXMLWriter.h +++ b/HtmlFile2/Writers/OOXMLWriter.h @@ -200,6 +200,8 @@ public: XmlString& GetWebSettingsXml(); XmlString* GetCurrentDocument() const override; + EWriterType GetType() const override; + bool SupportNestedTables() const override; const NSCSS::NSProperties::CPage* GetPageData() const; diff --git a/HtmlFile2/htmlfile2.cpp b/HtmlFile2/htmlfile2.cpp index cfa58a5047..d8585669b4 100644 --- a/HtmlFile2/htmlfile2.cpp +++ b/HtmlFile2/htmlfile2.cpp @@ -71,7 +71,7 @@ const std::map m_HTML_TAGS ADD_TAG(L"acronym", ACRONYM), ADD_TAG(L"address", ADDRESS), ADD_TAG(L"applet", APPLET), - ADD_TAG(L"area", AREA), + ADD_TAG(L"area", AREA), ADD_TAG(L"article", ARTICLE), ADD_TAG(L"aside", ASIDE), ADD_TAG(L"audio", AUDIO),