Files
core/HtmlFile2/Table.cpp
Kirill Polyakov 65a4fa90a5 Fixed bugs
2026-02-01 23:53:35 +03:00

716 lines
16 KiB
C++

#include "Table.h"
#include "Common.h"
#include "src/StringFinder.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
#define PARSE_MODE_HEADER 0x00000100
#define PARSE_MODE_BODY 0x00000200
#define PARSE_MODE_FOOTHER 0x00000400
#define COL_POSITION_MASK 0x0000000F
#define ROW_POSITION_MASK 0x000000F0
#define PARSE_MODE_MASK 0x00000F00
#define DEFAULT_PAGE_WIDTH 12240 // Значение в Twips
#define DEFAULT_PAGE_HEIGHT 15840 // Значение в Twips
TTableRowStyle::TTableRowStyle()
: m_unMaxIndex(0), m_unMaxHeight(0), m_bIsHeader(false)
{}
bool TTableRowStyle::Empty() const
{
return 0 == m_unMaxHeight && false == m_bIsHeader;
}
TTableCellStyle::TTableCellStyle()
{}
bool TTableCellStyle::Empty()
{
return m_oWidth.Empty() && m_oHeight.Empty() && m_oBorder.Empty() && m_oPadding.Empty() && m_wsVAlign.empty() && m_wsVAlign.empty();
}
void TTableCellStyle::Copy(const TTableCellStyle* pTableCellStyle)
{
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;
}
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)
{}
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)
{
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)
{
std::vector<CStorageTableCell*>::iterator itFoundEmpty = std::find_if(m_arCells.begin(), m_arCells.end(), [](CStorageTableCell* pCell) { return pCell->Empty() && !pCell->Merged(); });
if (m_arCells.end() != itFoundEmpty)
{
--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);
}
}
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
{
return m_arCells.empty();
}
const TTableRowStyle& CStorageTableRow::GetStyles() const
{
return m_oStyles;
}
const std::vector<CStorageTableCell*>& CStorageTableRow::GetCells() const
{
return m_arCells;
}
CTableCol::CTableCol(UINT unSpan)
: m_unSpan(unSpan)
{}
CTableCol::CTableCol(const NSCSS::CNode& oTableColNode)
: m_unSpan(1)
{
m_unSpan = NSStringFinder::ToInt(oTableColNode.GetAttributeValue(L"span"));
}
UINT CTableCol::GetSpan() const
{
return m_unSpan;
}
TTableCellStyle* CTableCol::GetStyle()
{
return &m_oStyle;
}
const TTableCellStyle* CTableCol::GetStyle() const
{
return &m_oStyle;
}
CTableColgroup::CTableColgroup(NSCSS::CNode& oTableColgroupNode)
: m_unWidth(0)
{
m_unWidth = NSStringFinder::ToInt(oTableColgroupNode.GetAttributeValue(L"width"), 0);
}
CTableColgroup::~CTableColgroup()
{
RELEASE_VECTOR_PTR(m_arCols, CTableCol)
}
bool CTableColgroup::Empty() const
{
return m_arCols.empty();
}
void CTableColgroup::AddCol(CTableCol* pCol)
{
if (NULL != pCol)
m_arCols.push_back(pCol);
}
const std::vector<CTableCol*>& CTableColgroup::GetCols() const
{
return m_arCols;
}
TTableStyles::TTableStyles()
: m_nCellSpacing(-1), m_enRules(None)
{}
bool TTableStyles::Empty() const
{
return m_oPadding.Empty() && m_oMargin.Empty() && m_oBorder.Empty() && m_oWidth.Empty() && -1 == m_nCellSpacing && m_wsAlign.empty();
}
CStorageTable::CStorageTable()
: m_unMaxColumns(0)
{}
CStorageTable::~CStorageTable()
{
for (std::vector<CStorageTableRow*>& 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)
{
for (const CTableCol* pCol : pColgroup->GetCols())
{
unCurrentNumber += pCol->GetSpan();
if (unCurrentNumber >= unColumnNumber)
return pCol->GetStyle();
}
}
return NULL;
}
void CStorageTable::AddRows(std::vector<CStorageTableRow*>& 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<CStorageTableRow*>& 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<std::vector<CStorageTableRow*>>& CStorageTable::GetHeaders() const
{
return m_arHeaders;
}
const std::vector<CStorageTableRow*>& CStorageTable::GetFoothers() const
{
return m_arFoother;
}
const std::vector<CStorageTableRow*>& CStorageTable::GetRows() const
{
return m_arRows;
}
const std::vector<CTableColgroup*> CStorageTable::GetColgroups() const
{
return m_arColgroups;
}
UINT CStorageTable::GetMaxColumns() const
{
return m_unMaxColumns;
}
}