summaryrefslogtreecommitdiffstats
path: root/writerfilter/source/dmapper/DomainMapperTableManager.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'writerfilter/source/dmapper/DomainMapperTableManager.cxx')
-rw-r--r--writerfilter/source/dmapper/DomainMapperTableManager.cxx859
1 files changed, 859 insertions, 0 deletions
diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.cxx b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
new file mode 100644
index 000000000..004f34971
--- /dev/null
+++ b/writerfilter/source/dmapper/DomainMapperTableManager.cxx
@@ -0,0 +1,859 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <optional>
+#include "DomainMapperTableManager.hxx"
+#include "ConversionHelper.hxx"
+#include "MeasureHandler.hxx"
+#include "TagLogger.hxx"
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <o3tl/numeric.hxx>
+#include <o3tl/safeint.hxx>
+#include <ooxml/resourceids.hxx>
+#include <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <numeric>
+#include "TrackChangesHandler.hxx"
+#include <oox/token/tokens.hxx>
+
+namespace writerfilter::dmapper {
+
+using namespace ::com::sun::star;
+using namespace ::std;
+
+DomainMapperTableManager::DomainMapperTableManager() :
+ m_nRow(0),
+ m_nGridSpan(1),
+ m_nHeaderRepeat(0),
+ m_nTableWidth(0),
+ m_bIsInShape(false),
+ m_bPushCurrentWidth(false),
+ m_bTableSizeTypeInserted(false),
+ m_nLayoutType(0),
+ m_pTablePropsHandler(new TablePropertiesHandler())
+{
+ m_pTablePropsHandler->SetTableManager( this );
+}
+
+
+DomainMapperTableManager::~DomainMapperTableManager()
+{
+}
+
+bool DomainMapperTableManager::attribute(Id nName, Value const & rValue)
+{
+ bool bRet = true;
+
+ switch (nName)
+ {
+ case NS_ooxml::LN_CT_TblLook_val:
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_TBL_LOOK, uno::Any(sal_Int32(rValue.getInt())));
+ insertTableProps(pPropMap);
+ m_aTableLook["val"] <<= static_cast<sal_Int32>(rValue.getInt());
+ }
+ break;
+ case NS_ooxml::LN_CT_TblLook_noVBand:
+ m_aTableLook["noVBand"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_noHBand:
+ m_aTableLook["noHBand"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_lastColumn:
+ m_aTableLook["lastColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_lastRow:
+ m_aTableLook["lastRow"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_firstColumn:
+ m_aTableLook["firstColumn"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ case NS_ooxml::LN_CT_TblLook_firstRow:
+ m_aTableLook["firstRow"] <<= static_cast<sal_Int32>(rValue.getInt());
+ break;
+ default:
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+void DomainMapperTableManager::finishTableLook()
+{
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(META_PROP_TABLE_LOOK, uno::Any(m_aTableLook.getAsConstPropertyValueList()));
+ m_aTableLook.clear();
+ insertTableProps(pPropMap);
+}
+
+bool DomainMapperTableManager::sprm(Sprm & rSprm)
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.sprm");
+ string sSprm = rSprm.toString();
+ TagLogger::getInstance().chars(sSprm);
+ TagLogger::getInstance().endElement();
+#endif
+ bool bRet = TableManager::sprm(rSprm);
+ if( !bRet )
+ {
+ bRet = m_pTablePropsHandler->sprm( rSprm );
+ }
+
+ if ( !bRet )
+ {
+ bRet = true;
+ sal_uInt32 nSprmId = rSprm.getId();
+ Value::Pointer_t pValue = rSprm.getValue();
+ sal_Int32 nIntValue = (pValue ? pValue->getInt() : 0);
+ switch ( nSprmId )
+ {
+ case NS_ooxml::LN_CT_TblPrBase_tblW:
+ case NS_ooxml::LN_CT_TblPrBase_tblInd:
+ {
+ //contains unit and value
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler( new MeasureHandler );
+ pProperties->resolve(*pMeasureHandler);
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ if (nSprmId == sal_uInt32(NS_ooxml::LN_CT_TblPrBase_tblInd))
+ {
+ pPropMap->setValue( TablePropertyMap::LEFT_MARGIN, pMeasureHandler->getMeasureValue() );
+ }
+ else
+ {
+ m_nTableWidth = pMeasureHandler->getMeasureValue();
+ if( m_nTableWidth )
+ {
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_pct )
+ {
+ sal_Int32 nPercent = pMeasureHandler->getValue() / 50;
+ if(nPercent > 100)
+ nPercent = 100;
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, nPercent );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if( sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto )
+ {
+ /*
+ This attribute specifies the width type of table. This is used as part of the table layout
+ algorithm specified by the tblLayout element.(See 17.4.64 and 17.4.65 of the ISO/IEC 29500-1:2011.)
+ If this value is 'auto', the table layout has to use the preferred widths on the table items to generate
+ the final sizing of the table, but then must use the contents of each cell to determine final column widths.
+ (See 17.18.87 of the ISO/IEC 29500-1:2011.)
+ */
+ IntVectorPtr pCellWidths = getCurrentCellWidths();
+ // Check whether all cells have fixed widths in the given row of table.
+ bool bFixed = std::find(pCellWidths->begin(), pCellWidths->end(), -1) == pCellWidths->end();
+ if (!bFixed)
+ {
+ // Set the width type of table with 'Auto' and set the width value to 0 (as per grid values)
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, 0 );
+ m_bTableSizeTypeInserted = true;
+ }
+ else if (getTableDepth() > 1)
+ {
+ // tdf#131819 limiting the fix for nested tables temporarily
+ // TODO revert the fix for tdf#104876 and reopen it
+ m_bTableSizeTypeInserted = true;
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ pPropMap->dumpXml();
+#endif
+ insertTableProps(pPropMap);
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_tblHeader:
+ // if nIntValue == 1 then the row is a repeated header line
+ // to prevent later rows from increasing the repeating m_nHeaderRepeat is set to NULL when repeating stops
+ if( nIntValue > 0 && m_nHeaderRepeat == static_cast<int>(m_nRow) )
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+
+ // FIXME: DOCX tables with more than 10 repeating header lines imported
+ // without repeating header lines to mimic an MSO workaround for its usability bug.
+ // Explanation: it's very hard to set and modify repeating header rows in Word,
+ // often resulting tables with a special workaround: setting all table rows as
+ // repeating header, because exceeding the pages by "unlimited" header rows turns off the
+ // table headers automatically in MSO. 10-row limit is a reasonable temporary limit
+ // to handle DOCX tables with "unlimited" repeating header, till the same "turn off
+ // exceeding header" feature is ready (see tdf#88496).
+#define HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND 10
+ if ( m_nHeaderRepeat == HEADER_ROW_LIMIT_FOR_MSO_WORKAROUND )
+ {
+ m_nHeaderRepeat = -1;
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
+ }
+ else
+ {
+ ++m_nHeaderRepeat;
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any( m_nHeaderRepeat ));
+ }
+ insertTableProps(pPropMap);
+ }
+ else
+ {
+ if ( nIntValue == 0 && m_nRow == 0 )
+ {
+ // explicit tblHeader=0 in the first row must overwrite table style
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_HEADER_ROW_COUNT, uno::Any(sal_Int32(0)));
+ insertTableProps(pPropMap);
+ }
+ m_nHeaderRepeat = -1;
+ }
+ if (nIntValue)
+ {
+ // Store the info that this is a header, we'll need that when we apply table styles.
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TBL_HEADER, uno::Any(nIntValue));
+ insertRowProps(pPropMap);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblStyle: //table style name
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( META_PROP_TABLE_STYLE_NAME, uno::Any( pValue->getString() ));
+ insertTableProps(pPropMap);
+ }
+ break;
+ case NS_ooxml::LN_CT_TblGridBase_gridCol:
+ {
+ if (nIntValue == -1)
+ getCurrentGrid()->clear();
+ else
+ getCurrentGrid()->push_back( nIntValue );
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_vMerge : //vertical merge
+ {
+ // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
+ TablePropertyMapPtr pMergeProps( new TablePropertyMap );
+ pMergeProps->Insert( PROP_VERTICAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ) );
+ cellProps( pMergeProps);
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_hMerge:
+ {
+ // values can be: LN_Value_ST_Merge_restart, LN_Value_ST_Merge_continue, in reality the second one is a 0
+ TablePropertyMapPtr pMergeProps(new TablePropertyMap());
+ pMergeProps->Insert(PROP_HORIZONTAL_MERGE, uno::Any( sal::static_int_cast<Id>(nIntValue) == NS_ooxml::LN_Value_ST_Merge_restart ));
+ cellProps(pMergeProps);
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_gridSpan: //number of grid positions spanned by this cell
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tablemanager.GridSpan");
+ TagLogger::getInstance().attribute("gridSpan", nIntValue);
+ TagLogger::getInstance().endElement();
+#endif
+ m_nGridSpan = nIntValue;
+ }
+ break;
+ case NS_ooxml::LN_CT_TcPrBase_textDirection:
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ bool bInsertCellProps = true;
+ switch ( nIntValue )
+ {
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRl:
+ // Binary filter takes BiDirection into account ( but I have no idea about that here )
+ // or even what it is. But... here's where to handle it if it becomes an issue
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
+ SAL_INFO( "writerfilter", "Have inserted textDirection " << nIntValue );
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_btLr:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::BT_LR ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTbV:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::LR_TB ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_tbRlV:
+ pPropMap->Insert( PROP_FRM_DIRECTION, uno::Any( text::WritingMode2::TB_RL ));
+ break;
+ case NS_ooxml::LN_Value_ST_TextDirection_lrTb:
+ case NS_ooxml::LN_Value_ST_TextDirection_tbLrV:
+ default:
+ // Ignore - we can't handle these
+ bInsertCellProps = false;
+ break;
+ }
+ if ( bInsertCellProps )
+ cellProps( pPropMap );
+ break;
+ }
+ case NS_ooxml::LN_CT_TcPrBase_tcW:
+ {
+ // Contains unit and value, but unit is not interesting for
+ // us, later we'll just distribute these values in a
+ // 0..10000 scale.
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ if( pProperties )
+ {
+ MeasureHandlerPtr pMeasureHandler(new MeasureHandler());
+ pProperties->resolve(*pMeasureHandler);
+ if (sal::static_int_cast<Id>(pMeasureHandler->getUnit()) == NS_ooxml::LN_Value_ST_TblWidth_auto)
+ getCurrentCellWidths()->push_back(sal_Int32(-1));
+ else
+ // store the original value to limit rounding mistakes, if it's there in a recognized measure (twip)
+ getCurrentCellWidths()->push_back(pMeasureHandler->getMeasureValue() ? pMeasureHandler->getValue() : sal_Int32(0));
+ if (getTableDepthDifference())
+ m_bPushCurrentWidth = true;
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblpPr:
+ {
+ writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
+ // Ignore <w:tblpPr> in shape text, those tables should be always non-floating ones.
+ if (!m_bIsInShape && pProperties)
+ {
+ TablePositionHandlerPtr pHandler = m_aTmpPosition.back();
+ if ( !pHandler )
+ {
+ m_aTmpPosition.pop_back();
+ pHandler = new TablePositionHandler;
+ m_aTmpPosition.push_back( pHandler );
+ }
+ pProperties->resolve(*m_aTmpPosition.back());
+ }
+ }
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_gridBefore:
+ setCurrentGridBefore( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_gridAfter:
+ setCurrentGridAfter( nIntValue );
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCaption:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblDescription:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TrPrBase_tblCellSpacing:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_tblCellSpacing:
+ // To-Do: Not yet preserved
+ break;
+ case NS_ooxml::LN_CT_TblPrBase_bidiVisual:
+ {
+ TablePropertyMapPtr pPropMap(new TablePropertyMap());
+ pPropMap->Insert(PROP_WRITING_MODE, uno::Any(sal_Int16(nIntValue ? text::WritingMode2::RL_TB : text::WritingMode2::LR_TB)));
+ insertTableProps(pPropMap);
+ break;
+ }
+ default:
+ bRet = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("unhandled");
+#endif
+ }
+ }
+ return bRet;
+}
+
+DomainMapperTableManager::IntVectorPtr const & DomainMapperTableManager::getCurrentGrid( )
+{
+ if (m_aTableGrid.empty())
+ throw std::out_of_range("no current grid");
+ return m_aTableGrid.back( );
+}
+
+DomainMapperTableManager::IntVectorPtr const & DomainMapperTableManager::getCurrentCellWidths( )
+{
+ return m_aCellWidths.back( );
+}
+
+uno::Sequence<beans::PropertyValue> DomainMapperTableManager::getCurrentTablePosition( )
+{
+ if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
+ return m_aTablePositions.back( )->getTablePosition();
+ else
+ return uno::Sequence< beans::PropertyValue >();
+}
+
+TablePositionHandler* DomainMapperTableManager::getCurrentTableRealPosition()
+{
+ if ( !m_aTablePositions.empty( ) && m_aTablePositions.back() )
+ return m_aTablePositions.back().get();
+ else
+ return nullptr;
+}
+
+const TableParagraphVectorPtr & DomainMapperTableManager::getCurrentParagraphs( )
+{
+ return m_aParagraphsToEndTable.top( );
+}
+
+void DomainMapperTableManager::setIsInShape(bool bIsInShape)
+{
+ m_bIsInShape = bIsInShape;
+}
+
+void DomainMapperTableManager::startLevel( )
+{
+ TableManager::startLevel( );
+
+ // If requested, pop the value that was pushed too early.
+ std::optional<sal_Int32> oCurrentWidth;
+ if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ {
+ oCurrentWidth = m_aCellWidths.back()->back();
+ m_aCellWidths.back()->pop_back();
+ }
+ std::optional<TableParagraph> oParagraph;
+ if (getTableDepthDifference() > 0 && !m_aParagraphsToEndTable.empty() && !m_aParagraphsToEndTable.top()->empty())
+ {
+ oParagraph = m_aParagraphsToEndTable.top()->back();
+ m_aParagraphsToEndTable.top()->pop_back();
+ }
+
+ IntVectorPtr pNewGrid = std::make_shared<vector<sal_Int32>>();
+ IntVectorPtr pNewCellWidths = std::make_shared<vector<sal_Int32>>();
+ TablePositionHandlerPtr pNewPositionHandler;
+ m_aTableGrid.push_back( pNewGrid );
+ m_aCellWidths.push_back( pNewCellWidths );
+ m_aTablePositions.push_back( pNewPositionHandler );
+ // empty name will be replaced by the table style name, if it exists
+ m_aTableStyleNames.push_back( OUString() );
+ m_aMoved.push_back( OUString() );
+
+ TablePositionHandlerPtr pTmpPosition;
+ TablePropertyMapPtr pTmpProperties( new TablePropertyMap( ) );
+ m_aTmpPosition.push_back( pTmpPosition );
+ m_aTmpTableProperties.push_back( pTmpProperties );
+ m_nCell.push_back( 0 );
+ m_nTableWidth = 0;
+ m_nLayoutType = 0;
+ TableParagraphVectorPtr pNewParagraphs = std::make_shared<vector<TableParagraph>>();
+ m_aParagraphsToEndTable.push( pNewParagraphs );
+
+ // And push it back to the right level.
+ if (oCurrentWidth)
+ m_aCellWidths.back()->push_back(*oCurrentWidth);
+ if (oParagraph)
+ m_aParagraphsToEndTable.top()->push_back(*oParagraph);
+}
+
+void DomainMapperTableManager::endLevel( )
+{
+ if (m_aTableGrid.empty())
+ {
+ SAL_WARN("writerfilter.dmapper", "Table stack is empty");
+ return;
+ }
+
+ m_aTableGrid.pop_back( );
+
+ // Do the same trick as in startLevel(): pop the value that was pushed too early.
+ std::optional<sal_Int32> oCurrentWidth;
+ if (m_bPushCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ oCurrentWidth = m_aCellWidths.back()->back();
+ m_aCellWidths.pop_back( );
+ // And push it back to the right level.
+ if (oCurrentWidth && !m_aCellWidths.empty() && !m_aCellWidths.back()->empty())
+ m_aCellWidths.back()->push_back(*oCurrentWidth);
+
+ m_nCell.pop_back( );
+ m_nTableWidth = 0;
+ m_nLayoutType = 0;
+
+ m_aTmpPosition.pop_back( );
+ m_aTmpTableProperties.pop_back( );
+
+ TableManager::endLevel( );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("dmappertablemanager.endLevel");
+ PropertyMapPtr pProps = getTableProps().get();
+ if (pProps)
+ getTableProps()->dumpXml();
+
+ TagLogger::getInstance().endElement();
+#endif
+
+ // Pop back the table position after endLevel as it's used
+ // in the endTable method called in endLevel.
+ m_aTablePositions.pop_back();
+ m_aTableStyleNames.pop_back();
+ m_aMoved.pop_back( );
+
+ std::optional<TableParagraph> oParagraph;
+ if (getTableDepthDifference() < 0 && !m_aParagraphsToEndTable.top()->empty())
+ oParagraph = m_aParagraphsToEndTable.top()->back();
+ m_aParagraphsToEndTable.pop();
+ if (oParagraph && m_aParagraphsToEndTable.size())
+ m_aParagraphsToEndTable.top()->push_back(*oParagraph);
+}
+
+void DomainMapperTableManager::endOfCellAction()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().element("endOFCellAction");
+#endif
+
+ if ( !isInTable() )
+ throw std::out_of_range("cell without a table");
+ if ( m_nGridSpan > 1 )
+ setCurrentGridSpan( m_nGridSpan );
+ m_nGridSpan = 1;
+ ++m_nCell.back( );
+}
+
+bool DomainMapperTableManager::shouldInsertRow(IntVectorPtr pCellWidths, IntVectorPtr pTableGrid, size_t nGrids, bool& rIsIncompleteGrid)
+{
+ if (pCellWidths->empty())
+ return false;
+ if (m_nLayoutType == NS_ooxml::LN_Value_doc_ST_TblLayout_fixed)
+ return true;
+ if (pCellWidths->size() == nGrids)
+ return true;
+ rIsIncompleteGrid = true;
+ return nGrids > pTableGrid->size();
+}
+
+void DomainMapperTableManager::endOfRowAction()
+{
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("endOfRowAction");
+#endif
+
+ // Compare the table position and style with the previous ones. We may need to split
+ // into two tables if those are different. We surely don't want to do anything
+ // if we don't have any row yet.
+ if (m_aTmpPosition.empty())
+ throw std::out_of_range("row without a position");
+ TablePositionHandlerPtr pTmpPosition = m_aTmpPosition.back();
+ TablePropertyMapPtr pTablePropMap = m_aTmpTableProperties.back( );
+ TablePositionHandlerPtr pCurrentPosition = m_aTablePositions.back();
+ bool bSamePosition = ( pTmpPosition == pCurrentPosition ) ||
+ ( pTmpPosition && pCurrentPosition && *pTmpPosition == *pCurrentPosition );
+ bool bIsSetTableStyle = pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME);
+ OUString sTableStyleName;
+ bool bSameTableStyle = ( !bIsSetTableStyle && m_aTableStyleNames.back().isEmpty()) ||
+ ( bIsSetTableStyle &&
+ (pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName) &&
+ sTableStyleName == m_aTableStyleNames.back() );
+ if ( (!bSamePosition || !bSameTableStyle) && m_nRow > 0 )
+ {
+ // Save the grid infos to have them survive the end/start level
+ IntVectorPtr pTmpTableGrid = m_aTableGrid.back();
+ IntVectorPtr pTmpCellWidths = m_aCellWidths.back();
+ sal_uInt32 nTmpCell = m_nCell.back();
+ TableParagraphVectorPtr pTableParagraphs = getCurrentParagraphs();
+
+ // endLevel and startLevel are taking care of the non finished row
+ // to carry it over to the next table
+ setKeepUnfinishedRow( true );
+ endLevel();
+ setKeepUnfinishedRow( false );
+ startLevel();
+
+ m_aTableGrid.pop_back();
+ m_aCellWidths.pop_back();
+ m_nCell.pop_back();
+ m_aTableGrid.push_back(pTmpTableGrid);
+ m_aCellWidths.push_back(pTmpCellWidths);
+ m_nCell.push_back(nTmpCell);
+ m_aParagraphsToEndTable.pop( );
+ m_aParagraphsToEndTable.push( pTableParagraphs );
+ }
+ // save table style in the first row for comparison
+ if ( m_nRow == 0 && pTablePropMap->isSet(META_PROP_TABLE_STYLE_NAME) )
+ {
+ pTablePropMap->getProperty(META_PROP_TABLE_STYLE_NAME)->second >>= sTableStyleName;
+ m_aTableStyleNames.pop_back();
+ m_aTableStyleNames.push_back( sTableStyleName );
+ }
+
+ // Push the tmp position now that we compared it
+ m_aTablePositions.pop_back();
+ m_aTablePositions.push_back( pTmpPosition );
+ m_aTmpPosition.back().clear( );
+
+
+ IntVectorPtr pTableGrid = getCurrentGrid( );
+ IntVectorPtr pCellWidths = getCurrentCellWidths( );
+ if(!m_nTableWidth && !pTableGrid->empty())
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("tableWidth");
+#endif
+
+ for( const auto& rCell : *pTableGrid )
+ {
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("col");
+ TagLogger::getInstance().attribute("width", rCell);
+ TagLogger::getInstance().endElement();
+#endif
+
+ m_nTableWidth = o3tl::saturating_add(m_nTableWidth, rCell);
+ }
+ if (m_nTableWidth)
+ // convert sum of grid twip values to 1/100 mm with rounding up to avoid table width loss
+ m_nTableWidth = static_cast<sal_Int32>(ceil(ConversionHelper::convertTwipToMM100Double(m_nTableWidth)));
+
+ if (m_nTableWidth > 0 && !m_bTableSizeTypeInserted)
+ {
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->setValue( TablePropertyMap::TABLE_WIDTH, m_nTableWidth );
+ insertTableProps(pPropMap);
+ }
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+ }
+
+ std::vector<sal_uInt32> rCurrentSpans = getCurrentGridSpans();
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("gridSpans");
+ {
+ for (const auto& rGridSpan : rCurrentSpans)
+ {
+ TagLogger::getInstance().startElement("gridSpan");
+ TagLogger::getInstance().attribute("span", rGridSpan);
+ TagLogger::getInstance().endElement();
+ }
+ }
+ TagLogger::getInstance().endElement();
+#endif
+
+ //calculate number of used grids - it has to match the size of m_aTableGrid
+ size_t nGrids = std::accumulate(rCurrentSpans.begin(), rCurrentSpans.end(), sal::static_int_cast<size_t>(0));
+
+ // sj: the grid is having no units... it is containing only relative values.
+ // a table with a grid of "1:2:1" looks identical as if the table is having
+ // a grid of "20:40:20" and it doesn't have to do something with the tableWidth
+ // -> so we have get the sum of each grid entry for the fullWidthRelative:
+ int nFullWidthRelative = 0;
+ for (int i : (*pTableGrid))
+ nFullWidthRelative = o3tl::saturating_add(nFullWidthRelative, i);
+
+ bool bIsIncompleteGrid = false;
+ if( pTableGrid->size() == nGrids && m_nCell.back( ) > 0 )
+ {
+ /*
+ * If table width property set earlier is smaller than the current table width,
+ * then replace the TABLE_WIDTH property, set earlier.
+ */
+ sal_Int32 nTableWidth(0);
+ sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
+ if ((nTableWidthType == text::SizeType::FIX) && (nTableWidth < m_nTableWidth))
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
+ }
+ if (nTableWidthType == text::SizeType::VARIABLE )
+ {
+ if(nTableWidth > 100 || nTableWidth <= 0)
+ {
+ if(getTableDepth() > 1 && !m_bTableSizeTypeInserted)
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, sal_Int32(100));
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::VARIABLE);
+ }
+ else
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, m_nTableWidth);
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH_TYPE, text::SizeType::FIX);
+ }
+ }
+ }
+ uno::Sequence< text::TableColumnSeparator > aSeparators( getCurrentGridBefore() + m_nCell.back() - 1 );
+ text::TableColumnSeparator* pSeparators = aSeparators.getArray();
+ double nLastRelPos = 0.0;
+ sal_uInt32 nBorderGridIndex = 0;
+
+ size_t nWidthsBound = getCurrentGridBefore() + m_nCell.back() - 1;
+ if (nWidthsBound)
+ {
+ ::std::vector< sal_uInt32 >::const_iterator aSpansIter = rCurrentSpans.begin();
+ for( size_t nBorder = 0; nBorder < nWidthsBound; ++nBorder )
+ {
+ double nRelPos, fGridWidth = 0.;
+ for ( sal_Int32 nGridCount = *aSpansIter; nGridCount > 0; --nGridCount )
+ fGridWidth += (*pTableGrid)[nBorderGridIndex++];
+
+ if (fGridWidth == 0.)
+ {
+ // allow nFullWidthRelative here, with a sane 0.0 result
+ nRelPos = 0.;
+ }
+ else
+ {
+ if (nFullWidthRelative == 0)
+ throw o3tl::divide_by_zero();
+
+ nRelPos = (fGridWidth * 10000) / nFullWidthRelative;
+ }
+
+ pSeparators[nBorder].Position = rtl::math::round(nRelPos + nLastRelPos);
+ pSeparators[nBorder].IsVisible = true;
+ nLastRelPos = nLastRelPos + nRelPos;
+ ++aSpansIter;
+ }
+ }
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("rowProperties");
+ pPropMap->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+
+ // set row insertion/deletion at tracked drag & drop of tables
+ OUString aMoved = getMoved();
+ if ( !aMoved.isEmpty() )
+ {
+ auto pTrackChangesHandler = std::make_shared<TrackChangesHandler>(
+ aMoved == getPropertyName( PROP_TABLE_ROW_DELETE )
+ ? oox::XML_tableRowDelete
+ : oox::XML_tableRowInsert );
+ uno::Sequence<beans::PropertyValue> aTableRedlineProperties = pTrackChangesHandler->getRedlineProperties();
+ pPropMap->Insert( PROP_TABLE_REDLINE_PARAMS , uno::Any( aTableRedlineProperties ));
+ }
+
+ insertRowProps(pPropMap);
+ }
+ else if (shouldInsertRow(pCellWidths, pTableGrid, nGrids, bIsIncompleteGrid))
+ {
+ // If we're here, then the number of cells does not equal to the amount
+ // defined by the grid, even after taking care of
+ // gridSpan/gridBefore/gridAfter. Handle this by ignoring the grid and
+ // providing the separators based on the provided cell widths, as long
+ // as we have a fixed layout;
+ // On the other hand even if the layout is not fixed, but the cell widths
+ // provided equal the total number of cells, and there are no after/before cells
+ // then use the cell widths to calculate the column separators.
+ // Also handle autofit tables with incomplete grids, when rows can have
+ // different widths and last cells can be wider, than their values.
+ uno::Sequence< text::TableColumnSeparator > aSeparators(pCellWidths->size() - 1);
+ text::TableColumnSeparator* pSeparators = aSeparators.getArray();
+ sal_Int16 nSum = 0;
+ sal_uInt32 nPos = 0;
+
+ if (bIsIncompleteGrid)
+ nFullWidthRelative = 0;
+
+ // Avoid divide by zero (if there's no grid, position using cell widths).
+ if( nFullWidthRelative == 0 )
+ for (size_t i = 0; i < pCellWidths->size(); ++i)
+ nFullWidthRelative += (*pCellWidths)[i];
+
+ if (bIsIncompleteGrid)
+ {
+ /*
+ * If table width property set earlier is smaller than the current table row width,
+ * then replace the TABLE_WIDTH property, set earlier.
+ */
+ sal_Int32 nFullWidth = static_cast<sal_Int32>(ceil(ConversionHelper::convertTwipToMM100Double(nFullWidthRelative)));
+ sal_Int32 nTableWidth(0);
+ sal_Int32 nTableWidthType(text::SizeType::VARIABLE);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
+ pTablePropMap->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
+ if (nTableWidth < nFullWidth)
+ {
+ pTablePropMap->setValue(TablePropertyMap::TABLE_WIDTH, nFullWidth);
+ }
+ }
+
+ size_t nWidthsBound = pCellWidths->size() - 1;
+ if (nWidthsBound)
+ {
+ // At incomplete table grids, last cell width can be smaller, than its final width.
+ // Correct it based on the last but one column width and their span values.
+ if ( bIsIncompleteGrid && rCurrentSpans.size()-1 == nWidthsBound )
+ {
+ auto aSpansIter = std::next(rCurrentSpans.begin(), nWidthsBound - 1);
+ sal_Int32 nFixLastCellWidth = (*pCellWidths)[nWidthsBound-1] / *aSpansIter * *std::next(aSpansIter);
+ if (nFixLastCellWidth > (*pCellWidths)[nWidthsBound])
+ nFullWidthRelative += nFixLastCellWidth - (*pCellWidths)[nWidthsBound];
+ }
+
+ // tdf#131203 handle missing w:tblGrid
+ if (nFullWidthRelative > 0)
+ {
+ for (size_t i = 0; i < nWidthsBound; ++i)
+ {
+ nSum += (*pCellWidths)[i];
+ pSeparators[nPos].Position = (nSum * 10000) / nFullWidthRelative; // Relative position
+ pSeparators[nPos].IsVisible = true;
+ nPos++;
+ }
+ }
+ }
+
+ TablePropertyMapPtr pPropMap( new TablePropertyMap );
+ pPropMap->Insert( PROP_TABLE_COLUMN_SEPARATORS, uno::Any( aSeparators ) );
+#ifdef DBG_UTIL
+ TagLogger::getInstance().startElement("rowProperties");
+ pPropMap->dumpXml();
+ TagLogger::getInstance().endElement();
+#endif
+ insertRowProps(pPropMap);
+ }
+
+ // Now that potentially opened table is closed, save the table properties
+ TableManager::insertTableProps(pTablePropMap);
+
+ m_aTmpTableProperties.pop_back();
+ TablePropertyMapPtr pEmptyTableProps( new TablePropertyMap() );
+ m_aTmpTableProperties.push_back( pEmptyTableProps );
+
+ ++m_nRow;
+ m_nCell.back( ) = 0;
+ getCurrentGrid()->clear();
+ pCellWidths->clear();
+
+ m_bTableSizeTypeInserted = false;
+
+#ifdef DBG_UTIL
+ TagLogger::getInstance().endElement();
+#endif
+}
+
+void DomainMapperTableManager::clearData()
+{
+ m_nRow = m_nHeaderRepeat = m_nTableWidth = m_nLayoutType = 0;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */