1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
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>
45 namespace writerfilter
{
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
)
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)
79 static void lcl_mergeBorder( PropertyIds nId
, const PropertyMapPtr
& pOrig
, const PropertyMapPtr
& pDest
)
81 boost::optional
<PropertyMap::Property
> pOrigVal
= pOrig
->getProperty(nId
);
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
99 pVerticalVal
= pTableBorders
->getProperty(META_PROP_VERTICAL_BORDER
);
101 aVertProp
= pVerticalVal
->second
;
105 aVertProp
= pVerticalVal
->second
;
106 pCellProps
->Erase( pVerticalVal
->first
);
110 if ( !pHorizontalVal
)
112 pHorizontalVal
= pTableBorders
->getProperty(META_PROP_HORIZONTAL_BORDER
);
113 if ( pHorizontalVal
)
114 aHorizProp
= pHorizontalVal
->second
;
118 aHorizProp
= pHorizontalVal
->second
;
119 pCellProps
->Erase( pHorizontalVal
->first
);
124 lcl_mergeBorder( PROP_LEFT_BORDER
, pTableBorders
, pCellProps
);
126 pCellProps
->Insert( PROP_RIGHT_BORDER
, aVertProp
, false );
131 lcl_mergeBorder( PROP_RIGHT_BORDER
, pTableBorders
, pCellProps
);
133 pCellProps
->Insert( PROP_LEFT_BORDER
, aVertProp
, false );
136 if ( nCell
> 0 && !bIsEndCol
)
140 pCellProps
->Insert( PROP_RIGHT_BORDER
, aVertProp
, false );
141 pCellProps
->Insert( PROP_LEFT_BORDER
, aVertProp
, false );
147 lcl_mergeBorder( PROP_TOP_BORDER
, pTableBorders
, pCellProps
);
148 if ( pHorizontalVal
)
149 pCellProps
->Insert( PROP_BOTTOM_BORDER
, aHorizProp
, false );
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();
204 sal_Int32 nLeftBorderDistance
;
205 sal_Int32 nRightBorderDistance
;
206 sal_Int32 nTopBorderDistance
;
207 sal_Int32 nBottomBorderDistance
;
209 sal_Int32 nNestLevel
;
210 PropertyMapPtr pTableDefaults
;
211 PropertyMapPtr pTableBorders
;
212 TableStyleSheetEntry
* pTableStyle
;
213 css::beans::PropertyValues aTableProperties
;
216 : nLeftBorderDistance(DEF_BORDER_DIST
)
217 , nRightBorderDistance(DEF_BORDER_DIST
)
218 , nTopBorderDistance(0)
219 , nBottomBorderDistance(0)
222 , pTableDefaults(new PropertyMap
)
223 , pTableBorders(new PropertyMap
)
224 , pTableStyle(nullptr)
233 bool lcl_extractTableBorderProperty(const PropertyMapPtr
& pTableProperties
, const PropertyIds nId
, TableInfo
const & rInfo
, table::BorderLine2
& rLine
)
235 if (!pTableProperties
)
238 const boost::optional
<PropertyMap::Property
> aTblBorder
= pTableProperties
->getProperty(nId
);
241 OSL_VERIFY(aTblBorder
->second
>>= rLine
);
243 rInfo
.pTableBorders
->Insert( nId
, uno::makeAny( rLine
) );
244 rInfo
.pTableDefaults
->Erase( nId
);
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
;
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
>();
276 rPropertyValue
.Value
<<= nValue
;
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
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
)
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
);
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
;
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();
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();
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
);
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
);
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();
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
,
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
);
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
);
552 aCellBorder
->second
>>= aLeftBorder
;
553 aCellBorder
= m_aCellProperties
[0][0]->getProperty(PROP_RIGHT_BORDER
);
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
) );
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
));
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)
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
);
612 bool bRowCanSplit
= true;
613 oSplitAllowed
->second
>>= 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();
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");
651 CellPropertyValuesSeq_t
aCellProperties( m_aCellProperties
.size() );
653 if ( m_aCellProperties
.empty() )
655 #ifdef DEBUG_WRITERFILTER
656 TagLogger::getInstance().endElement();
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;
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))
694 if(rInfo
.nTblLook
&0x20)
697 nRowStyleMask
= CNF_ODD_HBAND
;
699 nRowStyleMask
= CNF_EVEN_HBAND
;
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;
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
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))
739 if(rInfo
.nTblLook
&0x80)
742 nCellStyleMask
= CNF_ODD_VBAND
;
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
);
784 boost::optional
<PropertyMap::Property
> oTableBorder
= rInfo
.pTableBorders
->getProperty(rBorder
);
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
);
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();
858 ++aRowOfCellsIterator
;
862 #ifdef DEBUG_WRITERFILTER
863 TagLogger::getInstance().endElement();
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
))
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?");
891 RowSequence_t rRowSeq
= rTableRanges
[nRow
];
892 if (rRowSeq
.getLength() == 0)
894 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
898 if (!rRowSeq
[0][0].is())
900 // This can happen when we can't import the table, e.g. we're inside a
902 SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
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)
916 catch (const lang::IllegalArgumentException
& e
)
918 SAL_WARN( "writerfilter.dmapper", "compareRegionStarts() failed: " << e
);
924 css::uno::Sequence
<css::beans::PropertyValues
> DomainMapperTableHandler::endTableGetRowProperties()
926 #ifdef DEBUG_WRITERFILTER
927 TagLogger::getInstance().startElement("getRowProperties");
930 css::uno::Sequence
<css::beans::PropertyValues
> aRowProperties( m_aRowProperties
.size() );
932 for( const auto& rRow
: m_aRowProperties
)
934 #ifdef DEBUG_WRITERFILTER
935 TagLogger::getInstance().startElement("rowProps.row");
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
954 lcl_DumpPropertyValues(aRowProperties
[nRow
]);
958 #ifdef DEBUG_WRITERFILTER
959 TagLogger::getInstance().endElement();
963 #ifdef DEBUG_WRITERFILTER
964 TagLogger::getInstance().endElement();
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");
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
);
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
;
1032 xTable
= m_xText
->convertToTable(comphelper::containerToSequence(m_aTableRanges
), aCellProperties
, aRowProperties
, aTableInfo
.aTableProperties
);
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
);
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
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
);
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
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
);
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!"));
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
;
1128 beans::PropertyValue aValue
;
1129 aValue
.Name
= "Width";
1130 aValue
.Value
= xTableProperties
->getPropertyValue("Width");
1131 aFrameProperties
.push_back(aValue
);
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
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
);
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
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();
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)
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();
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() );
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();
1231 pProps
->printProperties();
1234 //add a new 'row' of properties
1235 m_aCellRange
.clear();
1236 uno::Reference
<text::XTextRange
> xStart
;
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();
1251 uno::Reference
<text::XTextRange
> xEnd
;
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: */