1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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"
26 
27 #include <tools/diagnose_ex.h>
28 
29 namespace writerfilter::dmapper
30 {
clearData()31 void TableManager::clearData() {}
32 
openCell(const css::uno::Reference<css::text::XTextRange> & rHandle,const TablePropertyMapPtr & pProps)33 void TableManager::openCell(const css::uno::Reference<css::text::XTextRange>& rHandle,
34                             const TablePropertyMapPtr& pProps)
35 {
36 #ifdef DBG_UTIL
37     TagLogger::getInstance().startElement("tablemanager.openCell");
38     TagLogger::getInstance().chars(XTextRangeToString(rHandle));
39     TagLogger::getInstance().endElement();
40 #endif
41 
42     if (!mTableDataStack.empty())
43     {
44         TableData::Pointer_t pTableData = mTableDataStack.top();
45 
46         pTableData->addCell(rHandle, pProps);
47     }
48 }
49 
isIgnore() const50 bool TableManager::isIgnore() const { return isRowEnd(); }
51 
getGridBefore(sal_uInt32 nRow)52 sal_uInt32 TableManager::getGridBefore(sal_uInt32 nRow)
53 {
54     assert(isInTable());
55     if (nRow >= mTableDataStack.top()->getRowCount())
56         return 0;
57     return mTableDataStack.top()->getRow(nRow)->getGridBefore();
58 }
59 
getCurrentGridBefore()60 sal_uInt32 TableManager::getCurrentGridBefore()
61 {
62     return mTableDataStack.top()->getCurrentRow()->getGridBefore();
63 }
64 
setCurrentGridBefore(sal_uInt32 nSkipGrids)65 void TableManager::setCurrentGridBefore(sal_uInt32 nSkipGrids)
66 {
67     mTableDataStack.top()->getCurrentRow()->setGridBefore(nSkipGrids);
68 }
69 
getGridAfter(sal_uInt32 nRow)70 sal_uInt32 TableManager::getGridAfter(sal_uInt32 nRow)
71 {
72     assert(isInTable());
73     if (nRow >= mTableDataStack.top()->getRowCount())
74         return 0;
75     return mTableDataStack.top()->getRow(nRow)->getGridAfter();
76 }
77 
setCurrentGridAfter(sal_uInt32 nSkipGrids)78 void TableManager::setCurrentGridAfter(sal_uInt32 nSkipGrids)
79 {
80     assert(isInTable());
81     mTableDataStack.top()->getCurrentRow()->setGridAfter(nSkipGrids);
82 }
83 
getCurrentGridSpans()84 std::vector<sal_uInt32> TableManager::getCurrentGridSpans()
85 {
86     return mTableDataStack.top()->getCurrentRow()->getGridSpans();
87 }
88 
setCurrentGridSpan(sal_uInt32 nGridSpan,bool bFirstCell)89 void TableManager::setCurrentGridSpan(sal_uInt32 nGridSpan, bool bFirstCell)
90 {
91     mTableDataStack.top()->getCurrentRow()->setCurrentGridSpan(nGridSpan, bFirstCell);
92 }
93 
findColumn(const sal_uInt32 nRow,const sal_uInt32 nCell)94 sal_uInt32 TableManager::findColumn(const sal_uInt32 nRow, const sal_uInt32 nCell)
95 {
96     if (nRow >= mTableDataStack.top()->getRowCount())
97         return SAL_MAX_UINT32;
98 
99     RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
100     if (!pRow || nCell < pRow->getGridBefore()
101         || nCell >= pRow->getCellCount() - pRow->getGridAfter())
102     {
103         return SAL_MAX_UINT32;
104     }
105 
106     // The gridSpans provide a one-based index, so add up all the spans of the PREVIOUS columns,
107     // and that result will provide the first possible zero-based number for the desired column.
108     sal_uInt32 nColumn = 0;
109     for (sal_uInt32 n = 0; n < nCell; ++n)
110         nColumn += pRow->getGridSpan(n);
111     return nColumn;
112 }
113 
findColumnCell(const sal_uInt32 nRow,const sal_uInt32 nCol)114 sal_uInt32 TableManager::findColumnCell(const sal_uInt32 nRow, const sal_uInt32 nCol)
115 {
116     if (nRow >= mTableDataStack.top()->getRowCount())
117         return SAL_MAX_UINT32;
118 
119     RowData::Pointer_t pRow = mTableDataStack.top()->getRow(nRow);
120     if (!pRow || nCol < pRow->getGridBefore())
121         return SAL_MAX_UINT32;
122 
123     sal_uInt32 nCell = 0;
124     sal_uInt32 nGrids = 0;
125     // The gridSpans give us a one-based index, but requested column is zero-based - so keep that in mind.
126     const sal_uInt32 nMaxCell = pRow->getCellCount() - pRow->getGridAfter() - 1;
127     for (const auto& rSpan : pRow->getGridSpans())
128     {
129         nGrids += rSpan;
130         if (nCol < nGrids)
131             return nCell;
132 
133         ++nCell;
134         if (nCell > nMaxCell)
135             break;
136     }
137     return SAL_MAX_UINT32; // must be in gridAfter or invalid column request
138 }
139 
endOfRowAction()140 void TableManager::endOfRowAction() {}
141 
endOfCellAction()142 void TableManager::endOfCellAction() {}
143 
insertTableProps(const TablePropertyMapPtr & pProps)144 void TableManager::insertTableProps(const TablePropertyMapPtr& pProps)
145 {
146 #ifdef DBG_UTIL
147     TagLogger::getInstance().startElement("tablemanager.insertTableProps");
148 #endif
149 
150     if (getTableProps() && getTableProps() != pProps)
151         getTableProps()->InsertProps(pProps.get());
152     else
153         mState.setTableProps(pProps);
154 
155 #ifdef DBG_UTIL
156     TagLogger::getInstance().endElement();
157 #endif
158 }
159 
insertRowProps(const TablePropertyMapPtr & pProps)160 void TableManager::insertRowProps(const TablePropertyMapPtr& pProps)
161 {
162 #ifdef DBG_UTIL
163     TagLogger::getInstance().startElement("tablemanager.insertRowProps");
164 #endif
165 
166     if (getRowProps())
167         getRowProps()->InsertProps(pProps.get());
168     else
169         mState.setRowProps(pProps);
170 
171 #ifdef DBG_UTIL
172     TagLogger::getInstance().endElement();
173 #endif
174 }
175 
cellProps(const TablePropertyMapPtr & pProps)176 void TableManager::cellProps(const TablePropertyMapPtr& pProps)
177 {
178 #ifdef DBG_UTIL
179     TagLogger::getInstance().startElement("tablemanager.cellProps");
180 #endif
181 
182     if (getCellProps())
183         getCellProps()->InsertProps(pProps.get());
184     else
185         mState.setCellProps(pProps);
186 
187 #ifdef DBG_UTIL
188     TagLogger::getInstance().endElement();
189 #endif
190 }
191 
tableExceptionProps(const TablePropertyMapPtr & pProps)192 void TableManager::tableExceptionProps(const TablePropertyMapPtr& pProps)
193 {
194 #ifdef DBG_UTIL
195     TagLogger::getInstance().startElement("tablemanager.tableExceptionProps");
196 #endif
197 
198     if (getTableExceptionProps())
199         getTableExceptionProps()->InsertProps(pProps.get());
200     else
201         mState.setTableExceptionProps(pProps);
202 
203 #ifdef DBG_UTIL
204     TagLogger::getInstance().endElement();
205 #endif
206 }
207 
utext(const sal_uInt8 * data,std::size_t len)208 void TableManager::utext(const sal_uInt8* data, std::size_t len)
209 {
210     // optimization: cell/row end characters are the last characters in a run
211 
212     if (len > 0)
213     {
214         sal_Unicode nChar = data[(len - 1) * 2] + (data[(len - 1) * 2 + 1] << 8);
215         if (nChar == 0x7)
216             handle0x7();
217     }
218 }
219 
text(const sal_uInt8 * data,std::size_t len)220 void TableManager::text(const sal_uInt8* data, std::size_t len)
221 {
222     // optimization: cell/row end characters are the last characters in a run
223     if (len > 0 && data[len - 1] == 0x7)
224         handle0x7();
225 }
226 
handle0x7()227 void TableManager::handle0x7()
228 {
229 #ifdef DBG_UTIL
230     TagLogger::getInstance().startElement("tablemanager.handle0x7");
231 #endif
232 
233     if (mnTableDepthNew < 1)
234         mnTableDepthNew = 1;
235 
236     if (isInCell())
237         endCell();
238     else
239         endRow();
240 
241 #ifdef DBG_UTIL
242     TagLogger::getInstance().endElement();
243 #endif
244 }
245 
sprm(Sprm & rSprm)246 bool TableManager::sprm(Sprm& rSprm)
247 {
248     bool bRet = true;
249     switch (rSprm.getId())
250     {
251         case NS_ooxml::LN_tblDepth:
252         {
253             Value::Pointer_t pValue = rSprm.getValue();
254 
255             cellDepth(pValue->getInt());
256         }
257         break;
258         case NS_ooxml::LN_inTbl:
259             inCell();
260             break;
261         case NS_ooxml::LN_tblCell:
262             endCell();
263             break;
264         case NS_ooxml::LN_tblRow:
265             endRow();
266             break;
267         default:
268             bRet = false;
269     }
270     return bRet;
271 }
272 
closeCell(const css::uno::Reference<css::text::XTextRange> & rHandle)273 void TableManager::closeCell(const css::uno::Reference<css::text::XTextRange>& rHandle)
274 {
275 #ifdef DBG_UTIL
276     TagLogger::getInstance().startElement("tablemanager.closeCell");
277     TagLogger::getInstance().chars(XTextRangeToString(rHandle));
278     TagLogger::getInstance().endElement();
279 #endif
280 
281     if (!mTableDataStack.empty())
282     {
283         TableData::Pointer_t pTableData = mTableDataStack.top();
284 
285         pTableData->endCell(rHandle);
286 
287         if (mpTableDataHandler)
288             mpTableDataHandler->getDomainMapperImpl().ClearPreviousParagraph();
289     }
290 }
291 
ensureOpenCell(const TablePropertyMapPtr & pProps)292 void TableManager::ensureOpenCell(const TablePropertyMapPtr& pProps)
293 {
294 #ifdef DBG_UTIL
295     TagLogger::getInstance().startElement("tablemanager.ensureOpenCell");
296 #endif
297 
298     if (!mTableDataStack.empty())
299     {
300         TableData::Pointer_t pTableData = mTableDataStack.top();
301 
302         if (pTableData != nullptr)
303         {
304             if (!pTableData->isCellOpen())
305                 openCell(getHandle(), pProps);
306             else
307                 pTableData->insertCellProperties(pProps);
308         }
309     }
310 #ifdef DBG_UTIL
311     TagLogger::getInstance().endElement();
312 #endif
313 }
314 
endParagraphGroup()315 void TableManager::endParagraphGroup()
316 {
317     sal_Int32 nTableDepthDifference = mnTableDepthNew - mnTableDepth;
318 
319     TablePropertyMapPtr pEmptyProps;
320 
321     while (nTableDepthDifference > 0)
322     {
323         ensureOpenCell(pEmptyProps);
324         startLevel();
325 
326         --nTableDepthDifference;
327     }
328     while (nTableDepthDifference < 0)
329     {
330         endLevel();
331 
332         ++nTableDepthDifference;
333     }
334 
335     mnTableDepth = mnTableDepthNew;
336 
337     if (mnTableDepth <= 0)
338         return;
339 
340     if (isRowEnd())
341     {
342         endOfRowAction();
343         mTableDataStack.top()->endRow(getRowProps());
344         mState.resetRowProps();
345     }
346 
347     else if (isInCell())
348     {
349         ensureOpenCell(getCellProps());
350 
351         if (mState.isCellEnd())
352         {
353             endOfCellAction();
354             closeCell(getHandle());
355         }
356     }
357     mState.resetCellProps();
358 }
359 
startParagraphGroup()360 void TableManager::startParagraphGroup()
361 {
362     mState.resetCellSpecifics();
363     mnTableDepthNew = 0;
364 }
365 
resolveCurrentTable()366 void TableManager::resolveCurrentTable()
367 {
368 #ifdef DBG_UTIL
369     TagLogger::getInstance().startElement("tablemanager.resolveCurrentTable");
370 #endif
371 
372     if (mpTableDataHandler != nullptr)
373     {
374         try
375         {
376             TableData::Pointer_t pTableData = mTableDataStack.top();
377 
378             unsigned int nRows = pTableData->getRowCount();
379 
380             mpTableDataHandler->startTable(getTableProps());
381 
382             for (unsigned int nRow = 0; nRow < nRows; ++nRow)
383             {
384                 RowData::Pointer_t pRowData = pTableData->getRow(nRow);
385 
386                 unsigned int nCells = pRowData->getCellCount();
387 
388                 mpTableDataHandler->startRow(pRowData->getProperties());
389 
390                 for (unsigned int nCell = 0; nCell < nCells; ++nCell)
391                 {
392                     mpTableDataHandler->startCell(pRowData->getCellStart(nCell),
393                                                   pRowData->getCellProperties(nCell));
394 
395                     mpTableDataHandler->endCell(pRowData->getCellEnd(nCell));
396                 }
397 
398                 mpTableDataHandler->endRow();
399             }
400 
401             mpTableDataHandler->endTable(mTableDataStack.size() - 1, m_bTableStartsAtCellStart);
402         }
403         catch (css::uno::Exception const&)
404         {
405             TOOLS_WARN_EXCEPTION("writerfilter", "resolving of current table failed");
406         }
407     }
408     mState.resetTableProps();
409     clearData();
410 
411 #ifdef DBG_UTIL
412     TagLogger::getInstance().endElement();
413 #endif
414 }
415 
endLevel()416 void TableManager::endLevel()
417 {
418     if (mpTableDataHandler != nullptr)
419         resolveCurrentTable();
420 
421     // Store the unfinished row as it will be used for the next table
422     if (mbKeepUnfinishedRow)
423         mpUnfinishedRow = mTableDataStack.top()->getCurrentRow();
424     mState.endLevel();
425     mTableDataStack.pop();
426 
427 #ifdef DBG_UTIL
428     TableData::Pointer_t pTableData;
429 
430     if (!mTableDataStack.empty())
431         pTableData = mTableDataStack.top();
432 
433     TagLogger::getInstance().startElement("tablemanager.endLevel");
434     TagLogger::getInstance().attribute("level", mTableDataStack.size());
435 
436     if (pTableData != nullptr)
437         TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
438 
439     TagLogger::getInstance().endElement();
440 #endif
441 }
442 
startLevel()443 void TableManager::startLevel()
444 {
445 #ifdef DBG_UTIL
446     TableData::Pointer_t pTableData;
447 
448     if (!mTableDataStack.empty())
449         pTableData = mTableDataStack.top();
450 
451     TagLogger::getInstance().startElement("tablemanager.startLevel");
452     TagLogger::getInstance().attribute("level", mTableDataStack.size());
453 
454     if (pTableData != nullptr)
455         TagLogger::getInstance().attribute("openCell", pTableData->isCellOpen() ? "yes" : "no");
456 
457     TagLogger::getInstance().endElement();
458 #endif
459 
460     TableData::Pointer_t pTableData2(new TableData(mTableDataStack.size()));
461 
462     // If we have an unfinished row stored here, then push it to the new TableData
463     if (mpUnfinishedRow)
464     {
465         for (unsigned int i = 0; i < mpUnfinishedRow->getCellCount(); ++i)
466         {
467             pTableData2->addCell(mpUnfinishedRow->getCellStart(i),
468                                  mpUnfinishedRow->getCellProperties(i));
469             pTableData2->endCell(mpUnfinishedRow->getCellEnd(i));
470             pTableData2->getCurrentRow()->setCurrentGridSpan(mpUnfinishedRow->getGridSpan(i));
471         }
472         pTableData2->getCurrentRow()->setGridBefore(mpUnfinishedRow->getGridBefore());
473         pTableData2->getCurrentRow()->setGridAfter(mpUnfinishedRow->getGridAfter());
474         mpUnfinishedRow.clear();
475     }
476 
477     mTableDataStack.push(pTableData2);
478     mState.startLevel();
479 }
480 
isInTable()481 bool TableManager::isInTable()
482 {
483     bool bInTable = false;
484     if (!mTableDataStack.empty())
485         bInTable = mTableDataStack.top()->getDepth() > 0;
486     return bInTable;
487 }
488 
handle(const css::uno::Reference<css::text::XTextRange> & rHandle)489 void TableManager::handle(const css::uno::Reference<css::text::XTextRange>& rHandle)
490 {
491 #ifdef DBG_UTIL
492     TagLogger::getInstance().startElement("tablemanager.handle");
493     TagLogger::getInstance().chars(XTextRangeToString(rHandle));
494     TagLogger::getInstance().endElement();
495 #endif
496 
497     setHandle(rHandle);
498 }
499 
setHandler(const tools::SvRef<DomainMapperTableHandler> & pTableDataHandler)500 void TableManager::setHandler(const tools::SvRef<DomainMapperTableHandler>& pTableDataHandler)
501 {
502     mpTableDataHandler = pTableDataHandler;
503 }
504 
endRow()505 void TableManager::endRow()
506 {
507 #ifdef DBG_UTIL
508     TagLogger::getInstance().element("tablemanager.endRow");
509 #endif
510     TableData::Pointer_t pTableData = mTableDataStack.top();
511 
512     // Add borderless w:gridBefore cell(s) to the row
513     sal_uInt32 nGridBefore = getCurrentGridBefore();
514     if (pTableData && nGridBefore > 0 && pTableData->getCurrentRow()->getCellCount() > 0)
515     {
516         const css::uno::Reference<css::text::XTextRange>& xRowStart
517             = pTableData->getCurrentRow()->getCellStart(0);
518         if (xRowStart.is())
519         {
520             try
521             {
522                 // valid TextRange for table creation (not a nested table)?
523                 xRowStart->getText()->createTextCursorByRange(xRowStart);
524 
525                 for (unsigned int i = 0; i < nGridBefore; ++i)
526                 {
527                     css::table::BorderLine2 aBorderLine;
528                     aBorderLine.Color = 0;
529                     aBorderLine.InnerLineWidth = 0;
530                     aBorderLine.OuterLineWidth = 0;
531                     TablePropertyMapPtr pCellProperties(new TablePropertyMap);
532                     pCellProperties->Insert(PROP_TOP_BORDER, css::uno::makeAny(aBorderLine));
533                     pCellProperties->Insert(PROP_LEFT_BORDER, css::uno::makeAny(aBorderLine));
534                     pCellProperties->Insert(PROP_BOTTOM_BORDER, css::uno::makeAny(aBorderLine));
535                     pCellProperties->Insert(PROP_RIGHT_BORDER, css::uno::makeAny(aBorderLine));
536                     pTableData->getCurrentRow()->addCell(xRowStart, pCellProperties,
537                                                          /*bAddBefore=*/true);
538                 }
539             }
540             catch (css::uno::Exception const&)
541             {
542                 // don't add gridBefore cells in not valid TextRange
543                 setCurrentGridBefore(0);
544                 setCurrentGridSpan(getCurrentGridSpans().front() + nGridBefore,
545                                    /*bFirstCell=*/true);
546             }
547         }
548     }
549 
550     setRowEnd(true);
551 }
552 
endCell()553 void TableManager::endCell()
554 {
555 #ifdef DBG_UTIL
556     TagLogger::getInstance().element("tablemanager.endCell");
557 #endif
558 
559     setCellEnd(true);
560 }
561 
inCell()562 void TableManager::inCell()
563 {
564 #ifdef DBG_UTIL
565     TagLogger::getInstance().element("tablemanager.inCell");
566 #endif
567     setInCell(true);
568 
569     if (mnTableDepthNew < 1)
570         mnTableDepthNew = 1;
571 }
572 
cellDepth(sal_uInt32 nDepth)573 void TableManager::cellDepth(sal_uInt32 nDepth)
574 {
575 #ifdef DBG_UTIL
576     TagLogger::getInstance().startElement("tablemanager.cellDepth");
577     TagLogger::getInstance().attribute("depth", nDepth);
578     TagLogger::getInstance().endElement();
579 #endif
580 
581     mnTableDepthNew = nDepth;
582 }
583 
setTableStartsAtCellStart(bool bTableStartsAtCellStart)584 void TableManager::setTableStartsAtCellStart(bool bTableStartsAtCellStart)
585 {
586     m_bTableStartsAtCellStart = bTableStartsAtCellStart;
587 }
588 
setCellLastParaAfterAutospacing(bool bIsAfterAutospacing)589 void TableManager::setCellLastParaAfterAutospacing(bool bIsAfterAutospacing)
590 {
591     m_bCellLastParaAfterAutospacing = bIsAfterAutospacing;
592 }
593 
TableManager()594 TableManager::TableManager()
595     : mnTableDepthNew(0)
596     , mnTableDepth(0)
597     , mbKeepUnfinishedRow(false)
598     , m_bTableStartsAtCellStart(false)
599 {
600     setRowEnd(false);
601     setInCell(false);
602     setCellEnd(false);
603     m_bCellLastParaAfterAutospacing = false;
604 }
605 
606 TableManager::~TableManager() = default;
607 }
608 
609 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
610