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 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include "DomainMapperTableHandler.hxx"
25 #include "DomainMapper_Impl.hxx"
26 #include "StyleSheetTable.hxx"
28 #include <com/sun/star/beans/TolerantPropertySetResultType.hpp>
29 #include <com/sun/star/beans/XTolerantMultiPropertySet.hpp>
30 #include <com/sun/star/style/ParagraphAdjust.hpp>
31 #include <com/sun/star/table/TableBorderDistances.hpp>
32 #include <com/sun/star/table/TableBorder.hpp>
33 #include <com/sun/star/table/BorderLine2.hpp>
34 #include <com/sun/star/table/BorderLineStyle.hpp>
35 #include <com/sun/star/table/XCellRange.hpp>
36 #include <com/sun/star/text/HoriOrientation.hpp>
37 #include <com/sun/star/text/SizeType.hpp>
38 #include <com/sun/star/text/TextContentAnchorType.hpp>
39 #include <com/sun/star/text/WritingMode2.hpp>
40 #include <com/sun/star/text/XTextField.hpp>
41 #include <com/sun/star/text/XTextRangeCompare.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/beans/XPropertyState.hpp>
44 #include <com/sun/star/container/XEnumeration.hpp>
45 #include <com/sun/star/container/XEnumerationAccess.hpp>
46 #include <com/sun/star/drawing/FillStyle.hpp>
47 #include "TablePositionHandler.hxx"
48 #include "TagLogger.hxx"
50 #include <osl/diagnose.h>
51 #include <sal/log.hxx>
52 #include <comphelper/diagnose_ex.hxx>
53 #include <comphelper/sequence.hxx>
54 #include <comphelper/propertyvalue.hxx>
55 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
56 #include <com/sun/star/style/BreakType.hpp>
57 #include <officecfg/Office/Writer.hxx>
60 #include "PropertyMapHelper.hxx"
61 #include <rtl/ustring.hxx>
65 namespace writerfilter::dmapper
{
67 using namespace ::com::sun::star
;
69 #define DEF_BORDER_DIST 190 //0,19cm
70 #define CNF_FIRST_ROW 0x800
71 #define CNF_LAST_ROW 0x400
72 #define CNF_FIRST_COLUMN 0x200
73 #define CNF_LAST_COLUMN 0x100
74 #define CNF_ODD_VBAND 0x080
75 #define CNF_EVEN_VBAND 0x040
76 #define CNF_ODD_HBAND 0x020
77 #define CNF_EVEN_HBAND 0x010
78 #define CNF_FIRST_ROW_LAST_COLUMN 0x008
79 #define CNF_FIRST_ROW_FIRST_COLUMN 0x004
80 #define CNF_LAST_ROW_LAST_COLUMN 0x002
81 #define CNF_LAST_ROW_FIRST_COLUMN 0x001
84 // total number of table columns
85 #define MAXTABLECELLS 63
87 DomainMapperTableHandler::DomainMapperTableHandler(
88 css::uno::Reference
<css::text::XTextAppendAndConvert
> xText
,
89 DomainMapper_Impl
& rDMapper_Impl
)
90 : m_xText(std::move(xText
)),
91 m_rDMapper_Impl( rDMapper_Impl
),
92 m_bHadFootOrEndnote(false)
96 DomainMapperTableHandler::~DomainMapperTableHandler()
100 void DomainMapperTableHandler::startTable(const TablePropertyMapPtr
& pProps
)
102 m_aTableProperties
= pProps
;
103 m_aTableRanges
.clear();
106 TagLogger::getInstance().startElement("tablehandler.table");
113 static void lcl_mergeBorder( PropertyIds nId
, const PropertyMapPtr
& pOrig
, const PropertyMapPtr
& pDest
)
115 std::optional
<PropertyMap::Property
> pOrigVal
= pOrig
->getProperty(nId
);
119 pDest
->Insert( nId
, pOrigVal
->second
, false );
123 static void lcl_computeCellBorders( const PropertyMapPtr
& pTableBorders
, const PropertyMapPtr
& pCellProps
,
124 sal_uInt32 nCell
, sal_uInt32 nFirstCell
, sal_uInt32 nLastCell
, sal_Int32 nRow
, bool bIsEndRow
, bool bMergedVertically
)
126 const bool bIsStartCol
= nCell
== nFirstCell
;
127 const bool bIsEndCol
= nCell
== nLastCell
;
128 std::optional
<PropertyMap::Property
> pVerticalVal
= pCellProps
->getProperty(META_PROP_VERTICAL_BORDER
);
129 std::optional
<PropertyMap::Property
> pHorizontalVal
= pCellProps
->getProperty(META_PROP_HORIZONTAL_BORDER
);
131 // Handle the vertical and horizontal borders
135 pVerticalVal
= pTableBorders
->getProperty(META_PROP_VERTICAL_BORDER
);
137 aVertProp
= pVerticalVal
->second
;
141 aVertProp
= pVerticalVal
->second
;
142 pCellProps
->Erase( pVerticalVal
->first
);
146 if ( !pHorizontalVal
)
148 pHorizontalVal
= pTableBorders
->getProperty(META_PROP_HORIZONTAL_BORDER
);
149 if ( pHorizontalVal
)
150 aHorizProp
= pHorizontalVal
->second
;
154 aHorizProp
= pHorizontalVal
->second
;
155 pCellProps
->Erase( pHorizontalVal
->first
);
159 lcl_mergeBorder( PROP_LEFT_BORDER
, pTableBorders
, pCellProps
);
162 lcl_mergeBorder( PROP_RIGHT_BORDER
, pTableBorders
, pCellProps
);
164 // <w:insideV> counts if there are multiple cells in this row.
167 if ( !bIsEndCol
&& nCell
>= nFirstCell
)
168 pCellProps
->Insert( PROP_RIGHT_BORDER
, aVertProp
, false );
169 if ( !bIsStartCol
&& nCell
<= nLastCell
)
170 pCellProps
->Insert( PROP_LEFT_BORDER
, aVertProp
, false );
175 lcl_mergeBorder( PROP_TOP_BORDER
, pTableBorders
, pCellProps
);
176 if ( pHorizontalVal
&& !bMergedVertically
)
177 pCellProps
->Insert( PROP_BOTTOM_BORDER
, aHorizProp
, false );
180 if ( bMergedVertically
)
181 lcl_mergeBorder( PROP_BOTTOM_BORDER
, pTableBorders
, pCellProps
);
185 lcl_mergeBorder( PROP_BOTTOM_BORDER
, pTableBorders
, pCellProps
);
186 if ( pHorizontalVal
)
187 pCellProps
->Insert( PROP_TOP_BORDER
, aHorizProp
, false );
190 if ( nRow
> 0 && !bIsEndRow
)
192 if ( pHorizontalVal
)
194 pCellProps
->Insert( PROP_TOP_BORDER
, aHorizProp
, false );
195 pCellProps
->Insert( PROP_BOTTOM_BORDER
, aHorizProp
, false );
202 static void lcl_debug_BorderLine(table::BorderLine
const & rLine
)
204 TagLogger::getInstance().startElement("BorderLine");
205 TagLogger::getInstance().attribute("Color", rLine
.Color
);
206 TagLogger::getInstance().attribute("InnerLineWidth", rLine
.InnerLineWidth
);
207 TagLogger::getInstance().attribute("OuterLineWidth", rLine
.OuterLineWidth
);
208 TagLogger::getInstance().attribute("LineDistance", rLine
.LineDistance
);
209 TagLogger::getInstance().endElement();
212 static void lcl_debug_TableBorder(table::TableBorder
const & rBorder
)
214 TagLogger::getInstance().startElement("TableBorder");
215 lcl_debug_BorderLine(rBorder
.TopLine
);
216 TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder
.IsTopLineValid
));
217 lcl_debug_BorderLine(rBorder
.BottomLine
);
218 TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder
.IsBottomLineValid
));
219 lcl_debug_BorderLine(rBorder
.LeftLine
);
220 TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder
.IsLeftLineValid
));
221 lcl_debug_BorderLine(rBorder
.RightLine
);
222 TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder
.IsRightLineValid
));
223 lcl_debug_BorderLine(rBorder
.VerticalLine
);
224 TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder
.IsVerticalLineValid
));
225 lcl_debug_BorderLine(rBorder
.HorizontalLine
);
226 TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder
.IsHorizontalLineValid
));
227 TagLogger::getInstance().attribute("Distance", rBorder
.Distance
);
228 TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder
.IsDistanceValid
));
229 TagLogger::getInstance().endElement();
235 sal_Int32 nLeftBorderDistance
;
236 sal_Int32 nRightBorderDistance
;
237 sal_Int32 nTopBorderDistance
;
238 sal_Int32 nBottomBorderDistance
;
240 sal_Int32 nNestLevel
;
241 PropertyMapPtr pTableDefaults
;
242 PropertyMapPtr pTableBorders
;
243 TableStyleSheetEntry
* pTableStyle
;
244 css::beans::PropertyValues aTableProperties
;
245 std::vector
< PropertyIds
> aTablePropertyIds
;
248 : nLeftBorderDistance(DEF_BORDER_DIST
)
249 , nRightBorderDistance(DEF_BORDER_DIST
)
250 , nTopBorderDistance(0)
251 , nBottomBorderDistance(0)
254 , pTableDefaults(new PropertyMap
)
255 , pTableBorders(new PropertyMap
)
256 , pTableStyle(nullptr)
265 bool lcl_extractTableBorderProperty(const PropertyMapPtr
& pTableProperties
, const PropertyIds nId
, TableInfo
const & rInfo
, table::BorderLine2
& rLine
)
267 if (!pTableProperties
)
270 const std::optional
<PropertyMap::Property
> aTblBorder
= pTableProperties
->getProperty(nId
);
273 OSL_VERIFY(aTblBorder
->second
>>= rLine
);
275 rInfo
.pTableBorders
->Insert( nId
, uno::Any( rLine
) );
276 rInfo
.pTableDefaults
->Erase( nId
);
284 void lcl_extractHoriOrient(std::vector
<beans::PropertyValue
>& rFrameProperties
, sal_Int32
& nHoriOrient
)
286 // Shifts the frame left by the given value.
287 for (const beans::PropertyValue
& rFrameProperty
: rFrameProperties
)
289 if (rFrameProperty
.Name
== "HoriOrient")
291 sal_Int32 nValue
= rFrameProperty
.Value
.get
<sal_Int32
>();
292 if (nValue
!= text::HoriOrientation::NONE
)
293 nHoriOrient
= nValue
;
299 void lcl_DecrementHoriOrientPosition(std::vector
<beans::PropertyValue
>& rFrameProperties
, sal_Int32 nAmount
)
301 // Shifts the frame left by the given value.
302 for (beans::PropertyValue
& rPropertyValue
: rFrameProperties
)
304 if (rPropertyValue
.Name
== "HoriOrientPosition")
306 sal_Int32 nValue
= rPropertyValue
.Value
.get
<sal_Int32
>();
308 rPropertyValue
.Value
<<= nValue
;
314 void lcl_adjustBorderDistance(TableInfo
& rInfo
, const table::BorderLine2
& rLeftBorder
,
315 const table::BorderLine2
& rRightBorder
)
317 // MS Word appears to do these things to adjust the cell horizontal area:
319 // bll = left borderline width
320 // blr = right borderline width
321 // cea = cell's edit area rectangle
323 // cml = cell's left margin (padding) defined in cell settings
324 // cmr = cell's right margin (padding) defined in cell settings
325 // cw = cell width (distance between middles of left borderline and right borderline)
326 // pad_l = actual cea left padding = (its left pos relative to middle of bll)
327 // pad_r = actual cea right padding = abs (its right pos relative to middle of blr)
329 // pad_l = max(bll/2, cml) -> cea does not overlap left borderline
330 // cea_w = cw-max(pad_l+blr/2, cml+cmr) -> cea does not overlap right borderline
331 // pad_r = max(pad_l+blr/2, cml+cmr) - pad_l
333 // It means that e.g. for border widths of 6 pt (~2.12 mm), left margin 0 mm, and right margin
334 // 2 mm, actual left and right margins will (unexpectedly) coincide with inner edges of cell's
335 // borderlines - the right margin won't create spacing between right of edit rectangle and the
336 // inner edge of right borderline.
338 const sal_Int32 nActualL
339 = std::max
<sal_Int32
>(rLeftBorder
.LineWidth
/ 2, rInfo
.nLeftBorderDistance
);
340 const sal_Int32 nActualR
341 = std::max
<sal_Int32
>(nActualL
+ rRightBorder
.LineWidth
/ 2,
342 rInfo
.nLeftBorderDistance
+ rInfo
.nRightBorderDistance
)
344 rInfo
.nLeftBorderDistance
= nActualL
;
345 rInfo
.nRightBorderDistance
= nActualR
;
350 TableStyleSheetEntry
* DomainMapperTableHandler::endTableGetTableStyle(TableInfo
& rInfo
,
351 std::vector
<beans::PropertyValue
>& rFrameProperties
,
352 bool bConvertToFloatingInFootnote
)
354 // will receive the table style if any
355 TableStyleSheetEntry
* pTableStyle
= nullptr;
357 if( m_aTableProperties
)
359 //create properties from the table attributes
360 sal_Int32 nLeftMargin
= 0;
362 comphelper::SequenceAsHashMap aGrabBag
;
364 if (bConvertToFloatingInFootnote
)
366 // define empty "TablePosition" to avoid export temporary floating
367 aGrabBag
["TablePosition"] = uno::Any();
370 std::optional
<PropertyMap::Property
> aTableStyleVal
= m_aTableProperties
->getProperty(META_PROP_TABLE_STYLE_NAME
);
373 // Apply table style properties recursively
374 OUString sTableStyleName
;
375 aTableStyleVal
->second
>>= sTableStyleName
;
376 StyleSheetTablePtr pStyleSheetTable
= m_rDMapper_Impl
.GetStyleSheetTable();
377 const StyleSheetEntryPtr pStyleSheet
= pStyleSheetTable
->FindStyleSheetByISTD( sTableStyleName
);
378 pTableStyle
= dynamic_cast<TableStyleSheetEntry
*>( pStyleSheet
.get( ) );
379 m_aTableProperties
->Erase( aTableStyleVal
->first
);
381 aGrabBag
["TableStyleName"] <<= sTableStyleName
;
385 // First get the style properties, then the table ones
386 PropertyMapPtr
pTableProps( m_aTableProperties
.get() );
387 TablePropertyMapPtr
pEmptyProps( new TablePropertyMap
);
389 m_aTableProperties
= pEmptyProps
;
391 PropertyMapPtr pMergedProperties
= pStyleSheet
->GetMergedInheritedProperties(pStyleSheetTable
);
393 table::BorderLine2 aBorderLine
;
394 TableInfo rStyleInfo
;
395 if (lcl_extractTableBorderProperty(pMergedProperties
, PROP_TOP_BORDER
, rStyleInfo
, aBorderLine
))
397 aGrabBag
["TableStyleTopBorder"] <<= aBorderLine
;
399 if (lcl_extractTableBorderProperty(pMergedProperties
, PROP_BOTTOM_BORDER
, rStyleInfo
, aBorderLine
))
401 aGrabBag
["TableStyleBottomBorder"] <<= aBorderLine
;
403 if (lcl_extractTableBorderProperty(pMergedProperties
, PROP_LEFT_BORDER
, rStyleInfo
, aBorderLine
))
405 aGrabBag
["TableStyleLeftBorder"] <<= aBorderLine
;
407 if (lcl_extractTableBorderProperty(pMergedProperties
, PROP_RIGHT_BORDER
, rStyleInfo
, aBorderLine
))
409 aGrabBag
["TableStyleRightBorder"] <<= aBorderLine
;
413 TagLogger::getInstance().startElement("mergedProps");
414 if (pMergedProperties
)
415 pMergedProperties
->dumpXml();
416 TagLogger::getInstance().endElement();
419 m_aTableProperties
->InsertProps(pMergedProperties
);
420 m_aTableProperties
->InsertProps(pTableProps
);
423 TagLogger::getInstance().startElement("TableProperties");
424 m_aTableProperties
->dumpXml();
425 TagLogger::getInstance().endElement();
429 // apply tblHeader setting of the table style
430 PropertyMapPtr pHeaderStyleProps
= pTableStyle
->GetProperties(CNF_FIRST_ROW
);
431 if ( pHeaderStyleProps
->getProperty(PROP_HEADER_ROW_COUNT
) )
432 m_aTableProperties
->Insert(PROP_HEADER_ROW_COUNT
, uno::Any( sal_Int32(1)), false);
437 // This is the one preserving just all the table look attributes.
438 std::optional
<PropertyMap::Property
> oTableLook
= m_aTableProperties
->getProperty(META_PROP_TABLE_LOOK
);
441 aGrabBag
["TableStyleLook"] = oTableLook
->second
;
442 m_aTableProperties
->Erase(oTableLook
->first
);
445 // This is just the "val" attribute's numeric value.
446 const std::optional
<PropertyMap::Property
> aTblLook
= m_aTableProperties
->getProperty(PROP_TBL_LOOK
);
449 aTblLook
->second
>>= rInfo
.nTblLook
;
450 m_aTableProperties
->Erase( aTblLook
->first
);
453 // apply cell margin settings of the table style
454 const std::optional
<PropertyMap::Property
> oLeftMargin
= m_aTableProperties
->getProperty(META_PROP_CELL_MAR_LEFT
);
457 oLeftMargin
->second
>>= rInfo
.nLeftBorderDistance
;
458 m_aTableProperties
->Erase(oLeftMargin
->first
);
460 const std::optional
<PropertyMap::Property
> oRightMargin
= m_aTableProperties
->getProperty(META_PROP_CELL_MAR_RIGHT
);
463 oRightMargin
->second
>>= rInfo
.nRightBorderDistance
;
464 m_aTableProperties
->Erase(oRightMargin
->first
);
466 const std::optional
<PropertyMap::Property
> oTopMargin
= m_aTableProperties
->getProperty(META_PROP_CELL_MAR_TOP
);
469 oTopMargin
->second
>>= rInfo
.nTopBorderDistance
;
470 m_aTableProperties
->Erase(oTopMargin
->first
);
472 const std::optional
<PropertyMap::Property
> oBottomMargin
= m_aTableProperties
->getProperty(META_PROP_CELL_MAR_BOTTOM
);
475 oBottomMargin
->second
>>= rInfo
.nBottomBorderDistance
;
476 m_aTableProperties
->Erase(oBottomMargin
->first
);
479 // Set the table default attributes for the cells
480 rInfo
.pTableDefaults
->InsertProps(m_aTableProperties
.get());
483 TagLogger::getInstance().startElement("TableDefaults");
484 rInfo
.pTableDefaults
->dumpXml();
485 TagLogger::getInstance().endElement();
488 if (!aGrabBag
.empty())
490 m_aTableProperties
->Insert( PROP_TABLE_INTEROP_GRAB_BAG
, uno::Any( aGrabBag
.getAsConstPropertyValueList() ) );
493 std::optional
<PropertyMap::Property
> oLeftMarginFromStyle
= m_aTableProperties
->getProperty(PROP_LEFT_MARGIN
);
494 if (oLeftMarginFromStyle
)
496 oLeftMarginFromStyle
->second
>>= nLeftMargin
;
497 // don't need to erase, we will push back the adjusted value
498 // of this (or the direct formatting, if that exists) later
500 m_aTableProperties
->getValue( TablePropertyMap::LEFT_MARGIN
, nLeftMargin
);
502 m_aTableProperties
->getValue( TablePropertyMap::CELL_MAR_LEFT
,
503 rInfo
.nLeftBorderDistance
);
504 m_aTableProperties
->getValue( TablePropertyMap::CELL_MAR_RIGHT
,
505 rInfo
.nRightBorderDistance
);
506 m_aTableProperties
->getValue( TablePropertyMap::CELL_MAR_TOP
,
507 rInfo
.nTopBorderDistance
);
508 m_aTableProperties
->getValue( TablePropertyMap::CELL_MAR_BOTTOM
,
509 rInfo
.nBottomBorderDistance
);
511 table::TableBorderDistances aDistances
;
512 aDistances
.IsTopDistanceValid
=
513 aDistances
.IsBottomDistanceValid
=
514 aDistances
.IsLeftDistanceValid
=
515 aDistances
.IsRightDistanceValid
= true;
516 aDistances
.TopDistance
= static_cast<sal_Int16
>( rInfo
.nTopBorderDistance
);
517 aDistances
.BottomDistance
= static_cast<sal_Int16
>( rInfo
.nBottomBorderDistance
);
518 aDistances
.LeftDistance
= static_cast<sal_Int16
>( rInfo
.nLeftBorderDistance
);
519 aDistances
.RightDistance
= static_cast<sal_Int16
>( rInfo
.nRightBorderDistance
);
521 m_aTableProperties
->Insert( PROP_TABLE_BORDER_DISTANCES
, uno::Any( aDistances
) );
523 sal_Int32 nMode
= m_rDMapper_Impl
.GetSettingsTable()->GetWordCompatibilityMode();
524 if (!rFrameProperties
.empty() && nMode
< 15)
525 lcl_DecrementHoriOrientPosition(rFrameProperties
, rInfo
.nLeftBorderDistance
);
527 // Set table above/bottom spacing to 0.
528 m_aTableProperties
->Insert( PROP_TOP_MARGIN
, uno::Any( sal_Int32( 0 ) ) );
529 m_aTableProperties
->Insert( PROP_BOTTOM_MARGIN
, uno::Any( sal_Int32( 0 ) ) );
531 //table border settings
532 table::TableBorder aTableBorder
;
533 table::BorderLine2 aBorderLine
, aLeftBorder
, aRightBorder
;
535 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), PROP_TOP_BORDER
, rInfo
, aBorderLine
))
537 aTableBorder
.TopLine
= aBorderLine
;
538 aTableBorder
.IsTopLineValid
= true;
540 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), PROP_BOTTOM_BORDER
, rInfo
, aBorderLine
))
542 aTableBorder
.BottomLine
= aBorderLine
;
543 aTableBorder
.IsBottomLineValid
= true;
545 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), PROP_LEFT_BORDER
, rInfo
, aLeftBorder
))
547 aTableBorder
.LeftLine
= aLeftBorder
;
548 aTableBorder
.IsLeftLineValid
= true;
550 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), PROP_RIGHT_BORDER
, rInfo
,
553 aTableBorder
.RightLine
= aRightBorder
;
554 aTableBorder
.IsRightLineValid
= true;
556 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), META_PROP_HORIZONTAL_BORDER
, rInfo
, aBorderLine
))
558 aTableBorder
.HorizontalLine
= aBorderLine
;
559 aTableBorder
.IsHorizontalLineValid
= true;
561 if (lcl_extractTableBorderProperty(m_aTableProperties
.get(), META_PROP_VERTICAL_BORDER
, rInfo
, aBorderLine
))
563 aTableBorder
.VerticalLine
= aBorderLine
;
564 aTableBorder
.IsVerticalLineValid
= true;
567 aTableBorder
.Distance
= 0;
568 aTableBorder
.IsDistanceValid
= false;
570 m_aTableProperties
->Insert( PROP_TABLE_BORDER
, uno::Any( aTableBorder
) );
573 lcl_debug_TableBorder(aTableBorder
);
576 // Table position in Office is computed in 2 different ways :
577 // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd),
578 // so table's position depends on table's cells margin
579 // - nested tables: the goal is to have left-most border starting at table_indent pos
581 // Only top level table position depends on border width of Column A.
582 if ( !m_aCellProperties
.empty() && !m_aCellProperties
[0].empty() )
584 // aLeftBorder already contains tblBorder; overwrite if cell is different.
585 std::optional
<PropertyMap::Property
> aCellBorder
586 = m_aCellProperties
[0][0]->getProperty(PROP_LEFT_BORDER
);
588 aCellBorder
->second
>>= aLeftBorder
;
589 aCellBorder
= m_aCellProperties
[0][0]->getProperty(PROP_RIGHT_BORDER
);
591 aCellBorder
->second
>>= aRightBorder
;
593 if (rInfo
.nNestLevel
== 1 && aLeftBorder
.LineWidth
&& !rFrameProperties
.empty())
595 lcl_DecrementHoriOrientPosition(rFrameProperties
, aLeftBorder
.LineWidth
* 0.5);
597 lcl_adjustBorderDistance(rInfo
, aLeftBorder
, aRightBorder
);
599 // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
600 // the default behavior when DOCX doesn't define "compatibilityMode" option is to add the cell spacing
602 if (0 < nMode
&& nMode
<= 14 && rInfo
.nNestLevel
== 1)
604 const sal_Int32 nAdjustedMargin
= nLeftMargin
- rInfo
.nLeftBorderDistance
;
605 m_aTableProperties
->Insert( PROP_LEFT_MARGIN
, uno::Any( nAdjustedMargin
) );
609 // Writer starts a table in the middle of the border.
610 // Word starts a table at the left edge of the border,
611 // so emulate that by adding the half the width. (also see docxattributeoutput)
612 if ( rInfo
.nNestLevel
> 1 && nLeftMargin
< 0 )
614 const sal_Int32 nAdjustedMargin
= nLeftMargin
+ (aLeftBorder
.LineWidth
/ 2);
615 m_aTableProperties
->Insert( PROP_LEFT_MARGIN
, uno::Any( nAdjustedMargin
) );
618 sal_Int32 nTableWidth
= 0;
619 sal_Int32 nTableWidthType
= text::SizeType::FIX
;
620 m_aTableProperties
->getValue( TablePropertyMap::TABLE_WIDTH
, nTableWidth
);
621 m_aTableProperties
->getValue( TablePropertyMap::TABLE_WIDTH_TYPE
, nTableWidthType
);
622 if( nTableWidthType
== text::SizeType::FIX
)
624 if( nTableWidth
> 0 )
625 m_aTableProperties
->Insert( PROP_WIDTH
, uno::Any( nTableWidth
));
628 // tdf#109524: If there is no width for the table, make it simply 100% by default.
629 // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87)
631 nTableWidthType
= text::SizeType::VARIABLE
;
634 if (nTableWidthType
!= text::SizeType::FIX
)
636 m_aTableProperties
->Insert( PROP_RELATIVE_WIDTH
, uno::Any( sal_Int16( nTableWidth
) ) );
637 m_aTableProperties
->Insert( PROP_IS_WIDTH_RELATIVE
, uno::Any( true ) );
640 sal_Int32 nHoriOrient
= text::HoriOrientation::LEFT_AND_WIDTH
;
641 // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties
642 if ( !m_aTableProperties
->getValue( TablePropertyMap::HORI_ORIENT
, nHoriOrient
) )
643 lcl_extractHoriOrient( rFrameProperties
, nHoriOrient
);
644 m_aTableProperties
->Insert( PROP_HORI_ORIENT
, uno::Any( sal_Int16(nHoriOrient
) ) );
645 //fill default value - if not available
646 m_aTableProperties
->Insert( PROP_HEADER_ROW_COUNT
, uno::Any( sal_Int32(0)), false);
647 m_aTableProperties
->Insert(PROP_WRITING_MODE
,
648 uno::Any(sal_Int16(text::WritingMode2::CONTEXT
)),
649 /*bOverWrite=*/false);
651 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
652 if( m_aRowProperties
.size() == 1 && m_aRowProperties
[0] )
654 std::optional
<PropertyMap::Property
> oSplitAllowed
= m_aRowProperties
[0]->getProperty(PROP_IS_SPLIT_ALLOWED
);
657 bool bRowCanSplit
= true;
658 oSplitAllowed
->second
>>= bRowCanSplit
;
660 m_aTableProperties
->Insert( PROP_SPLIT
, uno::Any(bRowCanSplit
) );
664 rInfo
.aTableProperties
= m_aTableProperties
->GetPropertyValues();
665 rInfo
.aTablePropertyIds
= m_aTableProperties
->GetPropertyIds();
668 TagLogger::getInstance().startElement("debug.tableprops");
669 m_aTableProperties
->dumpXml();
670 TagLogger::getInstance().endElement();
678 CellPropertyValuesSeq_t
DomainMapperTableHandler::endTableGetCellProperties(TableInfo
& rInfo
, std::vector
<HorizontallyMergedCell
>& rMerges
)
681 TagLogger::getInstance().startElement("getCellProperties");
684 CellPropertyValuesSeq_t
aCellProperties( m_aCellProperties
.size() );
686 if ( m_aCellProperties
.empty() )
689 TagLogger::getInstance().endElement();
691 return aCellProperties
;
693 // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties
694 PropertyMapVector2::const_iterator aRowOfCellsIterator
= m_aCellProperties
.begin();
695 PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd
= m_aCellProperties
.end();
696 PropertyMapVector2::const_iterator aLastRowIterator
= m_aCellProperties
.end() - 1;
699 css::uno::Sequence
<css::beans::PropertyValues
>* pCellProperties
= aCellProperties
.getArray();
700 PropertyMapVector1::const_iterator aRowIter
= m_aRowProperties
.begin();
701 while( aRowOfCellsIterator
!= aRowOfCellsIteratorEnd
)
703 //aRowOfCellsIterator points to a vector of PropertyMapPtr
704 PropertyMapVector1::const_iterator aCellIterator
= aRowOfCellsIterator
->begin();
705 PropertyMapVector1::const_iterator aCellIteratorEnd
= aRowOfCellsIterator
->end();
707 sal_Int32 nRowStyleMask
= 0;
709 if (aRowOfCellsIterator
==m_aCellProperties
.begin())
711 if(rInfo
.nTblLook
&0x20)
712 nRowStyleMask
|= CNF_FIRST_ROW
; // first row style used
714 else if (aRowOfCellsIterator
==aLastRowIterator
)
716 if(rInfo
.nTblLook
&0x40)
717 nRowStyleMask
|= CNF_LAST_ROW
; // last row style used
719 else if (*aRowIter
&& (*aRowIter
)->isSet(PROP_TBL_HEADER
))
720 nRowStyleMask
|= CNF_FIRST_ROW
; // table header implies first row
721 if(!nRowStyleMask
) // if no row style used yet
723 // banding used only if not first and or last row style used
724 if(!(rInfo
.nTblLook
&0x200))
727 if(rInfo
.nTblLook
&0x20)
730 nRowStyleMask
= CNF_ODD_HBAND
;
732 nRowStyleMask
= CNF_EVEN_HBAND
;
736 // Note that this is intentionally called "cell" and not "column".
737 // Don't make the mistake that all cell x's will be in the same column.
738 // Merged cells (grid span) in a row will affect the actual column. (fake cells were added to handle gridBefore/After)
740 pCellProperties
[nRow
].realloc( aRowOfCellsIterator
->size() );
741 beans::PropertyValues
* pSingleCellProperties
= pCellProperties
[nRow
].getArray();
743 while( aCellIterator
!= aCellIteratorEnd
)
745 PropertyMapPtr
pAllCellProps( new PropertyMap
);
747 PropertyMapVector1::const_iterator aLastCellIterator
= aRowOfCellsIterator
->end() - 1;
748 bool bIsEndCol
= aCellIterator
== aLastCellIterator
;
749 bool bIsEndRow
= aRowOfCellsIterator
== aLastRowIterator
;
751 //aCellIterator points to a PropertyMapPtr;
754 // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177)
755 (*aCellIterator
)->Erase(META_PROP_VERTICAL_BORDER
);
756 (*aCellIterator
)->Erase(META_PROP_HORIZONTAL_BORDER
);
758 pAllCellProps
->InsertProps(rInfo
.pTableDefaults
);
760 sal_Int32 nCellStyleMask
= 0;
761 if (aCellIterator
==aRowOfCellsIterator
->begin())
763 if(rInfo
.nTblLook
&0x80)
764 nCellStyleMask
= CNF_FIRST_COLUMN
; // first col style used
768 if(rInfo
.nTblLook
&0x100)
769 nCellStyleMask
= CNF_LAST_COLUMN
; // last col style used
771 if(!nCellStyleMask
) // if no cell style is used yet
773 if(!(rInfo
.nTblLook
&0x400))
776 if(rInfo
.nTblLook
&0x80)
779 nCellStyleMask
= CNF_ODD_VBAND
;
781 nCellStyleMask
= CNF_EVEN_VBAND
;
784 sal_Int32 nCnfStyleMask
= nCellStyleMask
+ nRowStyleMask
;
785 if(nCnfStyleMask
== CNF_FIRST_COLUMN
+ CNF_FIRST_ROW
)
786 nCnfStyleMask
|= CNF_FIRST_ROW_FIRST_COLUMN
;
787 else if(nCnfStyleMask
== CNF_FIRST_COLUMN
+ CNF_LAST_ROW
)
788 nCnfStyleMask
|= CNF_LAST_ROW_FIRST_COLUMN
;
789 else if(nCnfStyleMask
== CNF_LAST_COLUMN
+ CNF_FIRST_ROW
)
790 nCnfStyleMask
|= CNF_FIRST_ROW_LAST_COLUMN
;
791 else if(nCnfStyleMask
== CNF_LAST_COLUMN
+ CNF_LAST_ROW
)
792 nCnfStyleMask
|= CNF_LAST_ROW_LAST_COLUMN
;
794 if ( rInfo
.pTableStyle
)
796 PropertyMapPtr pStyleProps
= rInfo
.pTableStyle
->GetProperties( nCnfStyleMask
);
798 // Check if we need to clean up some empty border definitions to match what Word does.
799 static const PropertyIds pBorders
[] =
801 PROP_TOP_BORDER
, PROP_LEFT_BORDER
, PROP_BOTTOM_BORDER
, PROP_RIGHT_BORDER
803 for (const PropertyIds
& rBorder
: pBorders
)
805 std::optional
<PropertyMap::Property
> oStyleCellBorder
= pStyleProps
->getProperty(rBorder
);
806 std::optional
<PropertyMap::Property
> oDirectCellBorder
= (*aCellIterator
)->getProperty(rBorder
);
807 if (oStyleCellBorder
&& oDirectCellBorder
)
809 // We have a cell border from the table style and as direct formatting as well.
810 table::BorderLine2 aStyleCellBorder
= oStyleCellBorder
->second
.get
<table::BorderLine2
>();
811 table::BorderLine2 aDirectCellBorder
= oDirectCellBorder
->second
.get
<table::BorderLine2
>();
812 if (aStyleCellBorder
.LineStyle
!= table::BorderLineStyle::NONE
&& aDirectCellBorder
.LineStyle
== table::BorderLineStyle::NONE
)
814 // The style one would be visible, but then cleared away as direct formatting.
815 // Delete both, so that table formatting can become visible.
816 pStyleProps
->Erase(rBorder
);
817 (*aCellIterator
)->Erase(rBorder
);
821 std::optional
<PropertyMap::Property
> oTableBorder
= rInfo
.pTableBorders
->getProperty(rBorder
);
824 table::BorderLine2 aTableBorder
= oTableBorder
->second
.get
<table::BorderLine2
>();
825 // Both style and direct formatting says that the cell has no border.
826 bool bNoCellBorder
= aStyleCellBorder
.LineStyle
== table::BorderLineStyle::NONE
&& aDirectCellBorder
.LineStyle
== table::BorderLineStyle::NONE
;
827 if (aTableBorder
.LineStyle
!= table::BorderLineStyle::NONE
&& bNoCellBorder
)
829 // But at a table-level, there is a border, then again delete both cell properties.
830 pStyleProps
->Erase(rBorder
);
831 (*aCellIterator
)->Erase(rBorder
);
838 pAllCellProps
->InsertProps( pStyleProps
);
841 // Remove properties from style/row that aren't allowed in cells
842 pAllCellProps
->Erase( PROP_HEADER_ROW_COUNT
);
843 pAllCellProps
->Erase( PROP_TBL_HEADER
);
845 // Then add the cell properties
846 pAllCellProps
->InsertProps(*aCellIterator
);
847 std::swap(*(*aCellIterator
), *pAllCellProps
);
850 TagLogger::getInstance().startElement("cell");
851 TagLogger::getInstance().attribute("cell", nCell
);
852 TagLogger::getInstance().attribute("row", nRow
);
855 // Do not apply horizontal and vertical borders to a one cell table.
856 if (m_aCellProperties
.size() <= 1 && aRowOfCellsIterator
->size() <= 1)
858 rInfo
.pTableBorders
->Erase(META_PROP_HORIZONTAL_BORDER
);
859 rInfo
.pTableBorders
->Erase(META_PROP_VERTICAL_BORDER
);
861 // Do not apply vertical borders to a one column table.
862 else if (m_aCellProperties
.size() > 1 && aRowOfCellsIterator
->size() <= 1)
864 bool isOneCol
= true;
865 for (size_t i
= nRow
; i
< m_aCellProperties
.size(); i
++)
867 if (m_aCellProperties
[i
].size() > 1)
874 rInfo
.pTableBorders
->Erase(META_PROP_VERTICAL_BORDER
);
876 // Do not apply horizontal borders to a one row table.
877 else if (m_aCellProperties
.size() == 1 && aRowOfCellsIterator
->size() > 1)
879 rInfo
.pTableBorders
->Erase(META_PROP_HORIZONTAL_BORDER
);
882 // tdf#129452 Checking if current cell is vertically merged with all the other cells below to the bottom.
883 // This must be done in order to apply the bottom border of the table to the first cell in a vertical merge.
884 std::optional
<PropertyMap::Property
> oProp
= m_aCellProperties
[nRow
][nCell
]->getProperty(PROP_VERTICAL_MERGE
);
885 bool bMergedVertically
= oProp
&& oProp
->second
.get
<bool>(); // starting cell
886 if ( bMergedVertically
)
888 const sal_uInt32 nColumn
= m_rDMapper_Impl
.getTableManager().findColumn(nRow
, nCell
);
889 sal_Int32 nLastMergedRow
= 0;
890 for (size_t i
= nRow
+ 1; bMergedVertically
&& i
< m_aCellProperties
.size(); i
++)
892 const sal_uInt32 nColumnCell
= m_rDMapper_Impl
.getTableManager().findColumnCell(i
, nColumn
);
893 if ( m_aCellProperties
[i
].size() > sal::static_int_cast
<std::size_t>(nColumnCell
) )
895 oProp
= m_aCellProperties
[i
][nColumnCell
]->getProperty(PROP_VERTICAL_MERGE
);
896 bMergedVertically
= oProp
&& !oProp
->second
.get
<bool>(); //continuing cell
897 if ( bMergedVertically
)
901 bMergedVertically
= false;
904 // Only consider the bottom border setting from the last merged cell.
905 // Note: in MSO, left/right apply per-unmerged-row. Can't do that in LO, so just using the top cell's borders should be fine.
906 if ( nRow
< nLastMergedRow
)
908 (*aCellIterator
)->Erase(PROP_BOTTOM_BORDER
);
909 const sal_uInt32 nColumnCell
= m_rDMapper_Impl
.getTableManager().findColumnCell(nLastMergedRow
, nColumn
);
910 lcl_mergeBorder( PROP_BOTTOM_BORDER
, m_aCellProperties
[nLastMergedRow
][nColumnCell
], *aCellIterator
);
914 const sal_uInt32 nFirstCell
= m_rDMapper_Impl
.getTableManager().getGridBefore(nRow
);
915 const sal_uInt32 nLastCell
= m_aCellProperties
[nRow
].size() - m_rDMapper_Impl
.getTableManager().getGridAfter(nRow
) - 1;
916 lcl_computeCellBorders( rInfo
.pTableBorders
, *aCellIterator
, nCell
, nFirstCell
, nLastCell
, nRow
, bIsEndRow
, bMergedVertically
);
918 //now set the default left+right border distance TODO: there's an sprm containing the default distance!
919 aCellIterator
->get()->Insert( PROP_LEFT_BORDER_DISTANCE
,
920 uno::Any(rInfo
.nLeftBorderDistance
), false);
921 aCellIterator
->get()->Insert( PROP_RIGHT_BORDER_DISTANCE
,
922 uno::Any(rInfo
.nRightBorderDistance
), false);
923 aCellIterator
->get()->Insert( PROP_TOP_BORDER_DISTANCE
,
924 uno::Any(rInfo
.nTopBorderDistance
), false);
925 aCellIterator
->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE
,
926 uno::Any(rInfo
.nBottomBorderDistance
), false);
928 // Horizontal merge is not a UNO property, extract that info here to rMerges, and then remove it from the map.
929 const std::optional
<PropertyMap::Property
> aHorizontalMergeVal
= (*aCellIterator
)->getProperty(PROP_HORIZONTAL_MERGE
);
930 if (aHorizontalMergeVal
)
932 if (aHorizontalMergeVal
->second
.get
<bool>())
934 // first cell in a merge
935 HorizontallyMergedCell
aMerge(nRow
, nCell
);
936 rMerges
.push_back(aMerge
);
938 else if (!rMerges
.empty())
940 // resuming an earlier merge
941 HorizontallyMergedCell
& rMerge
= rMerges
.back();
942 rMerge
.m_nLastRow
= nRow
;
943 rMerge
.m_nLastCol
= nCell
;
945 (*aCellIterator
)->Erase(PROP_HORIZONTAL_MERGE
);
947 pSingleCellProperties
[nCell
] = (*aCellIterator
)->GetPropertyValues();
949 TagLogger::getInstance().endElement();
956 ++aRowOfCellsIterator
;
961 TagLogger::getInstance().endElement();
964 return aCellProperties
;
967 /// Do all cells in this row have a CellHideMark property?
968 static bool lcl_hideMarks(PropertyMapVector1
& rCellProperties
)
970 for (const PropertyMapPtr
& p
: rCellProperties
)
972 // if anything is vertically merged, the row must not be set to fixed
973 // as Writer's layout doesn't handle that well
974 if (!p
->isSet(PROP_CELL_HIDE_MARK
) || p
->isSet(PROP_VERTICAL_MERGE
))
980 /// Are all cells in this row empty?
981 static bool lcl_emptyRow(std::vector
<RowSequence_t
>& rTableRanges
, sal_Int32 nRow
)
983 if (nRow
>= static_cast<sal_Int32
>(rTableRanges
.size()))
985 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
989 const RowSequence_t rRowSeq
= rTableRanges
[nRow
];
990 if (!rRowSeq
.hasElements())
992 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
996 if (!rRowSeq
[0][0].is())
998 // This can happen when we can't import the table, e.g. we're inside a
1000 SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
1004 uno::Reference
<text::XTextRangeCompare
> xTextRangeCompare(rRowSeq
[0][0]->getText(), uno::UNO_QUERY
);
1007 // See SwXText::Impl::ConvertCell(), we need to compare the start of
1008 // the start and the end of the end. However for our text ranges, only
1009 // the starts are set, so compareRegionStarts() does what we need.
1010 bool bRangesAreNotEqual
= std::any_of(rRowSeq
.begin(), rRowSeq
.end(),
1011 [&xTextRangeCompare
](const CellSequence_t
& rCellSeq
) {
1012 return xTextRangeCompare
->compareRegionStarts(rCellSeq
[0], rCellSeq
[1]) != 0; });
1013 if (bRangesAreNotEqual
)
1016 catch (const lang::IllegalArgumentException
&)
1018 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "compareRegionStarts() failed");
1024 css::uno::Sequence
<css::beans::PropertyValues
> DomainMapperTableHandler::endTableGetRowProperties()
1027 TagLogger::getInstance().startElement("getRowProperties");
1030 css::uno::Sequence
<css::beans::PropertyValues
> aRowProperties( m_aRowProperties
.size() );
1031 auto aRowPropertiesRange
= asNonConstRange(aRowProperties
);
1033 for( const auto& rRow
: m_aRowProperties
)
1036 TagLogger::getInstance().startElement("rowProps.row");
1040 //set default to 'break across pages"
1041 rRow
->Insert( PROP_IS_SPLIT_ALLOWED
, uno::Any(true ), false );
1042 // tblHeader is only our property, remove before the property map hits UNO
1043 rRow
->Erase(PROP_TBL_HEADER
);
1045 if (lcl_hideMarks(m_aCellProperties
[nRow
]) && lcl_emptyRow(m_aTableRanges
, nRow
))
1047 // We have CellHideMark on all cells, and also all cells are empty:
1048 // Force the row height to be exactly as specified, and not just as the minimum suggestion.
1049 rRow
->Insert(PROP_SIZE_TYPE
, uno::Any(text::SizeType::FIX
));
1052 aRowPropertiesRange
[nRow
] = rRow
->GetPropertyValues();
1055 lcl_DumpPropertyValues(aRowProperties
[nRow
]);
1060 TagLogger::getInstance().endElement();
1065 TagLogger::getInstance().endElement();
1068 return aRowProperties
;
1071 static bool isAbsent(const std::vector
<beans::PropertyValue
>& propvals
, const OUString
& name
)
1073 return std::find_if(propvals
.begin(), propvals
.end(),
1074 [&name
](const beans::PropertyValue
& propval
)
1075 { return propval
.Name
== name
; })
1079 // table style has got bigger precedence than docDefault style,
1080 // but lower precedence than the paragraph styles and direct paragraph formatting
1081 void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp
, std::vector
< PropertyIds
> aAllTableParaProperties
, const css::beans::PropertyValues rCellProperties
)
1083 // Setting paragraph or character properties using setPropertyValue may have unwanted
1084 // side effects; e.g., setting a paragraph's font size can reset font size in a runs
1085 // of the paragraph, which have own formatting, which should have highest precedence.
1086 // Thus we have to collect property values, construct an autostyle, and assign it to
1087 // the paragraph, to avoid such side effects.
1089 // 1. Collect all the table-style-defined properties, that aren't overridden by the
1090 // paragraph style or direct formatting
1091 std::vector
<beans::PropertyValue
> aProps
;
1093 for( auto const& eId
: aAllTableParaProperties
)
1095 // apply paragraph and character properties of the table style on table paragraphs
1096 // if there is no direct paragraph formatting
1097 bool bIsParaLevel
= rParaProp
.m_pPropertyMap
->isSet(eId
);
1098 if ( !bIsParaLevel
|| isCharacterProperty(eId
) )
1100 if ( (eId
== PROP_PARA_LEFT_MARGIN
|| eId
== PROP_PARA_FIRST_LINE_INDENT
) &&
1101 rParaProp
.m_pPropertyMap
->isSet(PROP_NUMBERING_RULES
) )
1103 // indentation of direct numbering has bigger precedence, than table style
1107 OUString sPropertyName
= getPropertyName(eId
);
1109 auto pCellProp
= std::find_if(rCellProperties
.begin(), rCellProperties
.end(),
1110 [&](const beans::PropertyValue
& rProp
) { return rProp
.Name
== sPropertyName
; });
1111 // this cell applies the table style property
1112 if (pCellProp
!= rCellProperties
.end())
1115 // handle paragraph background color defined in CellColorHandler
1116 if (eId
== PROP_FILL_COLOR
)
1118 // table style defines paragraph background color, use the correct property name
1119 auto pFillStyleProp
= std::find_if(rCellProperties
.begin(), rCellProperties
.end(),
1120 [](const beans::PropertyValue
& rProp
) { return rProp
.Name
== "FillStyle"; });
1121 if ( pFillStyleProp
!= rCellProperties
.end() &&
1122 pFillStyleProp
->Value
== uno::Any(drawing::FillStyle_SOLID
) )
1124 sPropertyName
= "ParaBackColor";
1128 // FillStyle_NONE, skip table style usage for paragraph background color
1132 OUString sParaStyleName
;
1133 rParaProp
.m_rPropertySet
->getPropertyValue("ParaStyleName") >>= sParaStyleName
;
1134 StyleSheetEntryPtr pEntry
= m_rDMapper_Impl
.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName
);
1135 uno::Any aParaStyle
= m_rDMapper_Impl
.GetPropertyFromStyleSheet(eId
, pEntry
, true, true, &bDocDefault
);
1136 // A very strange compatibility rule says that the DEFAULT style's specified fontsize of 11 or 12
1137 // or a specified left justify will always be overridden by the table-style.
1138 // Normally this rule is applied, so always do this unless a compatSetting indicates otherwise.
1139 bool bCompatOverride
= false;
1140 if ( (eId
== PROP_CHAR_HEIGHT
|| eId
== PROP_PARA_ADJUST
) && sParaStyleName
== m_rDMapper_Impl
.GetDefaultParaStyleName() )
1142 if ( eId
== PROP_CHAR_HEIGHT
)
1143 bCompatOverride
= aParaStyle
== uno::Any(double(11)) || aParaStyle
== uno::Any(double(12));
1144 else if ( eId
== PROP_PARA_ADJUST
)
1146 style::ParagraphAdjust
eAdjust(style::ParagraphAdjust_CENTER
);
1147 aParaStyle
>>= eAdjust
;
1148 bCompatOverride
= eAdjust
== style::ParagraphAdjust_LEFT
;
1151 // The wording is confusing here. Normally, the paragraph style DOES override the table-style.
1152 // But for these two special situations, do not override the table-style. So the default is false.
1153 // If false, then "CompatOverride" the normal behaviour, and apply the table-style's value.
1154 bCompatOverride
&= !m_rDMapper_Impl
.GetSettingsTable()->GetCompatSettingValue(u
"overrideTableStyleFontSizeAndJustification");
1157 // use table style when no paragraph style setting or a docDefault value is applied instead of it
1158 if ( aParaStyle
== uno::Any() || bDocDefault
|| bCompatOverride
) try
1160 uno::Reference
<text::XParagraphCursor
> xParagraph(
1161 rParaProp
.m_rEndParagraph
->getText()->createTextCursorByRange(rParaProp
.m_rEndParagraph
), uno::UNO_QUERY_THROW
);
1163 xParagraph
->gotoStartOfParagraph( true );
1164 // don't overwrite empty paragraph with table style, if it has a direct paragraph formatting
1165 if ( bIsParaLevel
&& xParagraph
->getString().getLength() == 0 )
1168 if ( eId
!= PROP_FILL_COLOR
)
1170 // apply style setting when the paragraph doesn't modify it
1171 aProps
.push_back(comphelper::makePropertyValue(sPropertyName
, pCellProp
->Value
));
1175 // we need this for complete import of table-style based paragraph background color
1176 aProps
.push_back(comphelper::makePropertyValue("FillColor", pCellProp
->Value
));
1177 aProps
.push_back(comphelper::makePropertyValue("FillStyle", uno::Any(drawing::FillStyle_SOLID
)));
1180 catch ( const uno::Exception
& )
1182 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table style correction");
1188 if (!aProps
.empty())
1190 // 2. Get all properties directly defined in the paragraph
1191 uno::Reference
<beans::XPropertySetInfo
> xPropSetInfo(
1192 rParaProp
.m_rPropertySet
->getPropertySetInfo(), uno::UNO_SET_THROW
);
1193 auto props
= xPropSetInfo
->getProperties();
1194 uno::Sequence
<OUString
> propNames(props
.getLength());
1195 std::transform(props
.begin(), props
.end(), propNames
.getArray(),
1196 [](const beans::Property
& prop
) { return prop
.Name
; });
1197 uno::Reference
<beans::XTolerantMultiPropertySet
> xTolPara(rParaProp
.m_rPropertySet
,
1198 uno::UNO_QUERY_THROW
);
1199 // getDirectPropertyValuesTolerant requires a sorted sequence.
1200 // Let's hope XPropertySetInfo::getProperties returns a sorted sequence.
1201 for (auto& val
: xTolPara
->getDirectPropertyValuesTolerant(propNames
))
1203 // 3. Add them to aProps, unless such properties are already there
1204 // (which means, that 'val' comes from docDefault)
1205 if (val
.Result
== beans::TolerantPropertySetResultType::SUCCESS
1206 && val
.State
== beans::PropertyState_DIRECT_VALUE
1207 && isAbsent(aProps
, val
.Name
))
1209 aProps
.push_back(comphelper::makePropertyValue(val
.Name
, val
.Value
));
1213 // 4. Create an autostyle, and assign it to the paragraph. The hidden ParaAutoStyleDef
1214 // property is handled in SwXTextCursor::setPropertyValue.
1215 uno::Reference
<beans::XPropertySet
> xCursorProps(
1216 rParaProp
.m_rEndParagraph
->getText()->createTextCursorByRange(
1217 rParaProp
.m_rEndParagraph
),
1218 uno::UNO_QUERY_THROW
);
1219 xCursorProps
->setPropertyValue("ParaAutoStyleDef",
1220 uno::Any(comphelper::containerToSequence(aProps
)));
1224 // convert formula range identifier ABOVE, BELOW, LEFT and RIGHT
1225 static void lcl_convertFormulaRanges(const uno::Reference
<text::XTextTable
> & xTable
)
1227 uno::Reference
<table::XCellRange
> xCellRange(xTable
, uno::UNO_QUERY_THROW
);
1228 uno::Reference
<container::XIndexAccess
> xTableRows(xTable
->getRows(), uno::UNO_QUERY_THROW
);
1229 sal_Int32 nRows
= xTableRows
->getCount();
1230 for (sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
1232 for (sal_Int16 nCol
= 0; nCol
< MAXTABLECELLS
; ++nCol
)
1236 uno::Reference
<beans::XPropertySet
> xCellProperties(xCellRange
->getCellByPosition(nCol
, nRow
), uno::UNO_QUERY_THROW
);
1237 uno::Sequence
<beans::PropertyValue
> aCellGrabBag
;
1238 xCellProperties
->getPropertyValue("CellInteropGrabBag") >>= aCellGrabBag
;
1240 bool bReplace
= false;
1241 for (const auto& rProp
: std::as_const(aCellGrabBag
))
1243 if ( rProp
.Name
== "CellFormulaConverted" )
1245 rProp
.Value
>>= sFormula
;
1246 struct RangeDirection
1252 static const RangeDirection pDirections
[] =
1254 { OUString(" LEFT "), -1, 0},
1255 { OUString(" RIGHT "), 1, 0},
1256 { OUString(" ABOVE "), 0, -1},
1257 { OUString(" BELOW "), 0, 1 }
1259 for (const RangeDirection
& rRange
: pDirections
)
1261 if ( sFormula
.indexOf(rRange
.m_sName
) > -1 )
1263 // range starts at the first cell above/below/left/right, but ends at the
1264 // table border or at the first non-value cell after a value cell
1265 bool bFoundFirst
= false;
1268 OUString sLastValueCell
;
1269 // walk through the cells of the range
1272 sal_Int32 nCell
= 0;
1275 uno::Reference
<beans::XPropertySet
> xCell(
1276 xCellRange
->getCellByPosition(nCol
+ nCell
* rRange
.m_nCol
, nRow
+ nCell
* rRange
.m_nRow
),
1277 uno::UNO_QUERY_THROW
);
1278 // empty cell or cell with text content is end of the range
1279 uno::Reference
<text::XText
> xText(xCell
, uno::UNO_QUERY_THROW
);
1280 sLastCell
= xCell
->getPropertyValue("CellName").get
<OUString
>();
1281 if (sNextCell
.isEmpty())
1282 sNextCell
= sLastCell
;
1284 // accept numbers with comma and percent
1285 OUString sCellText
= xText
->getString().replace(',', '.');
1286 if (sCellText
.endsWith("%"))
1287 sCellText
= sCellText
.copy(0, sCellText
.getLength()-1);
1289 rtl_math_ConversionStatus eConversionStatus
;
1290 sal_Int32 nParsedEnd
;
1291 rtl::math::stringToDouble(sCellText
, '.', ',', &eConversionStatus
, &nParsedEnd
);
1292 if ( eConversionStatus
!= rtl_math_ConversionStatus_Ok
|| nParsedEnd
== 0 )
1296 // still search value cells
1305 sLastValueCell
= sLastCell
;
1309 catch ( const lang::IndexOutOfBoundsException
& )
1313 if ( !sNextCell
.isEmpty() )
1315 OUString sRange
= "<" + sNextCell
+ ":" +
1316 ( sLastValueCell
.isEmpty() ? sLastCell
: sLastValueCell
) + ">";
1317 sFormula
= sFormula
.replaceAll(rRange
.m_sName
, sRange
);
1323 // update formula field
1326 uno::Reference
<text::XText
> xCell(xCellRange
->getCellByPosition(nCol
, nRow
), uno::UNO_QUERY
);
1327 uno::Reference
<container::XEnumerationAccess
> xParaEnumAccess(xCell
, uno::UNO_QUERY
);
1328 uno::Reference
<container::XEnumeration
> xParaEnum
= xParaEnumAccess
->createEnumeration();
1329 uno::Reference
<container::XEnumerationAccess
> xRunEnumAccess(xParaEnum
->nextElement(), uno::UNO_QUERY
);
1330 uno::Reference
<container::XEnumeration
> xRunEnum
= xRunEnumAccess
->createEnumeration();
1331 while ( xRunEnum
->hasMoreElements() )
1333 uno::Reference
<text::XTextRange
> xRun(xRunEnum
->nextElement(), uno::UNO_QUERY
);
1334 uno::Reference
< beans::XPropertySet
> xRunProperties( xRun
, uno::UNO_QUERY_THROW
);
1335 if ( xRunProperties
->getPropertyValue("TextPortionType") == uno::Any(OUString("TextField")) )
1337 uno::Reference
<text::XTextField
> const xField(xRunProperties
->getPropertyValue("TextField").get
<uno::Reference
<text::XTextField
>>());
1338 uno::Reference
< beans::XPropertySet
> xFieldProperties( xField
, uno::UNO_QUERY_THROW
);
1339 // cell can contain multiple text fields, but only one is handled now (~formula cell)
1340 if ( rProp
.Value
!= xFieldProperties
->getPropertyValue("Content") )
1342 xFieldProperties
->setPropertyValue("Content", uno::Any(sFormula
));
1344 auto aGrabBag
= comphelper::sequenceToContainer
< std::vector
<beans::PropertyValue
> >(aCellGrabBag
);
1345 beans::PropertyValue aValue
;
1346 aValue
.Name
= "CellFormulaConverted";
1347 aValue
.Value
<<= sFormula
;
1348 aGrabBag
.push_back(aValue
);
1349 xCellProperties
->setPropertyValue("CellInteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag
)));
1356 catch ( const lang::IndexOutOfBoundsException
& )
1358 // jump to next table row
1365 void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel
)
1368 TagLogger::getInstance().startElement("tablehandler.endTable");
1371 // If we want to make this table a floating one.
1372 std::vector
<beans::PropertyValue
> aFrameProperties
= comphelper::sequenceToContainer
<std::vector
<beans::PropertyValue
> >
1373 (m_rDMapper_Impl
.getTableManager().getCurrentTablePosition());
1374 TableInfo aTableInfo
;
1375 aTableInfo
.nNestLevel
= nestedTableLevel
;
1377 // non-floating tables need floating in footnotes and endnotes, because
1378 // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise
1379 bool bConvertToFloating
= aFrameProperties
.empty() &&
1380 nestedTableLevel
<= 1 &&
1381 m_rDMapper_Impl
.IsInFootOrEndnote();
1382 bool bFloating
= !aFrameProperties
.empty() || bConvertToFloating
;
1384 aTableInfo
.pTableStyle
= endTableGetTableStyle(aTableInfo
, aFrameProperties
, bConvertToFloating
);
1385 // expands to uno::Sequence< Sequence< beans::PropertyValues > >
1387 std::vector
<HorizontallyMergedCell
> aMerges
;
1388 CellPropertyValuesSeq_t aCellProperties
= endTableGetCellProperties(aTableInfo
, aMerges
);
1390 css::uno::Sequence
<css::beans::PropertyValues
> aRowProperties
= endTableGetRowProperties();
1393 lcl_DumpPropertyValueSeq(aRowProperties
);
1396 if (!m_aTableRanges
.empty())
1398 uno::Reference
<text::XTextRange
> xStart
;
1399 uno::Reference
<text::XTextRange
> xEnd
;
1401 // fill empty frame properties to create an invisible frame around the table:
1402 // hide frame borders and zero inner and outer frame margins
1403 if (bConvertToFloating
)
1404 DomainMapper_Impl::fillEmptyFrameProperties(aFrameProperties
, true);
1406 // OOXML table style may contain paragraph properties, apply these on cell paragraphs
1407 if ( m_aTableRanges
[0].hasElements() && m_aTableRanges
[0][0].hasElements() )
1409 // collect all paragraph properties used in table styles
1410 PropertyMapPtr
pAllTableProps( new PropertyMap
);
1411 pAllTableProps
->InsertProps(aTableInfo
.pTableDefaults
);
1412 if ( aTableInfo
.pTableStyle
)
1413 pAllTableProps
->InsertProps(aTableInfo
.pTableStyle
->GetProperties( CNF_ALL
));
1414 for (const auto& eId
: pAllTableProps
->GetPropertyIds())
1416 if ( !isParagraphProperty(eId
) && !isCharacterProperty(eId
) )
1417 pAllTableProps
->Erase(eId
);
1419 std::vector
< PropertyIds
> aAllTableParaProperties
= pAllTableProps
->GetPropertyIds();
1421 if ( !aAllTableParaProperties
.empty() )
1423 TableParagraphVectorPtr pTableParagraphs
= m_rDMapper_Impl
.getTableManager().getCurrentParagraphs();
1424 for (size_t nRow
= 0; nRow
< m_aTableRanges
.size(); ++nRow
)
1426 // Note that this is "cell" since you must not treat it as "column".
1427 for (size_t nCell
= 0; nCell
< m_aTableRanges
[nRow
].size(); ++nCell
)
1429 auto rStartPara
= m_aTableRanges
[nRow
][nCell
][0];
1430 if (!rStartPara
.is())
1432 auto rEndPara
= m_aTableRanges
[nRow
][nCell
][1];
1433 uno::Reference
<text::XTextRangeCompare
> xTextRangeCompare(rStartPara
->getText(), uno::UNO_QUERY
);
1434 bool bApply
= false;
1435 // search paragraphs of the cell
1436 std::vector
<TableParagraph
>::iterator aIt
= pTableParagraphs
->begin();
1437 while ( aIt
!= pTableParagraphs
->end() ) try
1439 if (!bApply
&& xTextRangeCompare
->compareRegionStarts(rStartPara
, aIt
->m_rStartParagraph
) == 0)
1443 bool bEndOfApply
= (xTextRangeCompare
->compareRegionEnds(rEndPara
, aIt
->m_rEndParagraph
) == 0);
1444 // tdf#153891 handle missing cell properties (exception in style handling?)
1445 if ( nCell
< sal::static_int_cast
<std::size_t>(aCellProperties
[nRow
].getLength()) )
1446 ApplyParagraphPropertiesFromTableStyle(*aIt
, aAllTableParaProperties
, aCellProperties
[nRow
][nCell
]);
1447 // erase processed paragraph from list of pending paragraphs
1448 aIt
= pTableParagraphs
->erase(aIt
);
1455 catch( const lang::IllegalArgumentException
& )
1457 // skip compareRegion with nested tables
1465 // Additional checks: if we can do this.
1466 if (bFloating
&& m_aTableRanges
[0].hasElements() && m_aTableRanges
[0][0].hasElements())
1468 xStart
= m_aTableRanges
[0][0][0];
1469 uno::Sequence
< uno::Sequence
< uno::Reference
<text::XTextRange
> > >& rLastRow
= m_aTableRanges
[m_aTableRanges
.size() - 1];
1470 if (rLastRow
.hasElements())
1472 const uno::Sequence
< uno::Reference
<text::XTextRange
> >& rLastCell
= rLastRow
[rLastRow
.getLength() - 1];
1473 xEnd
= rLastCell
[1];
1476 uno::Reference
<text::XTextTable
> xTable
;
1481 xTable
= m_xText
->convertToTable(comphelper::containerToSequence(m_aTableRanges
), aCellProperties
, aRowProperties
, aTableInfo
.aTableProperties
);
1485 if (!aMerges
.empty())
1487 static const std::vector
<std::u16string_view
> aBorderNames
1488 = { u
"TopBorder", u
"LeftBorder", u
"BottomBorder", u
"RightBorder" };
1490 // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
1491 for (std::vector
<HorizontallyMergedCell
>::reverse_iterator it
= aMerges
.rbegin(); it
!= aMerges
.rend(); ++it
)
1493 uno::Reference
<table::XCellRange
> xCellRange(xTable
, uno::UNO_QUERY_THROW
);
1494 uno::Reference
<beans::XPropertySet
> xFirstCell(
1495 xCellRange
->getCellByPosition(it
->m_nFirstCol
, it
->m_nFirstRow
),
1496 uno::UNO_QUERY_THROW
);
1498 = xFirstCell
->getPropertyValue("CellName").get
<OUString
>();
1499 // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
1500 if (it
->m_nLastCol
!= -1)
1502 // Save border properties of the first cell
1504 table::BorderLine2 aBorderValues
[4];
1505 for (size_t i
= 0; i
< aBorderNames
.size(); ++i
)
1506 xFirstCell
->getPropertyValue(OUString(aBorderNames
[i
]))
1507 >>= aBorderValues
[i
];
1509 uno::Reference
<beans::XPropertySet
> xLastCell(
1510 xCellRange
->getCellByPosition(it
->m_nLastCol
, it
->m_nLastRow
),
1511 uno::UNO_QUERY_THROW
);
1513 = xLastCell
->getPropertyValue("CellName").get
<OUString
>();
1515 uno::Reference
<text::XTextTableCursor
> xCursor
= xTable
->createCursorByCellName(aFirst
);
1516 xCursor
->gotoCellByName(aLast
, true);
1518 xCursor
->mergeRange();
1520 // Handle conflicting properties: mergeRange()
1521 // takes the last cell, Word takes the first
1523 for (size_t i
= 0; i
< aBorderNames
.size(); ++i
)
1525 if (aBorderValues
[i
].LineStyle
!= table::BorderLineStyle::NONE
)
1526 xFirstCell
->setPropertyValue(
1527 OUString(aBorderNames
[i
]), uno::Any(aBorderValues
[i
]));
1533 // convert special range IDs ABOVE, BELOW, LEFT and RIGHT
1534 lcl_convertFormulaRanges(xTable
);
1538 catch ( const lang::IllegalArgumentException
& )
1540 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Conversion to table error");
1542 TagLogger::getInstance().chars(std::string("failed to import table!"));
1545 catch ( const uno::Exception
& )
1547 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table creation");
1550 // If we have a table with a start and an end position, we should make it a floating one.
1551 // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
1552 if (xTable
.is() && xStart
.is() && xEnd
.is() && !m_bHadFootOrEndnote
)
1554 uno::Reference
<beans::XPropertySet
> xTableProperties(xTable
, uno::UNO_QUERY
);
1555 bool bIsRelative
= false;
1556 xTableProperties
->getPropertyValue("IsWidthRelative") >>= bIsRelative
;
1559 beans::PropertyValue aValue
;
1560 aValue
.Name
= "Width";
1561 aValue
.Value
= xTableProperties
->getPropertyValue("Width");
1562 aFrameProperties
.push_back(aValue
);
1566 beans::PropertyValue aValue
;
1567 aValue
.Name
= "FrameWidthPercent";
1568 aValue
.Value
= xTableProperties
->getPropertyValue("RelativeWidth");
1569 aFrameProperties
.push_back(aValue
);
1571 // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
1572 xTableProperties
->setPropertyValue("RelativeWidth", uno::Any(sal_Int16(100)));
1575 // A non-zero left margin would move the table out of the frame, move the frame itself instead.
1576 xTableProperties
->setPropertyValue("LeftMargin", uno::Any(sal_Int32(0)));
1578 style::BreakType eBreakType
{};
1579 xTableProperties
->getPropertyValue("BreakType") >>= eBreakType
;
1580 if (eBreakType
!= style::BreakType_NONE
)
1582 // A break before the table was requested. Reset that break here, since the table
1583 // will be at the start of the fly frame, not in the body frame.
1584 xTableProperties
->setPropertyValue("BreakType", uno::Any(style::BreakType_NONE
));
1587 if (nestedTableLevel
>= 2 || m_rDMapper_Impl
.IsInHeaderFooter())
1589 // Floating tables inside a table always stay inside the cell.
1590 // Also extend the header/footer area if needed, so an in-header floating table
1591 // typically doesn't overlap with body test.
1592 aFrameProperties
.push_back(
1593 comphelper::makePropertyValue("IsFollowingTextFlow", true));
1596 // A text frame created for floating tables is always allowed to split.
1597 aFrameProperties
.push_back(comphelper::makePropertyValue("IsSplitAllowed", true));
1599 sal_Int32 nTableWidth
= 0;
1600 m_aTableProperties
->getValue(TablePropertyMap::TABLE_WIDTH
, nTableWidth
);
1601 sal_Int32 nTableWidthType
= text::SizeType::FIX
;
1602 m_aTableProperties
->getValue(TablePropertyMap::TABLE_WIDTH_TYPE
, nTableWidthType
);
1603 // 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.
1604 uno::Reference
<text::XTextAppendAndConvert
> xTextAppendAndConvert(m_rDMapper_Impl
.GetTopTextAppend(), uno::UNO_QUERY
);
1605 uno::Reference
<beans::XPropertySet
> xFrameAnchor
;
1606 if (xTextAppendAndConvert
.is())
1608 std::deque
<css::uno::Any
> aFramedRedlines
= m_rDMapper_Impl
.m_aStoredRedlines
[StoredRedlines::FRAME
];
1609 std::vector
<sal_Int32
> redPos
, redLen
;
1610 std::vector
<OUString
> redCell
;
1611 std::vector
<OUString
> redTable
;
1612 BeforeConvertToTextFrame(aFramedRedlines
, redPos
, redLen
, redCell
, redTable
);
1614 uno::Reference
<text::XTextContent
> xContent
= xTextAppendAndConvert
->convertToTextFrame(xStart
, xEnd
, comphelper::containerToSequence(aFrameProperties
));
1615 xFrameAnchor
.set(xContent
->getAnchor(), uno::UNO_QUERY
);
1617 bool bConvertToFloatingInFootnote
= false;
1618 if (xContent
.is() && xContent
->getAnchor().is())
1620 uno::Reference
<lang::XServiceInfo
> xText(xContent
->getAnchor()->getText(), uno::UNO_QUERY
);
1623 bConvertToFloatingInFootnote
= xText
->supportsService("com.sun.star.text.Footnote");
1627 // paragraph of the anchoring point of the floating table needs zero top and bottom
1628 // margins, if the table was a not floating table in the footnote, otherwise
1629 // docDefault margins could result bigger vertical spaces around the table
1630 if ( bConvertToFloatingInFootnote
&& xContent
.is() )
1632 uno::Reference
<beans::XPropertySet
> xParagraph(
1633 xContent
->getAnchor(), uno::UNO_QUERY
);
1634 if ( xParagraph
.is() )
1636 xParagraph
->setPropertyValue("ParaTopMargin",
1637 uno::Any(static_cast<sal_Int32
>(0)));
1638 xParagraph
->setPropertyValue("ParaBottomMargin",
1639 uno::Any(static_cast<sal_Int32
>(0)));
1645 // By the time the frame is created, the anchor's paragraph marker character
1646 // properties are already imported. Check if we need to disable "vanish", that
1647 // would lead to a hidden floating table in Writer, but it does not in Word.
1648 uno::Reference
<beans::XPropertySet
> xParagraph(xContent
->getAnchor(),
1650 if (xParagraph
.is())
1653 xParagraph
->getPropertyValue("CharHidden") >>= bCharHidden
;
1656 xParagraph
->setPropertyValue("CharHidden", uno::Any(false));
1661 AfterConvertToTextFrame(m_rDMapper_Impl
, aFramedRedlines
, redPos
, redLen
, redCell
, redTable
);
1664 if (xFrameAnchor
.is() && eBreakType
!= style::BreakType_NONE
)
1666 // A break before the table was requested. Restore that on the anchor.
1667 xFrameAnchor
->setPropertyValue("BreakType", uno::Any(eBreakType
));
1672 m_aTableProperties
.clear();
1673 m_aCellProperties
.clear();
1674 m_aRowProperties
.clear();
1675 m_bHadFootOrEndnote
= false;
1678 TagLogger::getInstance().endElement();
1679 TagLogger::getInstance().endElement();
1683 void DomainMapperTableHandler::startRow(const TablePropertyMapPtr
& pProps
)
1685 m_aRowProperties
.push_back( pProps
.get() );
1686 m_aCellProperties
.emplace_back( );
1689 TagLogger::getInstance().startElement("table.row");
1690 if (pProps
!= nullptr)
1694 m_aRowRanges
.clear();
1697 void DomainMapperTableHandler::endRow()
1699 m_aTableRanges
.push_back(comphelper::containerToSequence(m_aRowRanges
));
1701 TagLogger::getInstance().endElement();
1705 void DomainMapperTableHandler::startCell(const css::uno::Reference
< css::text::XTextRange
> & start
,
1706 const TablePropertyMapPtr
& pProps
)
1708 sal_uInt32 nRow
= m_aRowProperties
.size();
1710 m_aCellProperties
[nRow
- 1].push_back( pProps
.get() );
1713 // Adding an empty cell properties map to be able to get
1714 // the table defaults properties
1715 TablePropertyMapPtr
pEmptyProps( new TablePropertyMap( ) );
1716 m_aCellProperties
[nRow
- 1].push_back( pEmptyProps
.get() );
1720 TagLogger::getInstance().startElement("table.cell");
1721 TagLogger::getInstance().startElement("table.cell.start");
1722 TagLogger::getInstance().chars(XTextRangeToString(start
));
1723 TagLogger::getInstance().endElement();
1725 pProps
->printProperties();
1728 //add a new 'row' of properties
1729 m_aCellRange
.clear();
1730 uno::Reference
<text::XTextRange
> xStart
;
1732 xStart
= start
->getStart();
1733 m_aCellRange
.push_back(xStart
);
1736 void DomainMapperTableHandler::endCell(const css::uno::Reference
< css::text::XTextRange
> & end
)
1739 TagLogger::getInstance().startElement("table.cell.end");
1740 TagLogger::getInstance().chars(XTextRangeToString(end
));
1741 TagLogger::getInstance().endElement();
1742 TagLogger::getInstance().endElement();
1745 uno::Reference
<text::XTextRange
> xEnd
;
1747 xEnd
= end
->getEnd();
1748 m_aCellRange
.push_back(xEnd
);
1749 m_aRowRanges
.push_back(comphelper::containerToSequence(m_aCellRange
));
1752 void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote
)
1754 m_bHadFootOrEndnote
= bHadFootOrEndnote
;
1757 DomainMapper_Impl
& DomainMapperTableHandler::getDomainMapperImpl()
1759 return m_rDMapper_Impl
;
1764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */