LibreOffice Module writerfilter (master) 1
DomainMapperTableManager.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#include <optional>
21#include "ConversionHelper.hxx"
22#include "MeasureHandler.hxx"
23#include "TagLogger.hxx"
24#include <com/sun/star/text/SizeType.hpp>
25#include <com/sun/star/text/TableColumnSeparator.hpp>
26#include <com/sun/star/text/WritingMode2.hpp>
27#include <o3tl/numeric.hxx>
28#include <o3tl/safeint.hxx>
29#include <ooxml/resourceids.hxx>
30#include <rtl/math.hxx>
31#include <sal/log.hxx>
32#include <numeric>
34#include <oox/token/tokens.hxx>
35
36namespace writerfilter::dmapper {
37
38using namespace ::com::sun::star;
39
41 m_nRow(0),
42 m_nGridSpan(1),
43 m_nHeaderRepeat(0),
44 m_nTableWidth(0),
45 m_bIsInShape(false),
46 m_bPushCurrentWidth(false),
47 m_bTableSizeTypeInserted(false),
48 m_nLayoutType(0),
49 m_pTablePropsHandler(new TablePropertiesHandler())
50{
51 m_pTablePropsHandler->SetTableManager( this );
52}
53
54
56{
57}
58
59bool DomainMapperTableManager::attribute(Id nName, Value const & rValue)
60{
61 bool bRet = true;
62
63 switch (nName)
64 {
65 case NS_ooxml::LN_CT_TblLook_val:
66 {
68 pPropMap->Insert(PROP_TBL_LOOK, uno::Any(sal_Int32(rValue.getInt())));
69 insertTableProps(pPropMap);
70 m_aTableLook["val"] <<= static_cast<sal_Int32>(rValue.getInt());
71 }
72 break;
73 case NS_ooxml::LN_CT_TblLook_noVBand:
74 m_aTableLook["noVBand"] <<= static_cast<sal_Int32>(rValue.getInt());
75 break;
76 case NS_ooxml::LN_CT_TblLook_noHBand:
77 m_aTableLook["noHBand"] <<= static_cast<sal_Int32>(rValue.getInt());
78 break;
79 case NS_ooxml::LN_CT_TblLook_lastColumn:
80 m_aTableLook["lastColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
81 break;
82 case NS_ooxml::LN_CT_TblLook_lastRow:
83 m_aTableLook["lastRow"] <<= static_cast<sal_Int32>(rValue.getInt());
84 break;
85 case NS_ooxml::LN_CT_TblLook_firstColumn:
86 m_aTableLook["firstColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
87 break;
88 case NS_ooxml::LN_CT_TblLook_firstRow:
89 m_aTableLook["firstRow"] <<= static_cast<sal_Int32>(rValue.getInt());
90 break;
91 default:
92 bRet = false;
93 }
94
95 return bRet;
96}
97
99{
103 insertTableProps(pPropMap);
104}
105
107{
108#ifdef DBG_UTIL
109 TagLogger::getInstance().startElement("tablemanager.sprm");
110 std::string sSprm = rSprm.toString();
113#endif
114 bool bRet = TableManager::sprm(rSprm);
115 if( !bRet )
116 {
117 bRet = m_pTablePropsHandler->sprm( rSprm );
118 }
119
120 if ( !bRet )
121 {
122 bRet = true;
123 sal_uInt32 nSprmId = rSprm.getId();
124 Value::Pointer_t pValue = rSprm.getValue();
125 sal_Int32 nIntValue = (pValue ? pValue->getInt() : 0);
126 switch ( nSprmId )
127 {
128 case NS_ooxml::LN_CT_TblPrBase_tblW:
129 case NS_ooxml::LN_CT_TblPrBase_tblInd:
130 {
131 //contains unit and value
133 if( pProperties )
134 {
135 MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
136 pProperties->resolve(*pMeasureHandler);
138 if (nSprmId == sal_uInt32(NS_ooxml::LN_CT_TblPrBase_tblInd))
139 {
140 pPropMap->setValue( TablePropertyMap::LEFT_MARGIN, pMeasureHandler->getMeasureValue() );
141 }
142 else
143 {
144 m_nTableWidth = pMeasureHandler->getMeasureValue();
145 if( m_nTableWidth )
146 {
147 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX );
148 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
150 }
151 else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_pct )
152 {
153 sal_Int32 nPercent = pMeasureHandler->getValue() / 50;
154 if(nPercent > 100)
155 nPercent = 100;
156 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
157 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, nPercent );
159 }
160 else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto )
161 {
162 /*
163 This attribute specifies the width type of table. This is used as part of the table layout
164 algorithm specified by the tblLayout element.(See 17.4.64 and 17.4.65 of the ISO/IEC 29500-1:2011.)
165 If this value is 'auto', the table layout has to use the preferred widths on the table items to generate
166 the final sizing of the table, but then must use the contents of each cell to determine final column widths.
167 (See 17.18.87 of the ISO/IEC 29500-1:2011.)
168 */
169 IntVectorPtr pCellWidths = getCurrentCellWidths();
170 // Check whether all cells have fixed widths in the given row of table.
171 bool bFixed = std::find(pCellWidths->begin(), pCellWidths->end(), -1) == pCellWidths->end();
172 if (!bFixed)
173 {
174 // Set the width type of table with 'Auto' and set the width value to 0 (as per grid values)
175 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
176 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, 0 );
178 }
179 else if (getTableDepth() > 1)
180 {
181 // tdf#131819 limiting the fix for nested tables temporarily
182 // TODO revert the fix for tdf#104876 and reopen it
184 }
185 }
186 }
187#ifdef DBG_UTIL
188 pPropMap->dumpXml();
189#endif
190 insertTableProps(pPropMap);
191 }
192 }
193 break;
194 case NS_ooxml::LN_CT_TrPrBase_tblHeader:
195 // if nIntValue == 1 then the row is a repeated header line
196 // to prevent later rows from increasing the repeating m_nHeaderRepeat is set to NULL when repeating stops
197 if( nIntValue > 0 && m_nHeaderRepeat == static_cast<int>(m_nRow) )
198 {
200
201 // FIXME: DOCX tables with more than 10 repeating header lines imported
202 // without repeating header lines to mimic an MSO workaround for its usability bug.
203 // Explanation: it's very hard to set and modify repeating header rows in Word,
204 // often resulting tables with a special workaround: setting all table rows as
205 // repeating header, because exceeding the pages by "unlimited" header rows turns off the
206 // table headers automatically in MSO. 10-row limit is a reasonable temporary limit
207 // to handle DOCX tables with "unlimited" repeating header, till the same "turn off
208 // exceeding header" feature is ready (see tdf#88496).
209#define HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND 10
211 {
212 m_nHeaderRepeat = -1;
213 pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
214 }
215 else
216 {
218 pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any( m_nHeaderRepeat ));
219 }
220 insertTableProps(pPropMap);
221 }
222 else
223 {
224 if ( nIntValue == 0 && m_nRow == 0 )
225 {
226 // explicit tblHeader=0 in the first row must overwrite table style
228 pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
229 insertTableProps(pPropMap);
230 }
231 m_nHeaderRepeat = -1;
232 }
233 if (nIntValue)
234 {
235 // Store the info that this is a header, we'll need that when we apply table styles.
237 pPropMap->Insert( PROP_TBL_HEADER, uno::Any(nIntValue));
238 insertRowProps(pPropMap);
239 }
240 break;
241 case NS_ooxml::LN_CT_TblPrBase_tblStyle: //table style name
242 {
244 pPropMap->Insert( META_PROP_TABLE_STYLE_NAME, uno::Any( pValue->getString() ));
245 insertTableProps(pPropMap);
246 }
247 break;
248 case NS_ooxml::LN_CT_TblGridBase_gridCol:
249 {
250 if (nIntValue == -1)
251 getCurrentGrid()->clear();
252 else
253 getCurrentGrid()->push_back( nIntValue );
254 }
255 break;
256 case NS_ooxml::LN_CT_TcPrBase_vMerge : //vertical merge
257 {
258 // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
259 TablePropertyMapPtr pMergeProps( new TablePropertyMap );
260 pMergeProps->Insert( PROP_VERTICAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ) );
261 cellProps( pMergeProps);
262 }
263 break;
264 case NS_ooxml::LN_CT_TcPrBase_hMerge:
265 {
266 // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
267 TablePropertyMapPtr pMergeProps(new TablePropertyMap());
268 pMergeProps->Insert(PROP_HORIZONTAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ));
269 cellProps(pMergeProps);
270 }
271 break;
272 case NS_ooxml::LN_CT_TcPrBase_gridSpan: //number of grid positions spanned by this cell
273 {
274#ifdef DBG_UTIL
275 TagLogger::getInstance().startElement("tablemanager.GridSpan");
276 TagLogger::getInstance().attribute("gridSpan", nIntValue);
278#endif
279 m_nGridSpan = nIntValue;
280 }
281 break;
282 case NS_ooxml::LN_CT_TcPrBase_textDirection:
283 {
285 bool bInsertCellProps = true;
286 switch ( nIntValue )
287 {
288 case NS_ooxml::LN_Value_ST_TextDirection_tbRl:
289 // Binary filter takes BiDirection into account ( but I have no idea about that here )
290 // or even what it is. But... here's where to handle it if it becomes an issue
291 pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
292 SAL_INFO( "writerfilter", "Have inserted textDirection " << nIntValue );
293 break;
294 case NS_ooxml::LN_Value_ST_TextDirection_btLr:
295 pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::BT_LR ));
296 break;
297 case NS_ooxml::LN_Value_ST_TextDirection_lrTbV:
298 pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::LR_TB ));
299 break;
300 case NS_ooxml::LN_Value_ST_TextDirection_tbRlV:
301 pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
302 break;
303 case NS_ooxml::LN_Value_ST_TextDirection_lrTb:
304 case NS_ooxml::LN_Value_ST_TextDirection_tbLrV:
305 default:
306 // Ignore - we can't handle these
307 bInsertCellProps = false;
308 break;
309 }
310 if ( bInsertCellProps )
311 cellProps( pPropMap );
312 break;
313 }
314 case NS_ooxml::LN_CT_TcPrBase_tcW:
315 {
316 // Contains unit and value, but unit is not interesting for
317 // us, later we'll just distribute these values in a
318 // 0..10000 scale.
320 if( pProperties )
321 {
322 MeasureHandlerPtr pMeasureHandler(new MeasureHandler());
323 pProperties->resolve(*pMeasureHandler);
324 if (sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto)
325 getCurrentCellWidths()->push_back(sal_Int32(-1));
326 else
327 // store the original value to limit rounding mistakes, if it's there in a recognized measure (twip)
328 getCurrentCellWidths()->push_back(pMeasureHandler->getMeasureValue() ? pMeasureHandler->getValue() : sal_Int32(0));
330 m_bPushCurrentWidth = true;
331 }
332 }
333 break;
334 case NS_ooxml::LN_CT_TblPrBase_tblpPr:
335 {
337 // Ignore <w:tblpPr> in shape text, those tables should be always non-floating ones.
338 if (!m_bIsInShape && pProperties)
339 {
340 TablePositionHandlerPtr pHandler = m_aTmpPosition.back();
341 if ( !pHandler )
342 {
343 m_aTmpPosition.pop_back();
344 pHandler = new TablePositionHandler;
345 m_aTmpPosition.push_back( pHandler );
346 }
347 pProperties->resolve(*m_aTmpPosition.back());
348 }
349 }
350 break;
351 case NS_ooxml::LN_CT_TrPrBase_gridBefore:
352 setCurrentGridBefore( nIntValue );
353 break;
354 case NS_ooxml::LN_CT_TrPrBase_gridAfter:
355 setCurrentGridAfter( nIntValue );
356 break;
357 case NS_ooxml::LN_CT_TblPrBase_tblCaption:
358 // To-Do: Not yet preserved
359 break;
360 case NS_ooxml::LN_CT_TblPrBase_tblDescription:
361 // To-Do: Not yet preserved
362 break;
363 case NS_ooxml::LN_CT_TrPrBase_tblCellSpacing:
364 // To-Do: Not yet preserved
365 break;
366 case NS_ooxml::LN_CT_TblPrBase_tblCellSpacing:
367 // To-Do: Not yet preserved
368 break;
369 case NS_ooxml::LN_CT_TblPrBase_bidiVisual:
370 {
372 pPropMap->Insert(PROP_WRITING_MODE, uno::Any(sal_Int16(nIntValue ? text::WritingMode2::RL_TB : text::WritingMode2::LR_TB)));
373 insertTableProps(pPropMap);
374 break;
375 }
376 default:
377 bRet = false;
378
379#ifdef DBG_UTIL
380 TagLogger::getInstance().element("unhandled");
381#endif
382 }
383 }
384 return bRet;
385}
386
388{
389 if (m_aTableGrid.empty())
390 throw std::out_of_range("no current grid");
391 return m_aTableGrid.back( );
392}
393
395{
396 return m_aCellWidths.back( );
397}
398
400{
401 if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
402 return m_aTablePositions.back( )->getTablePosition();
403 else
405}
406
408{
409 if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
410 return m_aTablePositions.back().get();
411 else
412 return nullptr;
413}
414
416{
417 return m_aParagraphsToEndTable.top( );
418}
419
421{
422 m_bIsInShape = bIsInShape;
423}
424
426{
428
429 // If requested, pop the value that was pushed too early.
430 std::optional<sal_Int32> oCurrentWidth;
431 if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
432 {
433 oCurrentWidth = m_aCellWidths.back()->back();
434 m_aCellWidths.back()->pop_back();
435 }
436 std::optional<TableParagraph> oParagraph;
437 if (getTableDepthDifference() > 0 && !m_aParagraphsToEndTable.empty() && !m_aParagraphsToEndTable.top()->empty())
438 {
439 oParagraph = m_aParagraphsToEndTable.top()->back();
440 m_aParagraphsToEndTable.top()->pop_back();
441 }
442
443 IntVectorPtr pNewGrid = std::make_shared<std::vector<sal_Int32>>();
444 IntVectorPtr pNewCellWidths = std::make_shared<std::vector<sal_Int32>>();
445 TablePositionHandlerPtr pNewPositionHandler;
446 m_aTableGrid.push_back( pNewGrid );
447 m_aCellWidths.push_back( pNewCellWidths );
448 m_aTablePositions.push_back( pNewPositionHandler );
449 // empty name will be replaced by the table style name, if it exists
450 m_aTableStyleNames.push_back( OUString() );
451 m_aMoved.push_back( OUString() );
452
453 TablePositionHandlerPtr pTmpPosition;
454 TablePropertyMapPtr pTmpProperties( new TablePropertyMap( ) );
455 m_aTmpPosition.push_back( pTmpPosition );
456 m_aTmpTableProperties.push_back( pTmpProperties );
457 m_nCell.push_back( 0 );
458 m_nTableWidth = 0;
459 m_nLayoutType = 0;
460 TableParagraphVectorPtr pNewParagraphs = std::make_shared<std::vector<TableParagraph>>();
461 m_aParagraphsToEndTable.push( pNewParagraphs );
462
463 // And push it back to the right level.
464 if (oCurrentWidth)
465 m_aCellWidths.back()->push_back(*oCurrentWidth);
466 if (oParagraph)
467 m_aParagraphsToEndTable.top()->push_back(*oParagraph);
468}
469
471{
472 if (m_aTableGrid.empty())
473 {
474 SAL_WARN("writerfilter.dmapper", "Table stack is empty");
475 return;
476 }
477
478 m_aTableGrid.pop_back( );
479
480 // Do the same trick as in startLevel(): pop the value that was pushed too early.
481 std::optional<sal_Int32> oCurrentWidth;
482 if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
483 oCurrentWidth = m_aCellWidths.back()->back();
484 m_aCellWidths.pop_back( );
485 // And push it back to the right level.
486 if (oCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
487 m_aCellWidths.back()->push_back(*oCurrentWidth);
488
489 m_nCell.pop_back( );
490 m_nTableWidth = 0;
491 m_nLayoutType = 0;
492
493 m_aTmpPosition.pop_back( );
494 m_aTmpTableProperties.pop_back( );
495
497#ifdef DBG_UTIL
498 TagLogger::getInstance().startElement("dmappertablemanager.endLevel");
499 PropertyMapPtr pProps = getTableProps().get();
500 if (pProps)
501 getTableProps()->dumpXml();
502
504#endif
505
506 // Pop back the table position after endLevel as it's used
507 // in the endTable method called in endLevel.
508 m_aTablePositions.pop_back();
509 m_aTableStyleNames.pop_back();
510 m_aMoved.pop_back( );
511
512 std::optional<TableParagraph> oParagraph;
513 if (getTableDepthDifference() < 0 && !m_aParagraphsToEndTable.top()->empty())
514 oParagraph = m_aParagraphsToEndTable.top()->back();
516 if (oParagraph && m_aParagraphsToEndTable.size())
517 m_aParagraphsToEndTable.top()->push_back(*oParagraph);
518}
519
521{
522#ifdef DBG_UTIL
523 TagLogger::getInstance().element("endOFCellAction");
524#endif
525
526 if ( !isInTable() )
527 throw std::out_of_range("cell without a table");
528 if ( m_nGridSpan > 1 )
530 m_nGridSpan = 1;
531 ++m_nCell.back( );
532}
533
534bool DomainMapperTableManager::shouldInsertRow(IntVectorPtr pCellWidths, IntVectorPtr pTableGrid, size_t nGrids, bool& rIsIncompleteGrid)
535{
536 if (pCellWidths->empty())
537 return false;
538 if (m_nLayoutType == NS_ooxml::LN_Value_doc_ST_TblLayout_fixed)
539 return true;
540 if (pCellWidths->size() == nGrids)
541 return true;
542 rIsIncompleteGrid = true;
543 return nGrids > pTableGrid->size();
544}
545
547{
548#ifdef DBG_UTIL
549 TagLogger::getInstance().startElement("endOfRowAction");
550#endif
551
552 // Compare the table position and style with the previous ones. We may need to split
553 // into two tables if those are different. We surely don't want to do anything
554 // if we don't have any row yet.
555 if (m_aTmpPosition.empty())
556 throw std::out_of_range("row without a position");
557 TablePositionHandlerPtr pTmpPosition = m_aTmpPosition.back();
558 TablePropertyMapPtr pTablePropMap = m_aTmpTableProperties.back( );
559 TablePositionHandlerPtr pCurrentPosition = m_aTablePositions.back();
560 bool bSamePosition = ( pTmpPosition == pCurrentPosition ) ||
561 ( pTmpPosition && pCurrentPosition && *pTmpPosition == *pCurrentPosition );
562 bool bIsSetTableStyle = pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME);
563 OUString sTableStyleName;
564 bool bSameTableStyle = ( !bIsSetTableStyle && m_aTableStyleNames.back().isEmpty()) ||
565 ( bIsSetTableStyle &&
566 (pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName) &&
567 sTableStyleName == m_aTableStyleNames.back() );
568 if ( (!bSamePosition || !bSameTableStyle) && m_nRow > 0 )
569 {
570 // Save the grid infos to have them survive the end/start level
571 IntVectorPtr pTmpTableGrid = m_aTableGrid.back();
572 IntVectorPtr pTmpCellWidths = m_aCellWidths.back();
573 sal_uInt32 nTmpCell = m_nCell.back();
575
576 // endLevel and startLevel are taking care of the non finished row
577 // to carry it over to the next table
578 setKeepUnfinishedRow( true );
579 endLevel();
580 setKeepUnfinishedRow( false );
581 startLevel();
582
583 m_aTableGrid.pop_back();
584 m_aCellWidths.pop_back();
585 m_nCell.pop_back();
586 m_aTableGrid.push_back(pTmpTableGrid);
587 m_aCellWidths.push_back(pTmpCellWidths);
588 m_nCell.push_back(nTmpCell);
590 m_aParagraphsToEndTable.push( pTableParagraphs );
591 }
592 // save table style in the first row for comparison
593 if ( m_nRow == 0 && pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME) )
594 {
595 pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName;
596 m_aTableStyleNames.pop_back();
597 m_aTableStyleNames.push_back( sTableStyleName );
598 }
599
600 // Push the tmp position now that we compared it
601 m_aTablePositions.pop_back();
602 m_aTablePositions.push_back( pTmpPosition );
603 m_aTmpPosition.back().clear( );
604
605
606 IntVectorPtr pTableGrid = getCurrentGrid( );
607 IntVectorPtr pCellWidths = getCurrentCellWidths( );
608 if(!m_nTableWidth && !pTableGrid->empty())
609 {
610#ifdef DBG_UTIL
611 TagLogger::getInstance().startElement("tableWidth");
612#endif
613
614 for( const auto& rCell : *pTableGrid )
615 {
616#ifdef DBG_UTIL
618 TagLogger::getInstance().attribute("width", rCell);
620#endif
621
623 }
624 if (m_nTableWidth)
625 // convert sum of grid twip values to 1/100 mm with rounding up to avoid table width loss
627
629 {
631 pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
632 insertTableProps(pPropMap);
633 }
634
635#ifdef DBG_UTIL
637#endif
638 }
639
640 std::vector<sal_uInt32> rCurrentSpans = getCurrentGridSpans();
641
642#ifdef DBG_UTIL
644 {
645 for (const auto& rGridSpan : rCurrentSpans)
646 {
648 TagLogger::getInstance().attribute("span", rGridSpan);
650 }
651 }
653#endif
654
655 //calculate number of used grids - it has to match the size of m_aTableGrid
656 size_t nGrids = std::accumulate(rCurrentSpans.begin(), rCurrentSpans.end(), sal::static_int_cast<size_t>(0));
657
658 // sj: the grid is having no units... it is containing only relative values.
659 // a table with a grid of "1:2:1" looks identical as if the table is having
660 // a grid of "20:40:20" and it doesn't have to do something with the tableWidth
661 // -> so we have get the sum of each grid entry for the fullWidthRelative:
662 int nFullWidthRelative = 0;
663 for (int i : (*pTableGrid))
664 nFullWidthRelative = o3tl::saturating_add(nFullWidthRelative, i);
665
666 bool bIsIncompleteGrid = false;
667 if( pTableGrid->size() == nGrids && m_nCell.back( ) > 0 )
668 {
669 /*
670 * If table width property set earlier is smaller than the current table width,
671 * then replace the TABLE_WIDTH property, set earlier.
672 */
673 sal_Int32 nTableWidth(0);
674 sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
675 pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
676 pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
677 if ((nTableWidthType == text::SizeType::FIX) && (nTableWidth < m_nTableWidth))
678 {
679 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
680 }
681 if (nTableWidthType == text::SizeType::VARIABLE )
682 {
683 if(nTableWidth > 100 || nTableWidth <= 0)
684 {
686 {
687 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, sal_Int32(100));
688 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE);
689 }
690 else
691 {
692 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
693 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX);
694 }
695 }
696 }
698 text::TableColumnSeparator* pSeparators = aSeparators.getArray();
699 double nLastRelPos = 0.0;
700 sal_uInt32 nBorderGridIndex = 0;
701
702 size_t nWidthsBound = getCurrentGridBefore() + m_nCell.back() - 1;
703 if (nWidthsBound)
704 {
705 ::std::vector< sal_uInt32 >::const_iterator aSpansIter = rCurrentSpans.begin();
706 for( size_t nBorder = 0; nBorder < nWidthsBound; ++nBorder )
707 {
708 double nRelPos, fGridWidth = 0.;
709 for ( sal_Int32 nGridCount = *aSpansIter; nGridCount > 0; --nGridCount )
710 fGridWidth += (*pTableGrid)[nBorderGridIndex++];
711
712 if (fGridWidth == 0.)
713 {
714 // allow nFullWidthRelative here, with a sane 0.0 result
715 nRelPos = 0.;
716 }
717 else
718 {
719 if (nFullWidthRelative == 0)
720 throw o3tl::divide_by_zero();
721
722 nRelPos = (fGridWidth * 10000) / nFullWidthRelative;
723 }
724
725 pSeparators[nBorder].Position = rtl::math::round(nRelPos + nLastRelPos);
726 pSeparators[nBorder].IsVisible = true;
727 nLastRelPos = nLastRelPos + nRelPos;
728 ++aSpansIter;
729 }
730 }
732 pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
733
734#ifdef DBG_UTIL
735 TagLogger::getInstance().startElement("rowProperties");
736 pPropMap->dumpXml();
738#endif
739
740 // set row insertion/deletion at tracked drag & drop of tables
741 OUString aMoved = getMoved();
742 if ( !aMoved.isEmpty() )
743 {
744 auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>(
746 ? oox::XML_tableRowDelete
747 : oox::XML_tableRowInsert );
748 uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
749 pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::Any( aTableRedlineProperties ));
750 }
751
752 insertRowProps(pPropMap);
753 }
754 else if (shouldInsertRow(pCellWidths, pTableGrid, nGrids, bIsIncompleteGrid))
755 {
756 // If we're here, then the number of cells does not equal to the amount
757 // defined by the grid, even after taking care of
758 // gridSpan/gridBefore/gridAfter. Handle this by ignoring the grid and
759 // providing the separators based on the provided cell widths, as long
760 // as we have a fixed layout;
761 // On the other hand even if the layout is not fixed, but the cell widths
762 // provided equal the total number of cells, and there are no after/before cells
763 // then use the cell widths to calculate the column separators.
764 // Also handle autofit tables with incomplete grids, when rows can have
765 // different widths and last cells can be wider, than their values.
766 uno::Sequence< text::TableColumnSeparator > aSeparators(pCellWidths->size() - 1);
767 text::TableColumnSeparator* pSeparators = aSeparators.getArray();
768 sal_Int16 nSum = 0;
769 sal_uInt32 nPos = 0;
770
771 if (bIsIncompleteGrid)
772 nFullWidthRelative = 0;
773
774 // Avoid divide by zero (if there's no grid, position using cell widths).
775 if( nFullWidthRelative == 0 )
776 for (size_t i = 0; i < pCellWidths->size(); ++i)
777 nFullWidthRelative += (*pCellWidths)[i];
778
779 if (bIsIncompleteGrid)
780 {
781 /*
782 * If table width property set earlier is smaller than the current table row width,
783 * then replace the TABLE_WIDTH property, set earlier.
784 */
785 sal_Int32 nFullWidth = static_cast<sal_Int32>(ceil(ConversionHelper::convertTwipToMM100Double(nFullWidthRelative)));
786 sal_Int32 nTableWidth(0);
787 sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
788 pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
789 pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
790 if (nTableWidth < nFullWidth)
791 {
792 pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, nFullWidth);
793 }
794 }
795
796 size_t nWidthsBound = pCellWidths->size() - 1;
797 if (nWidthsBound)
798 {
799 // At incomplete table grids, last cell width can be smaller, than its final width.
800 // Correct it based on the last but one column width and their span values.
801 if ( bIsIncompleteGrid && rCurrentSpans.size()-1 == nWidthsBound )
802 {
803 auto aSpansIter = std::next(rCurrentSpans.begin(), nWidthsBound - 1);
804 sal_Int32 nFixLastCellWidth = (*pCellWidths)[nWidthsBound-1] / *aSpansIter * *std::next(aSpansIter);
805 if (nFixLastCellWidth > (*pCellWidths)[nWidthsBound])
806 nFullWidthRelative += nFixLastCellWidth - (*pCellWidths)[nWidthsBound];
807 }
808
809 // tdf#131203 handle missing w:tblGrid
810 if (nFullWidthRelative > 0)
811 {
812 for (size_t i = 0; i < nWidthsBound; ++i)
813 {
814 nSum += (*pCellWidths)[i];
815 pSeparators[nPos].Position = (nSum * 10000) / nFullWidthRelative; // Relative position
816 pSeparators[nPos].IsVisible = true;
817 nPos++;
818 }
819 }
820 }
821
823 pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
824#ifdef DBG_UTIL
825 TagLogger::getInstance().startElement("rowProperties");
826 pPropMap->dumpXml();
828#endif
829 insertRowProps(pPropMap);
830 }
831
832 // Now that potentially opened table is closed, save the table properties
833 TableManager::insertTableProps(pTablePropMap);
834
835 m_aTmpTableProperties.pop_back();
836 TablePropertyMapPtr pEmptyTableProps( new TablePropertyMap() );
837 m_aTmpTableProperties.push_back( pEmptyTableProps );
838
839 ++m_nRow;
840 m_nCell.back( ) = 0;
841 getCurrentGrid()->clear();
842 pCellWidths->clear();
843
845
846#ifdef DBG_UTIL
848#endif
849}
850
852{
854}
855
856}
857
858/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND
css::uno::Sequence< css::beans::PropertyValue > getAsConstPropertyValueList() const
T * get() const
An SPRM: Section, Paragraph and Run Modifier.
virtual sal_uInt32 getId() const =0
Returns id of the SPRM.
virtual writerfilter::Reference< Properties >::Pointer_t getProps()=0
Returns reference to properties contained in the SPRM.
virtual Value::Pointer_t getValue()=0
Returns value of the SPRM.
virtual std::string toString() const =0
Returns string representation of 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
virtual int getInt() const =0
Returns integer representation of the value.
virtual void insertRowProps(const TablePropertyMapPtr &pProps) override
Handle properties of the current row.
bool m_bTableSizeTypeInserted
Remember if table width was already set, when we lack a w:tblW, it should be set manually at the end.
std::shared_ptr< std::vector< sal_Int32 > > IntVectorPtr
void finishTableLook()
Turn the attributes collected so far in m_aTableLook into a property and clear the container.
std::stack< TableParagraphVectorPtr > m_aParagraphsToEndTable
Collected table paragraphs for table style handling.
virtual void endOfCellAction() override
Action to be carried out at the end of the last paragraph of a cell.
std::vector< TablePositionHandlerPtr > m_aTablePositions
bool shouldInsertRow(IntVectorPtr pCellWidths, IntVectorPtr pTableGrid, size_t nGrids, bool &rIsIncompleteGrid)
css::uno::Sequence< css::beans::PropertyValue > getCurrentTablePosition()
std::vector< OUString > m_aMoved
Moved table (in moveRangeFromStart...moveRangeFromEnd or moveRangeToStart...moveRangeToEnd)
::std::vector< IntVectorPtr > m_aCellWidths
Individual table cell width values, used only in case the number of cells doesn't match the table gri...
virtual void startLevel() override
Start a new table level.
std::vector< TablePositionHandlerPtr > m_aTmpPosition
Temporarily stores the position to compare it later.
virtual void endOfRowAction() override
Action to be carried out at the end of the "table row" paragraph.
std::unique_ptr< TablePropertiesHandler > m_pTablePropsHandler
std::vector< TablePropertyMapPtr > m_aTmpTableProperties
Temporarily stores the table properties until end of row.
virtual bool sprm(Sprm &rSprm) override
Handle an SPRM at current handle.
sal_uInt32 m_nLayoutType
Table layout algorithm, IOW if we should consider fixed column width or not.
virtual void cellProps(const TablePropertyMapPtr &pProps) override
Handle properties of the current cell.
bool m_bIsInShape
Are we in a shape (text append stack is not empty) or in the body document?
bool m_bPushCurrentWidth
If this is true, then we pushed a width before the next level started, and that should be carried ove...
comphelper::SequenceAsHashMap m_aTableLook
Grab-bag of table look attributes for preserving.
virtual void endLevel() override
End a table level.
virtual void clearData() override
let the derived class clear their table related data
virtual void insertTableProps(const TablePropertyMapPtr &pProps) override
Handle properties of the current table.
Handler for sprms that contain a measure and a unit.
virtual bool sprm(Sprm &rSprm)
Handle an SPRM at current handle.
sal_Int32 getTableDepthDifference() const
Return the current table difference, i.e.
TablePropertyMapPtr getTableProps()
bool isInTable()
Tells whether a table has been started or not.
void setCurrentGridBefore(sal_uInt32 nSkipGrids)
virtual void startLevel()
Start a new table level.
virtual void insertTableProps(const TablePropertyMapPtr &pProps)
Handle properties of the current table.
virtual void endLevel()
End a table level.
void setCurrentGridAfter(sal_uInt32 nSkipGrids)
void setKeepUnfinishedRow(bool bKeep)
Should we keep the unfinished row in endLevel to initialize the table data in the following startLeve...
void setCurrentGridSpan(sal_uInt32 nGridSpan, bool bFirstCell=false)
std::vector< sal_uInt32 > getCurrentGridSpans()
Handler for floating table positioning.
sal_uInt16 nPos
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
tools::Long const nBorder
int i
constexpr T saturating_add(T a, T b)
OUString getPropertyName(PropertyIds eId)
std::shared_ptr< std::vector< TableParagraph > > TableParagraphVectorPtr
sal_uInt32 Id