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