Avoid potential negative array index access to cached text.
[LibreOffice.git] / writerfilter / source / dmapper / TableManager.cxx
blob6a0c70a39f52a59db27a029fe6f160284e6246d2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "TableManager.hxx"
21 #include <ooxml/resourceids.hxx>
22 #include "TagLogger.hxx"
23 #include "DomainMapperTableHandler.hxx"
24 #include "DomainMapper_Impl.hxx"
25 #include "util.hxx"
27 #include <comphelper/diagnose_ex.hxx>
29 using namespace com::sun::star;
31 namespace writerfilter::dmapper
33 void TableManager::clearData() {}
35 void TableManager::openCell(const css::uno::Reference<css::text::XTextRange>& rHandle,
36 const TablePropertyMapPtr& pProps)
38 #ifdef DBG_UTIL
39 TagLogger::getInstance().startElement("tablemanager.openCell");
40 TagLogger::getInstance().chars(XTextRangeToString(rHandle));
41 TagLogger::getInstance().endElement();
42 #endif
44 if (!mTableDataStack.empty())
46 TableData::Pointer_t pTableData = mTableDataStack.back();
48 pTableData->addCell(rHandle, pProps);
52 bool TableManager::isIgnore() const { return isRowEnd(); }
54 sal_uInt32 TableManager::getGridBefore(sal_uInt32 nRow)
56 if (!isInTable())
58 SAL_WARN("writerfilter", "TableManager::getGridBefore called while not in table");
59 return 0;
61 if (nRow >= mTableDataStack.back()->getRowCount())
62 return 0;
63 return mTableDataStack.back()->getRow(nRow)->getGridBefore();
66 sal_uInt32 TableManager::getCurrentGridBefore()
68 return mTableDataStack.back()->getCurrentRow()->getGridBefore();
71 void TableManager::setCurrentGridBefore(sal_uInt32 nSkipGrids)
73 mTableDataStack.back()->getCurrentRow()->setGridBefore(nSkipGrids);
76 sal_uInt32 TableManager::getGridAfter(sal_uInt32 nRow)
78 if (!isInTable())
80 SAL_WARN("writerfilter", "TableManager::getGridAfter called while not in table");
81 return 0;
83 if (nRow >= mTableDataStack.back()->getRowCount())
84 return 0;
85 return mTableDataStack.back()->getRow(nRow)->getGridAfter();
88 void TableManager::setCurrentGridAfter(sal_uInt32 nSkipGrids)
90 assert(isInTable());
91 mTableDataStack.back()->getCurrentRow()->setGridAfter(nSkipGrids);
94 std::vector<sal_uInt32> TableManager::getCurrentGridSpans()
96 return mTableDataStack.back()->getCurrentRow()->getGridSpans();
99 void TableManager::setCurrentGridSpan(sal_uInt32 nGridSpan, bool bFirstCell)
101 mTableDataStack.back()->getCurrentRow()->setCurrentGridSpan(nGridSpan, bFirstCell);
104 sal_uInt32 TableManager::findColumn(const sal_uInt32 nRow, const sal_uInt32 nCell)
106 if (nRow >= mTableDataStack.back()->getRowCount())
107 return SAL_MAX_UINT32;
109 RowData::Pointer_t pRow = mTableDataStack.back()->getRow(nRow);
110 if (!pRow || nCell < pRow->getGridBefore()
111 || nCell >= pRow->getCellCount() - pRow->getGridAfter())
113 return SAL_MAX_UINT32;
116 // The gridSpans provide a one-based index, so add up all the spans of the PREVIOUS columns,
117 // and that result will provide the first possible zero-based number for the desired column.
118 sal_uInt32 nColumn = 0;
119 for (sal_uInt32 n = 0; n < nCell; ++n)
120 nColumn += pRow->getGridSpan(n);
121 return nColumn;
124 sal_uInt32 TableManager::findColumnCell(const sal_uInt32 nRow, const sal_uInt32 nCol)
126 if (nRow >= mTableDataStack.back()->getRowCount())
127 return SAL_MAX_UINT32;
129 RowData::Pointer_t pRow = mTableDataStack.back()->getRow(nRow);
130 if (!pRow || nCol < pRow->getGridBefore())
131 return SAL_MAX_UINT32;
133 sal_uInt32 nCell = 0;
134 sal_uInt32 nGrids = 0;
135 // The gridSpans give us a one-based index, but requested column is zero-based - so keep that in mind.
136 const sal_uInt32 nMaxCell = pRow->getCellCount() - pRow->getGridAfter() - 1;
137 for (const auto& rSpan : pRow->getGridSpans())
139 nGrids += rSpan;
140 if (nCol < nGrids)
141 return nCell;
143 ++nCell;
144 if (nCell > nMaxCell)
145 break;
147 return SAL_MAX_UINT32; // must be in gridAfter or invalid column request
150 void TableManager::endOfRowAction() {}
152 void TableManager::endOfCellAction() {}
154 void TableManager::insertTableProps(const TablePropertyMapPtr& pProps)
156 #ifdef DBG_UTIL
157 TagLogger::getInstance().startElement("tablemanager.insertTableProps");
158 #endif
160 if (getTableProps() && getTableProps() != pProps)
161 getTableProps()->InsertProps(pProps.get());
162 else
163 mState.setTableProps(pProps);
165 #ifdef DBG_UTIL
166 TagLogger::getInstance().endElement();
167 #endif
170 void TableManager::insertRowProps(const TablePropertyMapPtr& pProps)
172 #ifdef DBG_UTIL
173 TagLogger::getInstance().startElement("tablemanager.insertRowProps");
174 #endif
176 if (getRowProps())
177 getRowProps()->InsertProps(pProps.get());
178 else
179 mState.setRowProps(pProps);
181 #ifdef DBG_UTIL
182 TagLogger::getInstance().endElement();
183 #endif
186 void TableManager::cellProps(const TablePropertyMapPtr& pProps)
188 #ifdef DBG_UTIL
189 TagLogger::getInstance().startElement("tablemanager.cellProps");
190 #endif
192 if (getCellProps())
193 getCellProps()->InsertProps(pProps.get());
194 else
195 mState.setCellProps(pProps);
197 #ifdef DBG_UTIL
198 TagLogger::getInstance().endElement();
199 #endif
202 void TableManager::tableExceptionProps(const TablePropertyMapPtr& pProps)
204 #ifdef DBG_UTIL
205 TagLogger::getInstance().startElement("tablemanager.tableExceptionProps");
206 #endif
208 if (getTableExceptionProps())
209 getTableExceptionProps()->InsertProps(pProps.get());
210 else
211 mState.setTableExceptionProps(pProps);
213 #ifdef DBG_UTIL
214 TagLogger::getInstance().endElement();
215 #endif
218 void TableManager::utext(const sal_Unicode* const data, std::size_t const len)
220 // optimization: cell/row end characters are the last characters in a run
222 if (len > 0)
224 sal_Unicode const nChar = data[len - 1];
225 if (nChar == 0x7)
226 handle0x7();
230 void TableManager::text(const sal_uInt8* data, std::size_t len)
232 // optimization: cell/row end characters are the last characters in a run
233 if (len > 0 && data[len - 1] == 0x7)
234 handle0x7();
237 void TableManager::handle0x7()
239 #ifdef DBG_UTIL
240 TagLogger::getInstance().startElement("tablemanager.handle0x7");
241 #endif
243 if (mnTableDepthNew < 1)
244 mnTableDepthNew = 1;
246 if (isInCell())
247 endCell();
248 else
249 endRow();
251 #ifdef DBG_UTIL
252 TagLogger::getInstance().endElement();
253 #endif
256 bool TableManager::sprm(Sprm& rSprm)
258 bool bRet = true;
259 switch (rSprm.getId())
261 case NS_ooxml::LN_tblDepth:
263 Value::Pointer_t pValue = rSprm.getValue();
265 cellDepth(pValue->getInt());
267 break;
268 case NS_ooxml::LN_inTbl:
269 inCell();
270 break;
271 case NS_ooxml::LN_tblCell:
272 endCell();
273 break;
274 case NS_ooxml::LN_tblRow:
275 endRow();
276 break;
277 default:
278 bRet = false;
280 return bRet;
283 void TableManager::closeCell(const css::uno::Reference<css::text::XTextRange>& rHandle)
285 #ifdef DBG_UTIL
286 TagLogger::getInstance().startElement("tablemanager.closeCell");
287 TagLogger::getInstance().chars(XTextRangeToString(rHandle));
288 TagLogger::getInstance().endElement();
289 #endif
291 if (!mTableDataStack.empty())
293 TableData::Pointer_t pTableData = mTableDataStack.back();
295 pTableData->endCell(rHandle);
297 if (mpTableDataHandler)
298 mpTableDataHandler->getDomainMapperImpl().ClearPreviousParagraph();
302 void TableManager::ensureOpenCell(const TablePropertyMapPtr& pProps)
304 #ifdef DBG_UTIL
305 TagLogger::getInstance().startElement("tablemanager.ensureOpenCell");
306 #endif
308 if (!mTableDataStack.empty())
310 TableData::Pointer_t pTableData = mTableDataStack.back();
312 if (pTableData != nullptr)
314 if (!pTableData->isCellOpen())
315 openCell(getHandle(), pProps);
316 else
317 pTableData->insertCellProperties(pProps);
320 #ifdef DBG_UTIL
321 TagLogger::getInstance().endElement();
322 #endif
325 void TableManager::endParagraphGroup()
327 sal_Int32 nTableDepthDifference = mnTableDepthNew - mnTableDepth;
329 TablePropertyMapPtr pEmptyProps;
331 while (nTableDepthDifference > 0)
333 ensureOpenCell(pEmptyProps);
334 startLevel();
336 --nTableDepthDifference;
338 while (nTableDepthDifference < 0)
340 endLevel();
342 ++nTableDepthDifference;
345 mnTableDepth = mnTableDepthNew;
347 if (mnTableDepth <= 0)
348 return;
350 if (isRowEnd())
352 endOfRowAction();
353 mTableDataStack.back()->endRow(getRowProps());
354 mState.resetRowProps();
357 else if (isInCell())
359 ensureOpenCell(getCellProps());
361 if (mState.isCellEnd())
363 endOfCellAction();
364 closeCell(getHandle());
367 mState.resetCellProps();
370 void TableManager::startParagraphGroup()
372 mState.resetCellSpecifics();
373 mnTableDepthNew = 0;
376 void TableManager::resolveCurrentTable()
378 #ifdef DBG_UTIL
379 TagLogger::getInstance().startElement("tablemanager.resolveCurrentTable");
380 #endif
382 if (mpTableDataHandler != nullptr)
386 TableData::Pointer_t pTableData = mTableDataStack.back();
388 unsigned int nRows = pTableData->getRowCount();
390 mpTableDataHandler->startTable(getTableProps());
392 for (unsigned int nRow = 0; nRow < nRows; ++nRow)
394 RowData::Pointer_t pRowData = pTableData->getRow(nRow);
396 unsigned int nCells = pRowData->getCellCount();
398 mpTableDataHandler->startRow(pRowData->getProperties());
400 for (unsigned int nCell = 0; nCell < nCells; ++nCell)
402 mpTableDataHandler->startCell(pRowData->getCellStart(nCell),
403 pRowData->getCellProperties(nCell));
405 mpTableDataHandler->endCell(pRowData->getCellEnd(nCell));
408 mpTableDataHandler->endRow();
411 mpTableDataHandler->endTable(mTableDataStack.size() - 1);
413 catch (css::uno::Exception const&)
415 TOOLS_WARN_EXCEPTION("writerfilter", "resolving of current table failed");
418 mState.resetTableProps();
419 clearData();
421 #ifdef DBG_UTIL
422 TagLogger::getInstance().endElement();
423 #endif
426 void TableManager::endLevel()
428 uno::Reference<text::XTextCursor> xCursor;
429 if (mpTableDataHandler != nullptr)
431 if (mTableDataStack.size() > 1)
433 // This is an inner table: create a cursor from the outer cell's start position, in case
434 // that would become invalid during the current table resolution.
435 TableData::Pointer_t pUpperTableData = mTableDataStack[mTableDataStack.size() - 2];
436 RowData::Pointer_t pRow = pUpperTableData->getCurrentRow();
437 unsigned int nCells = pRow->getCellCount();
438 if (nCells > 0)
440 uno::Reference<text::XTextRange> xCellStart = pRow->getCellStart(nCells - 1);
441 if (xCellStart.is())
445 xCursor = xCellStart->getText()->createTextCursorByRange(
446 xCellStart->getStart());
447 if (xCursor.is())
449 xCursor->goLeft(1, false);
452 catch (const uno::RuntimeException&)
454 TOOLS_WARN_EXCEPTION(
455 "writerfilter",
456 "TableManager::endLevel: createTextCursorByRange() failed");
461 resolveCurrentTable();
464 // Store the unfinished row as it will be used for the next table
465 if (mbKeepUnfinishedRow)
466 mpUnfinishedRow = mTableDataStack.back()->getCurrentRow();
467 mState.endLevel();
468 mTableDataStack.pop_back();
470 TableData::Pointer_t pTableData;
472 if (!mTableDataStack.empty())
473 pTableData = mTableDataStack.back();
475 #ifdef DBG_UTIL
476 TagLogger::getInstance().startElement("tablemanager.endLevel");
477 TagLogger::getInstance().attribute("level", mTableDataStack.size());
478 #endif
480 if (pTableData != nullptr)
482 #ifdef DBG_UTIL
483 TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
484 #endif
485 if (pTableData->isCellOpen() && !pTableData->IsCellValid() && xCursor.is())
487 // The inner table is resolved and we have an outer table, but the currently opened
488 // cell's start position is no longer valid. Try to move the cursor back to where it was
489 // and update the cell start position accordingly.
492 xCursor->goRight(1, false);
493 pTableData->SetCellStart(xCursor->getStart());
495 catch (const uno::RuntimeException&)
497 TOOLS_WARN_EXCEPTION("writerfilter", "TableManager::endLevel: goRight() failed");
502 #ifdef DBG_UTIL
503 TagLogger::getInstance().endElement();
504 #endif
507 void TableManager::startLevel()
509 #ifdef DBG_UTIL
510 TableData::Pointer_t pTableData;
512 if (!mTableDataStack.empty())
513 pTableData = mTableDataStack.back();
515 TagLogger::getInstance().startElement("tablemanager.startLevel");
516 TagLogger::getInstance().attribute("level", mTableDataStack.size());
518 if (pTableData != nullptr)
519 TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
521 TagLogger::getInstance().endElement();
522 #endif
524 TableData::Pointer_t pTableData2(new TableData(mTableDataStack.size()));
526 // If we have an unfinished row stored here, then push it to the new TableData
527 if (mpUnfinishedRow)
529 for (unsigned int i = 0; i < mpUnfinishedRow->getCellCount(); ++i)
531 pTableData2->addCell(mpUnfinishedRow->getCellStart(i),
532 mpUnfinishedRow->getCellProperties(i));
533 pTableData2->endCell(mpUnfinishedRow->getCellEnd(i));
534 pTableData2->getCurrentRow()->setCurrentGridSpan(mpUnfinishedRow->getGridSpan(i));
536 pTableData2->getCurrentRow()->setGridBefore(mpUnfinishedRow->getGridBefore());
537 pTableData2->getCurrentRow()->setGridAfter(mpUnfinishedRow->getGridAfter());
538 mpUnfinishedRow.clear();
541 mTableDataStack.push_back(pTableData2);
542 mState.startLevel();
545 bool TableManager::isInTable()
547 bool bInTable = false;
548 if (!mTableDataStack.empty())
549 bInTable = mTableDataStack.back()->getDepth() > 0;
550 return bInTable;
553 void TableManager::handle(const css::uno::Reference<css::text::XTextRange>& rHandle)
555 #ifdef DBG_UTIL
556 TagLogger::getInstance().startElement("tablemanager.handle");
557 TagLogger::getInstance().chars(XTextRangeToString(rHandle));
558 TagLogger::getInstance().endElement();
559 #endif
561 setHandle(rHandle);
564 void TableManager::setHandler(const tools::SvRef<DomainMapperTableHandler>& pTableDataHandler)
566 mpTableDataHandler = pTableDataHandler;
569 void TableManager::endRow()
571 #ifdef DBG_UTIL
572 TagLogger::getInstance().element("tablemanager.endRow");
573 #endif
574 TableData::Pointer_t pTableData = mTableDataStack.back();
576 // Add borderless w:gridBefore cell(s) to the row
577 sal_uInt32 nGridBefore = getCurrentGridBefore();
578 if (pTableData && nGridBefore > 0 && pTableData->getCurrentRow()->getCellCount() > 0)
580 const css::uno::Reference<css::text::XTextRange>& xRowStart
581 = pTableData->getCurrentRow()->getCellStart(0);
582 if (xRowStart.is())
586 // valid TextRange for table creation (not a nested table)?
587 xRowStart->getText()->createTextCursorByRange(xRowStart);
589 for (unsigned int i = 0; i < nGridBefore; ++i)
591 css::table::BorderLine2 aBorderLine;
592 aBorderLine.Color = 0;
593 aBorderLine.InnerLineWidth = 0;
594 aBorderLine.OuterLineWidth = 0;
595 TablePropertyMapPtr pCellProperties(new TablePropertyMap);
596 pCellProperties->Insert(PROP_TOP_BORDER, css::uno::Any(aBorderLine));
597 pCellProperties->Insert(PROP_LEFT_BORDER, css::uno::Any(aBorderLine));
598 pCellProperties->Insert(PROP_BOTTOM_BORDER, css::uno::Any(aBorderLine));
599 pCellProperties->Insert(PROP_RIGHT_BORDER, css::uno::Any(aBorderLine));
600 pTableData->getCurrentRow()->addCell(xRowStart, pCellProperties,
601 /*bAddBefore=*/true);
604 catch (css::uno::Exception const&)
606 // don't add gridBefore cells in not valid TextRange
607 setCurrentGridBefore(0);
608 setCurrentGridSpan(getCurrentGridSpans().front() + nGridBefore,
609 /*bFirstCell=*/true);
614 setRowEnd(true);
617 void TableManager::endCell()
619 #ifdef DBG_UTIL
620 TagLogger::getInstance().element("tablemanager.endCell");
621 #endif
623 setCellEnd(true);
626 void TableManager::inCell()
628 #ifdef DBG_UTIL
629 TagLogger::getInstance().element("tablemanager.inCell");
630 #endif
631 setInCell(true);
633 if (mnTableDepthNew < 1)
634 mnTableDepthNew = 1;
637 void TableManager::cellDepth(sal_uInt32 nDepth)
639 #ifdef DBG_UTIL
640 TagLogger::getInstance().startElement("tablemanager.cellDepth");
641 TagLogger::getInstance().attribute("depth", nDepth);
642 TagLogger::getInstance().endElement();
643 #endif
645 mnTableDepthNew = nDepth;
648 void TableManager::setCellLastParaAfterAutospacing(bool bIsAfterAutospacing)
650 m_bCellLastParaAfterAutospacing = bIsAfterAutospacing;
653 TableManager::TableManager()
654 : mnTableDepthNew(0)
655 , mnTableDepth(0)
656 , mbKeepUnfinishedRow(false)
658 setRowEnd(false);
659 setInCell(false);
660 setCellEnd(false);
661 m_bCellLastParaAfterAutospacing = false;
664 TableManager::~TableManager() = default;
666 bool CellData::IsValid() const
668 if (!mStart.is())
670 return false;
675 mStart->getStart();
677 catch (const uno::RuntimeException&)
679 return false;
682 return true;
686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */