lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / dmapper / DomainMapperTableHandler.cxx
blobcf36a7abd1c7d9f181bcc0723d1cc68cc878f44d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include "DomainMapperTableHandler.hxx"
20 #include "DomainMapper_Impl.hxx"
21 #include "StyleSheetTable.hxx"
22 #include <com/sun/star/beans/XPropertyState.hpp>
23 #include <com/sun/star/container/XEnumerationAccess.hpp>
24 #include <com/sun/star/table/TableBorderDistances.hpp>
25 #include <com/sun/star/table/TableBorder.hpp>
26 #include <com/sun/star/table/BorderLine2.hpp>
27 #include <com/sun/star/table/BorderLineStyle.hpp>
28 #include <com/sun/star/table/XCellRange.hpp>
29 #include <com/sun/star/text/HoriOrientation.hpp>
30 #include <com/sun/star/text/RelOrientation.hpp>
31 #include <com/sun/star/text/SizeType.hpp>
32 #include <com/sun/star/text/XTextRangeCompare.hpp>
33 #include "TablePositionHandler.hxx"
34 #include "ConversionHelper.hxx"
35 #include "util.hxx"
36 #include <osl/diagnose.h>
37 #include <sal/log.hxx>
38 #include <comphelper/sequence.hxx>
40 #ifdef DEBUG_WRITERFILTER
41 #include "PropertyMapHelper.hxx"
42 #include <rtl/ustring.hxx>
43 #endif
45 namespace writerfilter {
46 namespace dmapper {
48 using namespace ::com::sun::star;
49 using namespace ::std;
51 #define DEF_BORDER_DIST 190 //0,19cm
53 DomainMapperTableHandler::DomainMapperTableHandler(
54 css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
55 DomainMapper_Impl& rDMapper_Impl)
56 : m_xText(xText),
57 m_rDMapper_Impl( rDMapper_Impl ),
58 m_bHadFootOrEndnote(false)
62 DomainMapperTableHandler::~DomainMapperTableHandler()
66 void DomainMapperTableHandler::startTable(const TablePropertyMapPtr& pProps)
68 m_aTableProperties = pProps;
69 m_aTableRanges.clear();
71 #ifdef DEBUG_WRITERFILTER
72 TagLogger::getInstance().startElement("tablehandler.table");
74 if (pProps.get() != nullptr)
75 pProps->dumpXml();
76 #endif
79 static void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest )
81 boost::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId);
83 if ( pOrigVal )
85 pDest->Insert( nId, pOrigVal->second, false );
89 static void lcl_computeCellBorders( const PropertyMapPtr& pTableBorders, const PropertyMapPtr& pCellProps,
90 sal_Int32 nCell, sal_Int32 nRow, bool bIsEndCol, bool bIsEndRow )
92 boost::optional<PropertyMap::Property> pVerticalVal = pCellProps->getProperty(META_PROP_VERTICAL_BORDER);
93 boost::optional<PropertyMap::Property> pHorizontalVal = pCellProps->getProperty(META_PROP_HORIZONTAL_BORDER);
95 // Handle the vertical and horizontal borders
96 uno::Any aVertProp;
97 if ( !pVerticalVal)
99 pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER);
100 if ( pVerticalVal )
101 aVertProp = pVerticalVal->second;
103 else
105 aVertProp = pVerticalVal->second;
106 pCellProps->Erase( pVerticalVal->first );
109 uno::Any aHorizProp;
110 if ( !pHorizontalVal )
112 pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER);
113 if ( pHorizontalVal )
114 aHorizProp = pHorizontalVal->second;
116 else
118 aHorizProp = pHorizontalVal->second;
119 pCellProps->Erase( pHorizontalVal->first );
122 if ( nCell == 0 )
124 lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps );
125 if ( pVerticalVal )
126 pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
129 if ( bIsEndCol )
131 lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps );
132 if ( pVerticalVal )
133 pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
136 if ( nCell > 0 && !bIsEndCol )
138 if ( pVerticalVal )
140 pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
141 pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
145 if ( nRow == 0 )
147 lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps );
148 if ( pHorizontalVal )
149 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
152 if ( bIsEndRow )
154 lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
155 if ( pHorizontalVal )
156 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
159 if ( nRow > 0 && !bIsEndRow )
161 if ( pHorizontalVal )
163 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
164 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
169 #ifdef DEBUG_WRITERFILTER
171 static void lcl_debug_BorderLine(table::BorderLine const & rLine)
173 TagLogger::getInstance().startElement("BorderLine");
174 TagLogger::getInstance().attribute("Color", rLine.Color);
175 TagLogger::getInstance().attribute("InnerLineWidth", rLine.InnerLineWidth);
176 TagLogger::getInstance().attribute("OuterLineWidth", rLine.OuterLineWidth);
177 TagLogger::getInstance().attribute("LineDistance", rLine.LineDistance);
178 TagLogger::getInstance().endElement();
181 static void lcl_debug_TableBorder(table::TableBorder const & rBorder)
183 TagLogger::getInstance().startElement("TableBorder");
184 lcl_debug_BorderLine(rBorder.TopLine);
185 TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder.IsTopLineValid));
186 lcl_debug_BorderLine(rBorder.BottomLine);
187 TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder.IsBottomLineValid));
188 lcl_debug_BorderLine(rBorder.LeftLine);
189 TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder.IsLeftLineValid));
190 lcl_debug_BorderLine(rBorder.RightLine);
191 TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder.IsRightLineValid));
192 lcl_debug_BorderLine(rBorder.VerticalLine);
193 TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder.IsVerticalLineValid));
194 lcl_debug_BorderLine(rBorder.HorizontalLine);
195 TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder.IsHorizontalLineValid));
196 TagLogger::getInstance().attribute("Distance", rBorder.Distance);
197 TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder.IsDistanceValid));
198 TagLogger::getInstance().endElement();
200 #endif
202 struct TableInfo
204 sal_Int32 nLeftBorderDistance;
205 sal_Int32 nRightBorderDistance;
206 sal_Int32 nTopBorderDistance;
207 sal_Int32 nBottomBorderDistance;
208 sal_Int32 nTblLook;
209 sal_Int32 nNestLevel;
210 PropertyMapPtr pTableDefaults;
211 PropertyMapPtr pTableBorders;
212 TableStyleSheetEntry* pTableStyle;
213 css::beans::PropertyValues aTableProperties;
215 TableInfo()
216 : nLeftBorderDistance(DEF_BORDER_DIST)
217 , nRightBorderDistance(DEF_BORDER_DIST)
218 , nTopBorderDistance(0)
219 , nBottomBorderDistance(0)
220 , nTblLook(0x4a0)
221 , nNestLevel(0)
222 , pTableDefaults(new PropertyMap)
223 , pTableBorders(new PropertyMap)
224 , pTableStyle(nullptr)
230 namespace
233 bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine)
235 if (!pTableProperties)
236 return false;
238 const boost::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId);
239 if( aTblBorder )
241 OSL_VERIFY(aTblBorder->second >>= rLine);
243 rInfo.pTableBorders->Insert( nId, uno::makeAny( rLine ) );
244 rInfo.pTableDefaults->Erase( nId );
246 return true;
249 return false;
252 void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient)
254 // Shifts the frame left by the given value.
255 for (beans::PropertyValue & rFrameProperty : rFrameProperties)
257 if (rFrameProperty.Name == "HoriOrient")
259 sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>();
260 if (nValue != text::HoriOrientation::NONE)
261 nHoriOrient = nValue;
262 return;
267 void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount)
269 // Shifts the frame left by the given value.
270 for (beans::PropertyValue & rPropertyValue : rFrameProperties)
272 if (rPropertyValue.Name == "HoriOrientPosition")
274 sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>();
275 nValue -= nAmount;
276 rPropertyValue.Value <<= nValue;
277 return;
282 void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftBorder,
283 const table::BorderLine2& rRightBorder)
285 // MS Word appears to do these things to adjust the cell horizontal area:
287 // bll = left borderline width
288 // blr = right borderline width
289 // cea = cell's edit area rectangle
290 // cea_w = cea width
291 // cml = cell's left margin (padding) defined in cell settings
292 // cmr = cell's right margin (padding) defined in cell settings
293 // cw = cell width (distance between middles of left borderline and right borderline)
294 // pad_l = actual cea left padding = (its left pos relative to middle of bll)
295 // pad_r = actual cea right padding = abs (its right pos relative to middle of blr)
297 // pad_l = max(bll/2, cml) -> cea does not overlap left borderline
298 // cea_w = cw-max(pad_l+blr/2, cml+cmr) -> cea does not overlap right borderline
299 // pad_r = max(pad_l+blr/2, cml+cmr) - pad_l
301 // It means that e.g. for border widths of 6 pt (~2.12 mm), left margin 0 mm, and right margin
302 // 2 mm, actual left and right margins will (unexpectedly) coincide with inner edges of cell's
303 // borderlines - the right margin won't create spacing between right of edit rectangle and the
304 // inner edge of right borderline.
306 const sal_Int32 nActualL
307 = std::max<sal_Int32>(rLeftBorder.LineWidth / 2, rInfo.nLeftBorderDistance);
308 const sal_Int32 nActualR
309 = std::max<sal_Int32>(nActualL + rRightBorder.LineWidth / 2,
310 rInfo.nLeftBorderDistance + rInfo.nRightBorderDistance)
311 - nActualL;
312 rInfo.nLeftBorderDistance = nActualL;
313 rInfo.nRightBorderDistance = nActualR;
318 TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
320 // will receive the table style if any
321 TableStyleSheetEntry* pTableStyle = nullptr;
323 if( m_aTableProperties.get() )
325 //create properties from the table attributes
326 //...pPropMap->Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin - m_nGapHalf ));
327 //pPropMap->Insert( PROP_HORI_ORIENT, uno::makeAny( text::HoriOrientation::RIGHT ));
328 sal_Int32 nGapHalf = 0;
329 sal_Int32 nLeftMargin = 0;
331 comphelper::SequenceAsHashMap aGrabBag;
333 if (nullptr != m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition())
335 TablePositionHandler *pTablePositions = m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition();
337 uno::Sequence< beans::PropertyValue > aGrabBagTS( 10 );
339 aGrabBagTS[0].Name = "bottomFromText";
340 aGrabBagTS[0].Value <<= pTablePositions->getBottomFromText();
342 aGrabBagTS[1].Name = "horzAnchor";
343 aGrabBagTS[1].Value <<= pTablePositions->getHorzAnchor();
345 aGrabBagTS[2].Name = "leftFromText";
346 aGrabBagTS[2].Value <<= pTablePositions->getLeftFromText();
348 aGrabBagTS[3].Name = "rightFromText";
349 aGrabBagTS[3].Value <<= pTablePositions->getRightFromText();
351 aGrabBagTS[4].Name = "tblpX";
352 aGrabBagTS[4].Value <<= pTablePositions->getX();
354 aGrabBagTS[5].Name = "tblpXSpec";
355 aGrabBagTS[5].Value <<= pTablePositions->getXSpec();
357 aGrabBagTS[6].Name = "tblpY";
358 aGrabBagTS[6].Value <<= pTablePositions->getY();
360 aGrabBagTS[7].Name = "tblpYSpec";
361 aGrabBagTS[7].Value <<= pTablePositions->getYSpec();
363 aGrabBagTS[8].Name = "topFromText";
364 aGrabBagTS[8].Value <<= pTablePositions->getTopFromText();
366 aGrabBagTS[9].Name = "vertAnchor";
367 aGrabBagTS[9].Value <<= pTablePositions->getVertAnchor();
369 aGrabBag["TablePosition"] <<= aGrabBagTS;
372 boost::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
373 if(aTableStyleVal)
375 // Apply table style properties recursively
376 OUString sTableStyleName;
377 aTableStyleVal->second >>= sTableStyleName;
378 StyleSheetTablePtr pStyleSheetTable = m_rDMapper_Impl.GetStyleSheetTable();
379 const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName );
380 pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) );
381 m_aTableProperties->Erase( aTableStyleVal->first );
383 aGrabBag["TableStyleName"] <<= sTableStyleName;
385 if( pStyleSheet )
387 // First get the style properties, then the table ones
388 PropertyMapPtr pTableProps( m_aTableProperties.get() );
389 TablePropertyMapPtr pEmptyProps( new TablePropertyMap );
391 m_aTableProperties = pEmptyProps;
393 PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);
395 table::BorderLine2 aBorderLine;
396 TableInfo rStyleInfo;
397 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine))
399 aGrabBag["TableStyleTopBorder"] <<= aBorderLine;
401 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine))
403 aGrabBag["TableStyleBottomBorder"] <<= aBorderLine;
405 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine))
407 aGrabBag["TableStyleLeftBorder"] <<= aBorderLine;
409 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine))
411 aGrabBag["TableStyleRightBorder"] <<= aBorderLine;
414 #ifdef DEBUG_WRITERFILTER
415 TagLogger::getInstance().startElement("mergedProps");
416 if (pMergedProperties)
417 pMergedProperties->dumpXml();
418 TagLogger::getInstance().endElement();
419 #endif
421 m_aTableProperties->InsertProps(pMergedProperties);
422 m_aTableProperties->InsertProps(pTableProps);
424 #ifdef DEBUG_WRITERFILTER
425 TagLogger::getInstance().startElement("TableProperties");
426 m_aTableProperties->dumpXml();
427 TagLogger::getInstance().endElement();
428 #endif
432 // This is the one preserving just all the table look attributes.
433 boost::optional<PropertyMap::Property> oTableLook = m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
434 if (oTableLook)
436 aGrabBag["TableStyleLook"] = oTableLook->second;
437 m_aTableProperties->Erase(oTableLook->first);
440 // This is just the "val" attribute's numeric value.
441 const boost::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK);
442 if(aTblLook)
444 aTblLook->second >>= rInfo.nTblLook;
445 m_aTableProperties->Erase( aTblLook->first );
448 // Set the table default attributes for the cells
449 rInfo.pTableDefaults->InsertProps(m_aTableProperties.get());
451 #ifdef DEBUG_WRITERFILTER
452 TagLogger::getInstance().startElement("TableDefaults");
453 rInfo.pTableDefaults->dumpXml();
454 TagLogger::getInstance().endElement();
455 #endif
457 if (!aGrabBag.empty())
459 m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::makeAny( aGrabBag.getAsConstPropertyValueList() ) );
462 m_aTableProperties->getValue( TablePropertyMap::GAP_HALF, nGapHalf );
463 m_aTableProperties->getValue( TablePropertyMap::LEFT_MARGIN, nLeftMargin );
465 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_LEFT,
466 rInfo.nLeftBorderDistance );
467 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_RIGHT,
468 rInfo.nRightBorderDistance );
469 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_TOP,
470 rInfo.nTopBorderDistance );
471 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_BOTTOM,
472 rInfo.nBottomBorderDistance );
474 table::TableBorderDistances aDistances;
475 aDistances.IsTopDistanceValid =
476 aDistances.IsBottomDistanceValid =
477 aDistances.IsLeftDistanceValid =
478 aDistances.IsRightDistanceValid = true;
479 aDistances.TopDistance = static_cast<sal_Int16>( rInfo.nTopBorderDistance );
480 aDistances.BottomDistance = static_cast<sal_Int16>( rInfo.nBottomBorderDistance );
481 aDistances.LeftDistance = static_cast<sal_Int16>( rInfo.nLeftBorderDistance );
482 aDistances.RightDistance = static_cast<sal_Int16>( rInfo.nRightBorderDistance );
484 m_aTableProperties->Insert( PROP_TABLE_BORDER_DISTANCES, uno::makeAny( aDistances ) );
486 if (!rFrameProperties.empty())
487 lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance);
489 // Set table above/bottom spacing to 0.
490 m_aTableProperties->Insert( PROP_TOP_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );
491 m_aTableProperties->Insert( PROP_BOTTOM_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );
493 //table border settings
494 table::TableBorder aTableBorder;
495 table::BorderLine2 aBorderLine, aLeftBorder, aRightBorder;
497 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine))
499 aTableBorder.TopLine = aBorderLine;
500 aTableBorder.IsTopLineValid = true;
502 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine))
504 aTableBorder.BottomLine = aBorderLine;
505 aTableBorder.IsBottomLineValid = true;
507 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder))
509 aTableBorder.LeftLine = aLeftBorder;
510 aTableBorder.IsLeftLineValid = true;
512 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo,
513 aRightBorder))
515 aTableBorder.RightLine = aRightBorder;
516 aTableBorder.IsRightLineValid = true;
518 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine))
520 aTableBorder.HorizontalLine = aBorderLine;
521 aTableBorder.IsHorizontalLineValid = true;
523 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine))
525 aTableBorder.VerticalLine = aBorderLine;
526 aTableBorder.IsVerticalLineValid = true;
529 aTableBorder.Distance = 0;
530 aTableBorder.IsDistanceValid = false;
532 m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::makeAny( aTableBorder ) );
534 #ifdef DEBUG_WRITERFILTER
535 lcl_debug_TableBorder(aTableBorder);
536 #endif
538 // Table position in Office is computed in 2 different ways :
539 // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd),
540 // so table's position depends on table's cells margin
541 // - nested tables: the goal is to have left-most border starting at table_indent pos
543 // Only top level table position depends on border width of Column A.
544 // TODO: Position based on last row (at least in MSOffice 2016), but first row in Office 2003.
545 // Export code is also based on first cell, so using first row here...
546 if ( !m_aCellProperties.empty() && !m_aCellProperties[0].empty() )
548 // aLeftBorder already contains tblBorder; overwrite if cell is different.
549 boost::optional<PropertyMap::Property> aCellBorder
550 = m_aCellProperties[0][0]->getProperty(PROP_LEFT_BORDER);
551 if ( aCellBorder )
552 aCellBorder->second >>= aLeftBorder;
553 aCellBorder = m_aCellProperties[0][0]->getProperty(PROP_RIGHT_BORDER);
554 if (aCellBorder)
555 aCellBorder->second >>= aRightBorder;
557 if (rInfo.nNestLevel == 1 && aLeftBorder.LineWidth && !rFrameProperties.empty())
559 lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5);
561 lcl_adjustBorderDistance(rInfo, aLeftBorder, aRightBorder);
563 // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
564 // this is also the default behavior in LO when DOCX doesn't define "compatibilityMode" option
565 sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();
567 if ( nMode > 0 && nMode <= 14 && rInfo.nNestLevel == 1 )
569 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nLeftMargin - nGapHalf - rInfo.nLeftBorderDistance ) );
571 else
573 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nLeftMargin - nGapHalf ) );
576 sal_Int32 nTableWidth = 0;
577 sal_Int32 nTableWidthType = text::SizeType::FIX;
578 m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH, nTableWidth );
579 m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType );
580 if( nTableWidthType == text::SizeType::FIX )
582 if( nTableWidth > 0 )
583 m_aTableProperties->Insert( PROP_WIDTH, uno::makeAny( nTableWidth ));
584 else
586 // tdf#109524: If there is no width for the table, make it simply 100% by default.
587 // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87)
588 nTableWidth = 100;
589 nTableWidthType = text::SizeType::VARIABLE;
592 if (nTableWidthType != text::SizeType::FIX)
594 m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::makeAny( sal_Int16( nTableWidth ) ) );
595 m_aTableProperties->Insert( PROP_IS_WIDTH_RELATIVE, uno::makeAny( true ) );
598 sal_Int32 nHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
599 // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties
600 if ( !m_aTableProperties->getValue( TablePropertyMap::HORI_ORIENT, nHoriOrient ) )
601 lcl_extractHoriOrient( rFrameProperties, nHoriOrient );
602 m_aTableProperties->Insert( PROP_HORI_ORIENT, uno::makeAny( sal_Int16(nHoriOrient) ) );
603 //fill default value - if not available
604 m_aTableProperties->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(0)), false);
606 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
607 if( m_aRowProperties.size() == 1 && m_aRowProperties[0].get() )
609 boost::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED);
610 if( oSplitAllowed )
612 bool bRowCanSplit = true;
613 oSplitAllowed->second >>= bRowCanSplit;
614 if( !bRowCanSplit )
615 m_aTableProperties->Insert( PROP_SPLIT, uno::makeAny(bRowCanSplit) );
619 rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();
621 #ifdef DEBUG_WRITERFILTER
622 TagLogger::getInstance().startElement("debug.tableprops");
623 m_aTableProperties->dumpXml();
624 TagLogger::getInstance().endElement();
625 #endif
629 return pTableStyle;
632 #define CNF_FIRST_ROW 0x800
633 #define CNF_LAST_ROW 0x400
634 #define CNF_FIRST_COLUMN 0x200
635 #define CNF_LAST_COLUMN 0x100
636 #define CNF_ODD_VBAND 0x080
637 #define CNF_EVEN_VBAND 0x040
638 #define CNF_ODD_HBAND 0x020
639 #define CNF_EVEN_HBAND 0x010
640 #define CNF_FIRST_ROW_LAST_COLUMN 0x008
641 #define CNF_FIRST_ROW_FIRST_COLUMN 0x004
642 #define CNF_LAST_ROW_LAST_COLUMN 0x002
643 #define CNF_LAST_ROW_FIRST_COLUMN 0x001
645 CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges)
647 #ifdef DEBUG_WRITERFILTER
648 TagLogger::getInstance().startElement("getCellProperties");
649 #endif
651 CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() );
653 if ( m_aCellProperties.empty() )
655 #ifdef DEBUG_WRITERFILTER
656 TagLogger::getInstance().endElement();
657 #endif
658 return aCellProperties;
660 // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties
661 PropertyMapVector2::const_iterator aRowOfCellsIterator = m_aCellProperties.begin();
662 PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd = m_aCellProperties.end();
663 PropertyMapVector2::const_iterator aLastRowIterator = m_aCellProperties.end() - 1;
664 sal_Int32 nRow = 0;
666 css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray();
667 PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
668 while( aRowOfCellsIterator != aRowOfCellsIteratorEnd )
670 //aRowOfCellsIterator points to a vector of PropertyMapPtr
671 PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin();
672 PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end();
674 sal_Int32 nRowStyleMask = 0;
676 if (aRowOfCellsIterator==m_aCellProperties.begin())
678 if(rInfo.nTblLook&0x20)
679 nRowStyleMask |= CNF_FIRST_ROW; // first row style used
681 else if (aRowOfCellsIterator==aLastRowIterator)
683 if(rInfo.nTblLook&0x40)
684 nRowStyleMask |= CNF_LAST_ROW; // last row style used
686 else if (*aRowIter && (*aRowIter)->isSet(PROP_TBL_HEADER))
687 nRowStyleMask |= CNF_FIRST_ROW; // table header implies first row
688 if(!nRowStyleMask) // if no row style used yet
690 // banding used only if not first and or last row style used
691 if(!(rInfo.nTblLook&0x200))
692 { // hbanding used
693 int n = nRow + 1;
694 if(rInfo.nTblLook&0x20)
695 n++;
696 if(n & 1)
697 nRowStyleMask = CNF_ODD_HBAND;
698 else
699 nRowStyleMask = CNF_EVEN_HBAND;
703 sal_Int32 nCell = 0;
704 pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
705 beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
706 while( aCellIterator != aCellIteratorEnd )
708 PropertyMapPtr pAllCellProps( new PropertyMap );
710 PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1;
711 bool bIsEndCol = aCellIterator == aLastCellIterator;
712 bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator;
714 //aCellIterator points to a PropertyMapPtr;
715 if( *aCellIterator )
717 // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177)
718 (*aCellIterator)->Erase(META_PROP_VERTICAL_BORDER);
719 (*aCellIterator)->Erase(META_PROP_HORIZONTAL_BORDER);
721 pAllCellProps->InsertProps(rInfo.pTableDefaults);
723 sal_Int32 nCellStyleMask = 0;
724 if (aCellIterator==aRowOfCellsIterator->begin())
726 if(rInfo.nTblLook&0x80)
727 nCellStyleMask = CNF_FIRST_COLUMN; // first col style used
729 else if (bIsEndCol)
731 if(rInfo.nTblLook&0x100)
732 nCellStyleMask = CNF_LAST_COLUMN; // last col style used
734 if(!nCellStyleMask) // if no cell style is used yet
736 if(!(rInfo.nTblLook&0x400))
737 { // vbanding used
738 int n = nCell + 1;
739 if(rInfo.nTblLook&0x80)
740 n++;
741 if(n & 1)
742 nCellStyleMask = CNF_ODD_VBAND;
743 else
744 nCellStyleMask = CNF_EVEN_VBAND;
747 sal_Int32 nCnfStyleMask = nCellStyleMask + nRowStyleMask;
748 if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_FIRST_ROW)
749 nCnfStyleMask |= CNF_FIRST_ROW_FIRST_COLUMN;
750 else if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_LAST_ROW)
751 nCnfStyleMask |= CNF_LAST_ROW_FIRST_COLUMN;
752 else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_FIRST_ROW)
753 nCnfStyleMask |= CNF_FIRST_ROW_LAST_COLUMN;
754 else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_LAST_ROW)
755 nCnfStyleMask |= CNF_LAST_ROW_LAST_COLUMN;
757 if ( rInfo.pTableStyle )
759 PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask );
761 // Check if we need to clean up some empty border definitions to match what Word does.
762 static const PropertyIds pBorders[] =
764 PROP_TOP_BORDER, PROP_LEFT_BORDER, PROP_BOTTOM_BORDER, PROP_RIGHT_BORDER
766 for (const PropertyIds& rBorder : pBorders)
768 boost::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder);
769 boost::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder);
770 if (oStyleCellBorder && oDirectCellBorder)
772 // We have a cell border from the table style and as direct formatting as well.
773 table::BorderLine2 aStyleCellBorder = oStyleCellBorder->second.get<table::BorderLine2>();
774 table::BorderLine2 aDirectCellBorder = oDirectCellBorder->second.get<table::BorderLine2>();
775 if (aStyleCellBorder.LineStyle != table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE)
777 // The style one would be visible, but then cleared away as direct formatting.
778 // Delete both, so that table formatting can become visible.
779 pStyleProps->Erase(rBorder);
780 (*aCellIterator)->Erase(rBorder);
782 else
784 boost::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder);
785 if (oTableBorder)
787 table::BorderLine2 aTableBorder = oTableBorder->second.get<table::BorderLine2>();
788 // Both style and direct formatting says that the cell has no border.
789 bool bNoCellBorder = aStyleCellBorder.LineStyle == table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE;
790 if (aTableBorder.LineStyle != table::BorderLineStyle::NONE && bNoCellBorder)
792 // But at a table-level, there is a border, then again delete both cell properties.
793 pStyleProps->Erase(rBorder);
794 (*aCellIterator)->Erase(rBorder);
801 pAllCellProps->InsertProps( pStyleProps );
804 // Remove properties from style/row that aren't allowed in cells
805 pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
806 pAllCellProps->Erase( PROP_TBL_HEADER );
808 // Then add the cell properties
809 pAllCellProps->InsertProps(*aCellIterator);
810 std::swap(*(*aCellIterator), *pAllCellProps );
812 #ifdef DEBUG_WRITERFILTER
813 TagLogger::getInstance().startElement("cell");
814 TagLogger::getInstance().attribute("cell", nCell);
815 TagLogger::getInstance().attribute("row", nRow);
816 #endif
818 lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nRow, bIsEndCol, bIsEndRow );
820 //now set the default left+right border distance TODO: there's an sprm containing the default distance!
821 aCellIterator->get()->Insert( PROP_LEFT_BORDER_DISTANCE,
822 uno::makeAny(rInfo.nLeftBorderDistance ), false);
823 aCellIterator->get()->Insert( PROP_RIGHT_BORDER_DISTANCE,
824 uno::makeAny(rInfo.nRightBorderDistance ), false);
825 aCellIterator->get()->Insert( PROP_TOP_BORDER_DISTANCE,
826 uno::makeAny(rInfo.nTopBorderDistance ), false);
827 aCellIterator->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE,
828 uno::makeAny(rInfo.nBottomBorderDistance ), false);
830 // Horizontal merge is not an UNO property, extract that info here to rMerges, and then remove it from the map.
831 const boost::optional<PropertyMap::Property> aHorizontalMergeVal = (*aCellIterator)->getProperty(PROP_HORIZONTAL_MERGE);
832 if (aHorizontalMergeVal)
834 if (aHorizontalMergeVal->second.get<bool>())
836 // first cell in a merge
837 HorizontallyMergedCell aMerge(nRow, nCell);
838 rMerges.push_back(aMerge);
840 else if (!rMerges.empty())
842 // resuming an earlier merge
843 HorizontallyMergedCell& rMerge = rMerges.back();
844 rMerge.m_nLastRow = nRow;
845 rMerge.m_nLastCol = nCell;
847 (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE);
849 pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues();
850 #ifdef DEBUG_WRITERFILTER
851 TagLogger::getInstance().endElement();
852 #endif
854 ++nCell;
855 ++aCellIterator;
857 ++nRow;
858 ++aRowOfCellsIterator;
859 ++aRowIter;
862 #ifdef DEBUG_WRITERFILTER
863 TagLogger::getInstance().endElement();
864 #endif
866 return aCellProperties;
869 /// Do all cells in this row have a CellHideMark property?
870 static bool lcl_hideMarks(PropertyMapVector1& rCellProperties)
872 for (PropertyMapPtr & p : rCellProperties)
874 // if anything is vertically merged, the row must not be set to fixed
875 // as Writer's layout doesn't handle that well
876 if (!p->isSet(PROP_CELL_HIDE_MARK) || p->isSet(PROP_VERTICAL_MERGE))
877 return false;
879 return true;
882 /// Are all cells in this row empty?
883 static bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow)
885 if (nRow >= static_cast<sal_Int32>(rTableRanges.size()))
887 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
888 return false;
891 RowSequence_t rRowSeq = rTableRanges[nRow];
892 if (rRowSeq.getLength() == 0)
894 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
895 return false;
898 if (!rRowSeq[0][0].is())
900 // This can happen when we can't import the table, e.g. we're inside a
901 // comment.
902 SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
903 return false;
906 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY);
909 for (sal_Int32 nCell = 0; nCell < rRowSeq.getLength(); ++nCell)
910 // See SwXText::Impl::ConvertCell(), we need to compare the start of
911 // the start and the end of the end. However for our text ranges, only
912 // the starts are set, so compareRegionStarts() does what we need.
913 if (xTextRangeCompare->compareRegionStarts(rRowSeq[nCell][0], rRowSeq[nCell][1]) != 0)
914 return false;
916 catch (const lang::IllegalArgumentException& e)
918 SAL_WARN( "writerfilter.dmapper", "compareRegionStarts() failed: " << e);
919 return false;
921 return true;
924 css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties()
926 #ifdef DEBUG_WRITERFILTER
927 TagLogger::getInstance().startElement("getRowProperties");
928 #endif
930 css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() );
931 sal_Int32 nRow = 0;
932 for( const auto& rRow : m_aRowProperties )
934 #ifdef DEBUG_WRITERFILTER
935 TagLogger::getInstance().startElement("rowProps.row");
936 #endif
937 if (rRow)
939 //set default to 'break across pages"
940 rRow->Insert( PROP_IS_SPLIT_ALLOWED, uno::makeAny(true ), false );
941 // tblHeader is only our property, remove before the property map hits UNO
942 rRow->Erase(PROP_TBL_HEADER);
944 if (lcl_hideMarks(m_aCellProperties[nRow]) && lcl_emptyRow(m_aTableRanges, nRow))
946 // We have CellHideMark on all cells, and also all cells are empty:
947 // Force the row height to be exactly as specified, and not just as the minimum suggestion.
948 rRow->Insert(PROP_SIZE_TYPE, uno::makeAny(text::SizeType::FIX));
951 aRowProperties[nRow] = rRow->GetPropertyValues();
952 #ifdef DEBUG_WRITERFILTER
953 rRow->dumpXml();
954 lcl_DumpPropertyValues(aRowProperties[nRow]);
955 #endif
957 ++nRow;
958 #ifdef DEBUG_WRITERFILTER
959 TagLogger::getInstance().endElement();
960 #endif
963 #ifdef DEBUG_WRITERFILTER
964 TagLogger::getInstance().endElement();
965 #endif
967 return aRowProperties;
970 // Apply paragraph property to each paragraph within a cell.
971 static void lcl_ApplyCellParaProps(uno::Reference<table::XCell> const& xCell,
972 const uno::Any& rBottomMargin)
974 uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCell, uno::UNO_QUERY);
975 uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
976 while (xEnumeration->hasMoreElements())
978 uno::Reference<beans::XPropertySet> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
979 uno::Reference<beans::XPropertyState> xPropertyState(xParagraph, uno::UNO_QUERY);
980 // Don't apply in case direct formatting is already present.
981 // TODO: probably paragraph style has priority over table style here.
982 if (xPropertyState.is() && xPropertyState->getPropertyState("ParaBottomMargin") == beans::PropertyState_DEFAULT_VALUE)
983 xParagraph->setPropertyValue("ParaBottomMargin", rBottomMargin);
987 void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
989 #ifdef DEBUG_WRITERFILTER
990 TagLogger::getInstance().startElement("tablehandler.endTable");
991 #endif
993 // If we want to make this table a floating one.
994 std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >
995 (m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
996 TableInfo aTableInfo;
997 aTableInfo.nNestLevel = nestedTableLevel;
998 aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties);
999 // expands to uno::Sequence< Sequence< beans::PropertyValues > >
1001 std::vector<HorizontallyMergedCell> aMerges;
1002 CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges);
1004 css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties();
1006 #ifdef DEBUG_WRITERFILTER
1007 lcl_DumpPropertyValueSeq(aRowProperties);
1008 #endif
1010 if (!m_aTableRanges.empty())
1012 uno::Reference<text::XTextRange> xStart;
1013 uno::Reference<text::XTextRange> xEnd;
1015 bool bFloating = !aFrameProperties.empty();
1016 // Additional checks: if we can do this.
1017 if (bFloating && m_aTableRanges[0].getLength() > 0 && m_aTableRanges[0][0].getLength() > 0)
1019 xStart = m_aTableRanges[0][0][0];
1020 uno::Sequence< uno::Sequence< uno::Reference<text::XTextRange> > >& rLastRow = m_aTableRanges[m_aTableRanges.size() - 1];
1021 if (rLastRow.getLength())
1023 uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1];
1024 xEnd = rLastCell[1];
1027 uno::Reference<text::XTextTable> xTable;
1030 if (m_xText.is())
1032 xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties);
1034 if (xTable.is())
1036 if (!aMerges.empty())
1038 static const std::vector<OUStringLiteral> aBorderNames
1039 = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" };
1041 // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
1042 for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it)
1044 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
1045 uno::Reference<beans::XPropertySet> xFirstCell(
1046 xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow),
1047 uno::UNO_QUERY_THROW);
1048 OUString aFirst
1049 = xFirstCell->getPropertyValue("CellName").get<OUString>();
1050 // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
1051 if (it->m_nLastCol != -1)
1053 // Save border properties of the first cell
1054 // before merge.
1055 table::BorderLine2 aBorderValues[4];
1056 for (size_t i = 0; i < aBorderNames.size(); ++i)
1057 xFirstCell->getPropertyValue(aBorderNames[i])
1058 >>= aBorderValues[i];
1060 uno::Reference<beans::XPropertySet> xLastCell(
1061 xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow),
1062 uno::UNO_QUERY_THROW);
1063 OUString aLast
1064 = xLastCell->getPropertyValue("CellName").get<OUString>();
1066 uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst);
1067 xCursor->gotoCellByName(aLast, true);
1069 xCursor->mergeRange();
1071 // Handle conflicting properties: mergeRange()
1072 // takes the last cell, Word takes the first
1073 // cell.
1074 for (size_t i = 0; i < aBorderNames.size(); ++i)
1076 if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE)
1077 xFirstCell->setPropertyValue(
1078 aBorderNames[i], uno::makeAny(aBorderValues[i]));
1085 // OOXML table style may container paragraph properties, apply these now.
1086 for (int i = 0; i < aTableInfo.aTableProperties.getLength(); ++i)
1088 if (aTableInfo.aTableProperties[i].Name == "ParaBottomMargin")
1090 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY);
1091 uno::Any aBottomMargin = aTableInfo.aTableProperties[i].Value;
1092 sal_Int32 nRows = aCellProperties.getLength();
1093 for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1095 const uno::Sequence< beans::PropertyValues > aCurrentRow = aCellProperties[nRow];
1096 sal_Int32 nCells = aCurrentRow.getLength();
1097 for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
1098 lcl_ApplyCellParaProps(xCellRange->getCellByPosition(nCell, nRow), aBottomMargin);
1100 break;
1105 catch ( const lang::IllegalArgumentException &e )
1107 SAL_INFO("writerfilter.dmapper",
1108 "Conversion to table error: " << e);
1109 #ifdef DEBUG_WRITERFILTER
1110 TagLogger::getInstance().chars(std::string("failed to import table!"));
1111 #endif
1113 catch ( const uno::Exception &e )
1115 SAL_INFO("writerfilter.dmapper",
1116 "Exception during table creation: " << e);
1119 // If we have a table with a start and an end position, we should make it a floating one.
1120 // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
1121 if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote)
1123 uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
1124 bool bIsRelative = false;
1125 xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative;
1126 if (!bIsRelative)
1128 beans::PropertyValue aValue;
1129 aValue.Name = "Width";
1130 aValue.Value = xTableProperties->getPropertyValue("Width");
1131 aFrameProperties.push_back(aValue);
1133 else
1135 beans::PropertyValue aValue;
1136 aValue.Name = "FrameWidthPercent";
1137 aValue.Value = xTableProperties->getPropertyValue("RelativeWidth");
1138 aFrameProperties.push_back(aValue);
1140 // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
1141 xTableProperties->setPropertyValue("RelativeWidth", uno::makeAny(sal_Int16(100)));
1144 // A non-zero left margin would move the table out of the frame, move the frame itself instead.
1145 xTableProperties->setPropertyValue("LeftMargin", uno::makeAny(sal_Int32(0)));
1147 // In case the document ends with a table, we're called after
1148 // SectionPropertyMap::CloseSectionGroup(), so we'll have no idea
1149 // about the text area width, nor can fix this by delaying the text
1150 // frame conversion: just do it here.
1151 // Also, when the anchor is within a table, then do it here as well,
1152 // as xStart/xEnd would not point to the start/end at conversion
1153 // time anyway.
1154 // Next exception: it's pointless to delay the conversion if the
1155 // table is not in the body text.
1156 sal_Int32 nTableWidth = 0;
1157 m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
1158 sal_Int32 nTableWidthType = text::SizeType::FIX;
1159 m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
1160 if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
1161 m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType);
1162 else
1164 // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header.
1165 uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY);
1166 // Only execute the conversion if the table is not anchored at
1167 // the start of an outer table cell, that's not yet
1168 // implemented.
1169 if (xTextAppendAndConvert.is() && !bTableStartsAtCellStart)
1170 xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties));
1174 // We're right after a table conversion.
1175 m_rDMapper_Impl.m_bConvertedTable = true;
1178 m_aTableProperties.clear();
1179 m_aCellProperties.clear();
1180 m_aRowProperties.clear();
1181 m_bHadFootOrEndnote = false;
1183 #ifdef DEBUG_WRITERFILTER
1184 TagLogger::getInstance().endElement();
1185 TagLogger::getInstance().endElement();
1186 #endif
1189 void DomainMapperTableHandler::startRow(const TablePropertyMapPtr& pProps)
1191 m_aRowProperties.push_back( pProps.get() );
1192 m_aCellProperties.emplace_back( );
1194 #ifdef DEBUG_WRITERFILTER
1195 TagLogger::getInstance().startElement("table.row");
1196 if (pProps != nullptr)
1197 pProps->dumpXml();
1198 #endif
1200 m_aRowRanges.clear();
1203 void DomainMapperTableHandler::endRow()
1205 m_aTableRanges.push_back(comphelper::containerToSequence(m_aRowRanges));
1206 #ifdef DEBUG_WRITERFILTER
1207 TagLogger::getInstance().endElement();
1208 #endif
1211 void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start,
1212 const TablePropertyMapPtr& pProps )
1214 sal_uInt32 nRow = m_aRowProperties.size();
1215 if ( pProps.get( ) )
1216 m_aCellProperties[nRow - 1].push_back( pProps.get() );
1217 else
1219 // Adding an empty cell properties map to be able to get
1220 // the table defaults properties
1221 TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) );
1222 m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() );
1225 #ifdef DEBUG_WRITERFILTER
1226 TagLogger::getInstance().startElement("table.cell");
1227 TagLogger::getInstance().startElement("table.cell.start");
1228 TagLogger::getInstance().chars(XTextRangeToString(start));
1229 TagLogger::getInstance().endElement();
1230 if (pProps.get())
1231 pProps->printProperties();
1232 #endif
1234 //add a new 'row' of properties
1235 m_aCellRange.clear();
1236 uno::Reference<text::XTextRange> xStart;
1237 if (start.get())
1238 xStart = start->getStart();
1239 m_aCellRange.push_back(xStart);
1242 void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end)
1244 #ifdef DEBUG_WRITERFILTER
1245 TagLogger::getInstance().startElement("table.cell.end");
1246 TagLogger::getInstance().chars(XTextRangeToString(end));
1247 TagLogger::getInstance().endElement();
1248 TagLogger::getInstance().endElement();
1249 #endif
1251 uno::Reference<text::XTextRange> xEnd;
1252 if (end.get())
1253 xEnd = end->getEnd();
1254 m_aCellRange.push_back(xEnd);
1255 m_aRowRanges.push_back(comphelper::containerToSequence(m_aCellRange));
1258 void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote)
1260 m_bHadFootOrEndnote = bHadFootOrEndnote;
1263 DomainMapper_Impl& DomainMapperTableHandler::getDomainMapperImpl()
1265 return m_rDMapper_Impl;
1270 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */