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