1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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
)
39 TagLogger::getInstance().startElement("tablemanager.openCell");
40 TagLogger::getInstance().chars(XTextRangeToString(rHandle
));
41 TagLogger::getInstance().endElement();
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
)
58 SAL_WARN("writerfilter", "TableManager::getGridBefore called while not in table");
61 if (nRow
>= mTableDataStack
.back()->getRowCount())
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
)
80 SAL_WARN("writerfilter", "TableManager::getGridAfter called while not in table");
83 if (nRow
>= mTableDataStack
.back()->getRowCount())
85 return mTableDataStack
.back()->getRow(nRow
)->getGridAfter();
88 void TableManager::setCurrentGridAfter(sal_uInt32 nSkipGrids
)
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
);
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())
144 if (nCell
> nMaxCell
)
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
)
157 TagLogger::getInstance().startElement("tablemanager.insertTableProps");
160 if (getTableProps() && getTableProps() != pProps
)
161 getTableProps()->InsertProps(pProps
.get());
163 mState
.setTableProps(pProps
);
166 TagLogger::getInstance().endElement();
170 void TableManager::insertRowProps(const TablePropertyMapPtr
& pProps
)
173 TagLogger::getInstance().startElement("tablemanager.insertRowProps");
177 getRowProps()->InsertProps(pProps
.get());
179 mState
.setRowProps(pProps
);
182 TagLogger::getInstance().endElement();
186 void TableManager::cellProps(const TablePropertyMapPtr
& pProps
)
189 TagLogger::getInstance().startElement("tablemanager.cellProps");
193 getCellProps()->InsertProps(pProps
.get());
195 mState
.setCellProps(pProps
);
198 TagLogger::getInstance().endElement();
202 void TableManager::tableExceptionProps(const TablePropertyMapPtr
& pProps
)
205 TagLogger::getInstance().startElement("tablemanager.tableExceptionProps");
208 if (getTableExceptionProps())
209 getTableExceptionProps()->InsertProps(pProps
.get());
211 mState
.setTableExceptionProps(pProps
);
214 TagLogger::getInstance().endElement();
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
224 sal_Unicode
const nChar
= data
[len
- 1];
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)
237 void TableManager::handle0x7()
240 TagLogger::getInstance().startElement("tablemanager.handle0x7");
243 if (mnTableDepthNew
< 1)
252 TagLogger::getInstance().endElement();
256 bool TableManager::sprm(Sprm
& rSprm
)
259 switch (rSprm
.getId())
261 case NS_ooxml::LN_tblDepth
:
263 Value::Pointer_t pValue
= rSprm
.getValue();
265 cellDepth(pValue
->getInt());
268 case NS_ooxml::LN_inTbl
:
271 case NS_ooxml::LN_tblCell
:
274 case NS_ooxml::LN_tblRow
:
283 void TableManager::closeCell(const css::uno::Reference
<css::text::XTextRange
>& rHandle
)
286 TagLogger::getInstance().startElement("tablemanager.closeCell");
287 TagLogger::getInstance().chars(XTextRangeToString(rHandle
));
288 TagLogger::getInstance().endElement();
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
)
305 TagLogger::getInstance().startElement("tablemanager.ensureOpenCell");
308 if (!mTableDataStack
.empty())
310 TableData::Pointer_t pTableData
= mTableDataStack
.back();
312 if (pTableData
!= nullptr)
314 if (!pTableData
->isCellOpen())
315 openCell(getHandle(), pProps
);
317 pTableData
->insertCellProperties(pProps
);
321 TagLogger::getInstance().endElement();
325 void TableManager::endParagraphGroup()
327 sal_Int32 nTableDepthDifference
= mnTableDepthNew
- mnTableDepth
;
329 TablePropertyMapPtr pEmptyProps
;
331 while (nTableDepthDifference
> 0)
333 ensureOpenCell(pEmptyProps
);
336 --nTableDepthDifference
;
338 while (nTableDepthDifference
< 0)
342 ++nTableDepthDifference
;
345 mnTableDepth
= mnTableDepthNew
;
347 if (mnTableDepth
<= 0)
353 mTableDataStack
.back()->endRow(getRowProps());
354 mState
.resetRowProps();
359 ensureOpenCell(getCellProps());
361 if (mState
.isCellEnd())
364 closeCell(getHandle());
367 mState
.resetCellProps();
370 void TableManager::startParagraphGroup()
372 mState
.resetCellSpecifics();
376 void TableManager::resolveCurrentTable()
379 TagLogger::getInstance().startElement("tablemanager.resolveCurrentTable");
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();
422 TagLogger::getInstance().endElement();
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();
440 uno::Reference
<text::XTextRange
> xCellStart
= pRow
->getCellStart(nCells
- 1);
445 xCursor
= xCellStart
->getText()->createTextCursorByRange(
446 xCellStart
->getStart());
449 xCursor
->goLeft(1, false);
452 catch (const uno::RuntimeException
&)
454 TOOLS_WARN_EXCEPTION(
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();
468 mTableDataStack
.pop_back();
470 TableData::Pointer_t pTableData
;
472 if (!mTableDataStack
.empty())
473 pTableData
= mTableDataStack
.back();
476 TagLogger::getInstance().startElement("tablemanager.endLevel");
477 TagLogger::getInstance().attribute("level", mTableDataStack
.size());
480 if (pTableData
!= nullptr)
483 TagLogger::getInstance().attribute("openCell", pTableData
->isCellOpen() ? "yes" : "no");
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");
503 TagLogger::getInstance().endElement();
507 void TableManager::startLevel()
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();
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
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
);
545 bool TableManager::isInTable()
547 bool bInTable
= false;
548 if (!mTableDataStack
.empty())
549 bInTable
= mTableDataStack
.back()->getDepth() > 0;
553 void TableManager::handle(const css::uno::Reference
<css::text::XTextRange
>& rHandle
)
556 TagLogger::getInstance().startElement("tablemanager.handle");
557 TagLogger::getInstance().chars(XTextRangeToString(rHandle
));
558 TagLogger::getInstance().endElement();
564 void TableManager::setHandler(const tools::SvRef
<DomainMapperTableHandler
>& pTableDataHandler
)
566 mpTableDataHandler
= pTableDataHandler
;
569 void TableManager::endRow()
572 TagLogger::getInstance().element("tablemanager.endRow");
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);
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);
617 void TableManager::endCell()
620 TagLogger::getInstance().element("tablemanager.endCell");
626 void TableManager::inCell()
629 TagLogger::getInstance().element("tablemanager.inCell");
633 if (mnTableDepthNew
< 1)
637 void TableManager::cellDepth(sal_uInt32 nDepth
)
640 TagLogger::getInstance().startElement("tablemanager.cellDepth");
641 TagLogger::getInstance().attribute("depth", nDepth
);
642 TagLogger::getInstance().endElement();
645 mnTableDepthNew
= nDepth
;
648 void TableManager::setCellLastParaAfterAutospacing(bool bIsAfterAutospacing
)
650 m_bCellLastParaAfterAutospacing
= bIsAfterAutospacing
;
653 TableManager::TableManager()
656 , mbKeepUnfinishedRow(false)
661 m_bCellLastParaAfterAutospacing
= false;
664 TableManager::~TableManager() = default;
666 bool CellData::IsValid() const
677 catch (const uno::RuntimeException
&)
686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */