LibreOffice Module writerfilter (master) 1
DomainMapperTableHandler.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
25#include "DomainMapper_Impl.hxx"
26#include "StyleSheetTable.hxx"
27
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>
48#include "TagLogger.hxx"
49#include "util.hxx"
50#include <osl/diagnose.h>
51#include <sal/log.hxx>
55#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
56#include <com/sun/star/style/BreakType.hpp>
57#include <officecfg/Office/Writer.hxx>
58
59#ifdef DBG_UTIL
60#include "PropertyMapHelper.hxx"
61#include <rtl/ustring.hxx>
62#include <utility>
63#endif
64
65namespace writerfilter::dmapper {
66
67using namespace ::com::sun::star;
68
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
82#define CNF_ALL 0xFFF
83
84// total number of table columns
85#define MAXTABLECELLS 63
86
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)
93{
94}
95
97{
98}
99
101{
102 m_aTableProperties = pProps;
103 m_aTableRanges.clear();
104
105#ifdef DBG_UTIL
106 TagLogger::getInstance().startElement("tablehandler.table");
107
108 if (pProps)
109 pProps->dumpXml();
110#endif
111}
112
113static void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest )
114{
115 std::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId);
116
117 if ( pOrigVal )
118 {
119 pDest->Insert( nId, pOrigVal->second, false );
120 }
121}
122
123static 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 )
125{
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);
130
131 // Handle the vertical and horizontal borders
132 uno::Any aVertProp;
133 if ( !pVerticalVal)
134 {
135 pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER);
136 if ( pVerticalVal )
137 aVertProp = pVerticalVal->second;
138 }
139 else
140 {
141 aVertProp = pVerticalVal->second;
142 pCellProps->Erase( pVerticalVal->first );
143 }
144
145 uno::Any aHorizProp;
146 if ( !pHorizontalVal )
147 {
148 pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER);
149 if ( pHorizontalVal )
150 aHorizProp = pHorizontalVal->second;
151 }
152 else
153 {
154 aHorizProp = pHorizontalVal->second;
155 pCellProps->Erase( pHorizontalVal->first );
156 }
157
158 if ( bIsStartCol )
159 lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps );
160
161 if ( bIsEndCol )
162 lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps );
163
164 // <w:insideV> counts if there are multiple cells in this row.
165 if ( pVerticalVal )
166 {
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 );
171 }
172
173 if ( nRow == 0 )
174 {
175 lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps );
176 if ( pHorizontalVal && !bMergedVertically )
177 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
178 }
179
180 if ( bMergedVertically )
181 lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
182
183 if ( bIsEndRow )
184 {
185 lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
186 if ( pHorizontalVal )
187 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
188 }
189
190 if ( nRow > 0 && !bIsEndRow )
191 {
192 if ( pHorizontalVal )
193 {
194 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
195 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
196 }
197 }
198}
199
200#ifdef DBG_UTIL
201
202static void lcl_debug_BorderLine(table::BorderLine const & rLine)
203{
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);
210}
211
212static void lcl_debug_TableBorder(table::TableBorder const & rBorder)
213{
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));
230}
231#endif
232
234{
239 sal_Int32 nTblLook;
240 sal_Int32 nNestLevel;
244 css::beans::PropertyValues aTableProperties;
245 std::vector< PropertyIds > aTablePropertyIds;
246
252 , nTblLook(0x4a0)
253 , nNestLevel(0)
256 , pTableStyle(nullptr)
257 {
258 }
259
260};
261
262namespace
263{
264
265bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine)
266{
267 if (!pTableProperties)
268 return false;
269
270 const std::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId);
271 if( aTblBorder )
272 {
273 OSL_VERIFY(aTblBorder->second >>= rLine);
274
275 rInfo.pTableBorders->Insert( nId, uno::Any( rLine ) );
276 rInfo.pTableDefaults->Erase( nId );
277
278 return true;
279 }
280
281 return false;
282}
283
284void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient)
285{
286 // Shifts the frame left by the given value.
287 for (const beans::PropertyValue & rFrameProperty : rFrameProperties)
288 {
289 if (rFrameProperty.Name == "HoriOrient")
290 {
291 sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>();
292 if (nValue != text::HoriOrientation::NONE)
293 nHoriOrient = nValue;
294 return;
295 }
296 }
297}
298
299void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount)
300{
301 // Shifts the frame left by the given value.
302 for (beans::PropertyValue & rPropertyValue : rFrameProperties)
303 {
304 if (rPropertyValue.Name == "HoriOrientPosition")
305 {
306 sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>();
307 nValue -= nAmount;
308 rPropertyValue.Value <<= nValue;
309 return;
310 }
311 }
312}
313
314void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftBorder,
315 const table::BorderLine2& rRightBorder)
316{
317 // MS Word appears to do these things to adjust the cell horizontal area:
318 //
319 // bll = left borderline width
320 // blr = right borderline width
321 // cea = cell's edit area rectangle
322 // cea_w = cea width
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)
328 //
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
332 //
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.
337
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)
343 - nActualL;
344 rInfo.nLeftBorderDistance = nActualL;
345 rInfo.nRightBorderDistance = nActualR;
346}
347
348}
349
351 std::vector<beans::PropertyValue>& rFrameProperties,
352 bool bConvertToFloatingInFootnote)
353{
354 // will receive the table style if any
355 TableStyleSheetEntry* pTableStyle = nullptr;
356
358 {
359 //create properties from the table attributes
360 sal_Int32 nLeftMargin = 0;
361
363
364 if (bConvertToFloatingInFootnote)
365 {
366 // define empty "TablePosition" to avoid export temporary floating
367 aGrabBag["TablePosition"] = uno::Any();
368 }
369
370 std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
371 if(aTableStyleVal)
372 {
373 // Apply table style properties recursively
374 OUString sTableStyleName;
375 aTableStyleVal->second >>= sTableStyleName;
377 const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName );
378 pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) );
379 m_aTableProperties->Erase( aTableStyleVal->first );
380
381 aGrabBag["TableStyleName"] <<= sTableStyleName;
382
383 if( pStyleSheet )
384 {
385 // First get the style properties, then the table ones
386 PropertyMapPtr pTableProps( m_aTableProperties.get() );
387 TablePropertyMapPtr pEmptyProps( new TablePropertyMap );
388
389 m_aTableProperties = pEmptyProps;
390
391 PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);
392
393 table::BorderLine2 aBorderLine;
394 TableInfo rStyleInfo;
395 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine))
396 {
397 aGrabBag["TableStyleTopBorder"] <<= aBorderLine;
398 }
399 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine))
400 {
401 aGrabBag["TableStyleBottomBorder"] <<= aBorderLine;
402 }
403 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine))
404 {
405 aGrabBag["TableStyleLeftBorder"] <<= aBorderLine;
406 }
407 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine))
408 {
409 aGrabBag["TableStyleRightBorder"] <<= aBorderLine;
410 }
411
412#ifdef DBG_UTIL
413 TagLogger::getInstance().startElement("mergedProps");
414 if (pMergedProperties)
415 pMergedProperties->dumpXml();
417#endif
418
419 m_aTableProperties->InsertProps(pMergedProperties);
420 m_aTableProperties->InsertProps(pTableProps);
421
422#ifdef DBG_UTIL
423 TagLogger::getInstance().startElement("TableProperties");
424 m_aTableProperties->dumpXml();
426#endif
427 if (pTableStyle)
428 {
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);
433 }
434 }
435 }
436
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);
439 if (oTableLook)
440 {
441 aGrabBag["TableStyleLook"] = oTableLook->second;
442 m_aTableProperties->Erase(oTableLook->first);
443 }
444
445 // This is just the "val" attribute's numeric value.
446 const std::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK);
447 if(aTblLook)
448 {
449 aTblLook->second >>= rInfo.nTblLook;
450 m_aTableProperties->Erase( aTblLook->first );
451 }
452
453 // apply cell margin settings of the table style
454 const std::optional<PropertyMap::Property> oLeftMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_LEFT);
455 if (oLeftMargin)
456 {
457 oLeftMargin->second >>= rInfo.nLeftBorderDistance;
458 m_aTableProperties->Erase(oLeftMargin->first);
459 }
460 const std::optional<PropertyMap::Property> oRightMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_RIGHT);
461 if (oRightMargin)
462 {
463 oRightMargin->second >>= rInfo.nRightBorderDistance;
464 m_aTableProperties->Erase(oRightMargin->first);
465 }
466 const std::optional<PropertyMap::Property> oTopMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_TOP);
467 if (oTopMargin)
468 {
469 oTopMargin->second >>= rInfo.nTopBorderDistance;
470 m_aTableProperties->Erase(oTopMargin->first);
471 }
472 const std::optional<PropertyMap::Property> oBottomMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_BOTTOM);
473 if (oBottomMargin)
474 {
475 oBottomMargin->second >>= rInfo.nBottomBorderDistance;
476 m_aTableProperties->Erase(oBottomMargin->first);
477 }
478
479 // Set the table default attributes for the cells
480 rInfo.pTableDefaults->InsertProps(m_aTableProperties.get());
481
482#ifdef DBG_UTIL
483 TagLogger::getInstance().startElement("TableDefaults");
484 rInfo.pTableDefaults->dumpXml();
486#endif
487
488 if (!aGrabBag.empty())
489 {
491 }
492
493 std::optional<PropertyMap::Property> oLeftMarginFromStyle = m_aTableProperties->getProperty(PROP_LEFT_MARGIN);
494 if (oLeftMarginFromStyle)
495 {
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
499 }
501
503 rInfo.nLeftBorderDistance );
505 rInfo.nRightBorderDistance );
507 rInfo.nTopBorderDistance );
509 rInfo.nBottomBorderDistance );
510
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 );
520
522
523 sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();
524 if (!rFrameProperties.empty() && nMode < 15)
525 lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance);
526
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 ) ) );
530
531 //table border settings
532 table::TableBorder aTableBorder;
533 table::BorderLine2 aBorderLine, aLeftBorder, aRightBorder;
534
535 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine))
536 {
537 aTableBorder.TopLine = aBorderLine;
538 aTableBorder.IsTopLineValid = true;
539 }
540 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine))
541 {
542 aTableBorder.BottomLine = aBorderLine;
543 aTableBorder.IsBottomLineValid = true;
544 }
545 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder))
546 {
547 aTableBorder.LeftLine = aLeftBorder;
548 aTableBorder.IsLeftLineValid = true;
549 }
550 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo,
551 aRightBorder))
552 {
553 aTableBorder.RightLine = aRightBorder;
554 aTableBorder.IsRightLineValid = true;
555 }
556 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine))
557 {
558 aTableBorder.HorizontalLine = aBorderLine;
559 aTableBorder.IsHorizontalLineValid = true;
560 }
561 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine))
562 {
563 aTableBorder.VerticalLine = aBorderLine;
564 aTableBorder.IsVerticalLineValid = true;
565 }
566
567 aTableBorder.Distance = 0;
568 aTableBorder.IsDistanceValid = false;
569
570 m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::Any( aTableBorder ) );
571
572#ifdef DBG_UTIL
573 lcl_debug_TableBorder(aTableBorder);
574#endif
575
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
580
581 // Only top level table position depends on border width of Column A.
582 if ( !m_aCellProperties.empty() && !m_aCellProperties[0].empty() )
583 {
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);
587 if ( aCellBorder )
588 aCellBorder->second >>= aLeftBorder;
589 aCellBorder = m_aCellProperties[0][0]->getProperty(PROP_RIGHT_BORDER);
590 if (aCellBorder)
591 aCellBorder->second >>= aRightBorder;
592 }
593 if (rInfo.nNestLevel == 1 && aLeftBorder.LineWidth && !rFrameProperties.empty())
594 {
595 lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5);
596 }
597 lcl_adjustBorderDistance(rInfo, aLeftBorder, aRightBorder);
598
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
601
602 if (0 < nMode && nMode <= 14 && rInfo.nNestLevel == 1)
603 {
604 const sal_Int32 nAdjustedMargin = nLeftMargin - rInfo.nLeftBorderDistance;
605 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::Any( nAdjustedMargin ) );
606 }
607 else
608 {
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 )
613 nLeftMargin = 0;
614 const sal_Int32 nAdjustedMargin = nLeftMargin + (aLeftBorder.LineWidth / 2);
615 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::Any( nAdjustedMargin ) );
616 }
617
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 )
623 {
624 if( nTableWidth > 0 )
625 m_aTableProperties->Insert( PROP_WIDTH, uno::Any( nTableWidth ));
626 else
627 {
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)
630 nTableWidth = 100;
631 nTableWidthType = text::SizeType::VARIABLE;
632 }
633 }
634 if (nTableWidthType != text::SizeType::FIX)
635 {
636 m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::Any( sal_Int16( nTableWidth ) ) );
638 }
639
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);
648 uno::Any(sal_Int16(text::WritingMode2::CONTEXT)),
649 /*bOverWrite=*/false);
650
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] )
653 {
654 std::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED);
655 if( oSplitAllowed )
656 {
657 bool bRowCanSplit = true;
658 oSplitAllowed->second >>= bRowCanSplit;
659 if( !bRowCanSplit )
660 m_aTableProperties->Insert( PROP_SPLIT, uno::Any(bRowCanSplit) );
661 }
662 }
663
664 rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();
665 rInfo.aTablePropertyIds = m_aTableProperties->GetPropertyIds();
666
667#ifdef DBG_UTIL
668 TagLogger::getInstance().startElement("debug.tableprops");
669 m_aTableProperties->dumpXml();
671#endif
672
673 }
674
675 return pTableStyle;
676}
677
679{
680#ifdef DBG_UTIL
681 TagLogger::getInstance().startElement("getCellProperties");
682#endif
683
684 CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() );
685
686 if ( m_aCellProperties.empty() )
687 {
688 #ifdef DBG_UTIL
690 #endif
691 return aCellProperties;
692 }
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;
697 sal_Int32 nRow = 0;
698
699 css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray();
700 PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
701 while( aRowOfCellsIterator != aRowOfCellsIteratorEnd )
702 {
703 //aRowOfCellsIterator points to a vector of PropertyMapPtr
704 PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin();
705 PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end();
706
707 sal_Int32 nRowStyleMask = 0;
708
709 if (aRowOfCellsIterator==m_aCellProperties.begin())
710 {
711 if(rInfo.nTblLook&0x20)
712 nRowStyleMask |= CNF_FIRST_ROW; // first row style used
713 }
714 else if (aRowOfCellsIterator==aLastRowIterator)
715 {
716 if(rInfo.nTblLook&0x40)
717 nRowStyleMask |= CNF_LAST_ROW; // last row style used
718 }
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
722 {
723 // banding used only if not first and or last row style used
724 if(!(rInfo.nTblLook&0x200))
725 { // hbanding used
726 int n = nRow + 1;
727 if(rInfo.nTblLook&0x20)
728 n++;
729 if(n & 1)
730 nRowStyleMask = CNF_ODD_HBAND;
731 else
732 nRowStyleMask = CNF_EVEN_HBAND;
733 }
734 }
735
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)
739 sal_Int32 nCell = 0;
740 pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
741 beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
742
743 while( aCellIterator != aCellIteratorEnd )
744 {
745 PropertyMapPtr pAllCellProps( new PropertyMap );
746
747 PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1;
748 bool bIsEndCol = aCellIterator == aLastCellIterator;
749 bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator;
750
751 //aCellIterator points to a PropertyMapPtr;
752 if( *aCellIterator )
753 {
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);
757
758 pAllCellProps->InsertProps(rInfo.pTableDefaults);
759
760 sal_Int32 nCellStyleMask = 0;
761 if (aCellIterator==aRowOfCellsIterator->begin())
762 {
763 if(rInfo.nTblLook&0x80)
764 nCellStyleMask = CNF_FIRST_COLUMN; // first col style used
765 }
766 else if (bIsEndCol)
767 {
768 if(rInfo.nTblLook&0x100)
769 nCellStyleMask = CNF_LAST_COLUMN; // last col style used
770 }
771 if(!nCellStyleMask) // if no cell style is used yet
772 {
773 if(!(rInfo.nTblLook&0x400))
774 { // vbanding used
775 int n = nCell + 1;
776 if(rInfo.nTblLook&0x80)
777 n++;
778 if(n & 1)
779 nCellStyleMask = CNF_ODD_VBAND;
780 else
781 nCellStyleMask = CNF_EVEN_VBAND;
782 }
783 }
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;
793
794 if ( rInfo.pTableStyle )
795 {
796 PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask );
797
798 // Check if we need to clean up some empty border definitions to match what Word does.
799 static const PropertyIds pBorders[] =
800 {
802 };
803 for (const PropertyIds& rBorder : pBorders)
804 {
805 std::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder);
806 std::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder);
807 if (oStyleCellBorder && oDirectCellBorder)
808 {
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)
813 {
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);
818 }
819 else
820 {
821 std::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder);
822 if (oTableBorder)
823 {
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)
828 {
829 // But at a table-level, there is a border, then again delete both cell properties.
830 pStyleProps->Erase(rBorder);
831 (*aCellIterator)->Erase(rBorder);
832 }
833 }
834 }
835 }
836 }
837
838 pAllCellProps->InsertProps( pStyleProps );
839 }
840
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 );
844
845 // Then add the cell properties
846 pAllCellProps->InsertProps(*aCellIterator);
847 std::swap(*(*aCellIterator), *pAllCellProps );
848
849#ifdef DBG_UTIL
851 TagLogger::getInstance().attribute("cell", nCell);
852 TagLogger::getInstance().attribute("row", nRow);
853#endif
854
855 // Do not apply horizontal and vertical borders to a one cell table.
856 if (m_aCellProperties.size() <= 1 && aRowOfCellsIterator->size() <= 1)
857 {
860 }
861 // Do not apply vertical borders to a one column table.
862 else if (m_aCellProperties.size() > 1 && aRowOfCellsIterator->size() <= 1)
863 {
864 bool isOneCol = true;
865 for (size_t i = nRow; i < m_aCellProperties.size(); i++)
866 {
867 if (m_aCellProperties[i].size() > 1)
868 {
869 isOneCol = false;
870 break;
871 }
872 }
873 if (isOneCol)
875 }
876 // Do not apply horizontal borders to a one row table.
877 else if (m_aCellProperties.size() == 1 && aRowOfCellsIterator->size() > 1)
878 {
880 }
881
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 )
887 {
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++)
891 {
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) )
894 {
895 oProp = m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE);
896 bMergedVertically = oProp && !oProp->second.get<bool>(); //continuing cell
897 if ( bMergedVertically )
898 nLastMergedRow = i;
899 }
900 else
901 bMergedVertically = false;
902 }
903
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 )
907 {
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 );
911 }
912 }
913
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 );
917
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);
927
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)
931 {
932 if (aHorizontalMergeVal->second.get<bool>())
933 {
934 // first cell in a merge
935 HorizontallyMergedCell aMerge(nRow, nCell);
936 rMerges.push_back(aMerge);
937 }
938 else if (!rMerges.empty())
939 {
940 // resuming an earlier merge
941 HorizontallyMergedCell& rMerge = rMerges.back();
942 rMerge.m_nLastRow = nRow;
943 rMerge.m_nLastCol = nCell;
944 }
945 (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE);
946 }
947 pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues();
948#ifdef DBG_UTIL
950#endif
951 }
952 ++nCell;
953 ++aCellIterator;
954 }
955 ++nRow;
956 ++aRowOfCellsIterator;
957 ++aRowIter;
958 }
959
960#ifdef DBG_UTIL
962#endif
963
964 return aCellProperties;
965}
966
968static bool lcl_hideMarks(PropertyMapVector1& rCellProperties)
969{
970 for (const PropertyMapPtr & p : rCellProperties)
971 {
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))
975 return false;
976 }
977 return true;
978}
979
981static bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow)
982{
983 if (nRow >= static_cast<sal_Int32>(rTableRanges.size()))
984 {
985 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
986 return false;
987 }
988
989 const RowSequence_t rRowSeq = rTableRanges[nRow];
990 if (!rRowSeq.hasElements())
991 {
992 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
993 return false;
994 }
995
996 if (!rRowSeq[0][0].is())
997 {
998 // This can happen when we can't import the table, e.g. we're inside a
999 // comment.
1000 SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
1001 return false;
1002 }
1003
1004 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY);
1005 try
1006 {
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)
1014 return false;
1015 }
1016 catch (const lang::IllegalArgumentException&)
1017 {
1018 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "compareRegionStarts() failed");
1019 return false;
1020 }
1021 return true;
1022}
1023
1024css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties()
1025{
1026#ifdef DBG_UTIL
1027 TagLogger::getInstance().startElement("getRowProperties");
1028#endif
1029
1030 css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() );
1031 auto aRowPropertiesRange = asNonConstRange(aRowProperties);
1032 sal_Int32 nRow = 0;
1033 for( const auto& rRow : m_aRowProperties )
1034 {
1035#ifdef DBG_UTIL
1036 TagLogger::getInstance().startElement("rowProps.row");
1037#endif
1038 if (rRow)
1039 {
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);
1044
1046 {
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));
1050 }
1051
1052 aRowPropertiesRange[nRow] = rRow->GetPropertyValues();
1053#ifdef DBG_UTIL
1054 rRow->dumpXml();
1055 lcl_DumpPropertyValues(aRowProperties[nRow]);
1056#endif
1057 }
1058 ++nRow;
1059#ifdef DBG_UTIL
1061#endif
1062 }
1063
1064#ifdef DBG_UTIL
1066#endif
1067
1068 return aRowProperties;
1069}
1070
1071static bool isAbsent(const std::vector<beans::PropertyValue>& propvals, const OUString& name)
1072{
1073 return std::find_if(propvals.begin(), propvals.end(),
1074 [&name](const beans::PropertyValue& propval)
1075 { return propval.Name == name; })
1076 == propvals.end();
1077}
1078
1079// table style has got bigger precedence than docDefault style,
1080// but lower precedence than the paragraph styles and direct paragraph formatting
1081void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableParaProperties, const css::beans::PropertyValues rCellProperties)
1082{
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.
1088
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;
1092 std::optional<OUString> oParagraphText;
1093
1094 for( auto const& eId : aAllTableParaProperties )
1095 {
1096 // apply paragraph and character properties of the table style on table paragraphs
1097 // if there is no direct paragraph formatting
1098 bool bSetDirectlyInParaLevel = rParaProp.m_pPropertyMap->isSet(eId);
1099 if ( !bSetDirectlyInParaLevel || isCharacterProperty(eId) )
1100 {
1101 if ( (eId == PROP_PARA_LEFT_MARGIN || eId == PROP_PARA_FIRST_LINE_INDENT) &&
1102 rParaProp.m_pPropertyMap->isSet(PROP_NUMBERING_RULES) )
1103 {
1104 // indentation of direct numbering has bigger precedence, than table style
1105 continue;
1106 }
1107
1108 OUString sPropertyName = getPropertyName(eId);
1109
1110 auto pCellProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
1111 [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
1112 // this cell applies the table style property
1113 if (pCellProp != rCellProperties.end())
1114 {
1115 if (bSetDirectlyInParaLevel) // it is a character property set directly in the paragraph
1116 {
1117 if (!oParagraphText) // do it only once
1118 {
1120 rParaProp.m_rEndParagraph->getText()->createTextCursorByRange(rParaProp.m_rEndParagraph), uno::UNO_QUERY_THROW );
1121 // select paragraph
1122 xParagraph->gotoStartOfParagraph( true );
1123 oParagraphText = xParagraph->getString();
1124 }
1125 // don't overwrite empty paragraph with table style, if it has a direct paragraph formatting
1126 if (oParagraphText->isEmpty())
1127 continue;
1128 }
1129 // handle paragraph background color defined in CellColorHandler
1130 if (eId == PROP_FILL_COLOR)
1131 {
1132 auto pFillStyleProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
1133 [](const beans::PropertyValue& rProp) { return rProp.Name == "FillStyle"; });
1134 if ( pFillStyleProp == rCellProperties.end() ||
1135 pFillStyleProp->Value != uno::Any(drawing::FillStyle_SOLID) )
1136 {
1137 // FillStyle_NONE, skip table style usage for paragraph background color
1138 continue;
1139 }
1140 }
1141 OUString sParaStyleName;
1142 rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
1143 StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
1144 bool bDocDefault;
1145 uno::Any aParaStyle = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true, &bDocDefault);
1146 // A very strange compatibility rule says that the DEFAULT style's specified fontsize of 11 or 12
1147 // or a specified left justify will always be overridden by the table-style.
1148 // Normally this rule is applied, so always do this unless a compatSetting indicates otherwise.
1149 bool bCompatOverride = false;
1150 if ( (eId == PROP_CHAR_HEIGHT || eId == PROP_PARA_ADJUST) && sParaStyleName == m_rDMapper_Impl.GetDefaultParaStyleName() )
1151 {
1152 if ( eId == PROP_CHAR_HEIGHT )
1153 bCompatOverride = aParaStyle == uno::Any(double(11)) || aParaStyle == uno::Any(double(12));
1154 else if ( eId == PROP_PARA_ADJUST )
1155 {
1156 style::ParagraphAdjust eAdjust(style::ParagraphAdjust_CENTER);
1157 aParaStyle >>= eAdjust;
1158 bCompatOverride = eAdjust == style::ParagraphAdjust_LEFT;
1159 }
1160
1161 // The wording is confusing here. Normally, the paragraph style DOES override the table-style.
1162 // But for these two special situations, do not override the table-style. So the default is false.
1163 // If false, then "CompatOverride" the normal behaviour, and apply the table-style's value.
1164 bCompatOverride &= !m_rDMapper_Impl.GetSettingsTable()->GetCompatSettingValue(u"overrideTableStyleFontSizeAndJustification");
1165 }
1166
1167 // use table style when no paragraph style setting or a docDefault value is applied instead of it
1168 if (!aParaStyle.hasValue() || bDocDefault || bCompatOverride) try
1169 {
1170 // apply style setting when the paragraph doesn't modify it
1171 aProps.push_back(comphelper::makePropertyValue(sPropertyName, pCellProp->Value));
1172 if (eId == PROP_FILL_COLOR)
1173 {
1174 // we need this for complete import of table-style based paragraph background color
1175 aProps.push_back(comphelper::makePropertyValue("FillStyle", uno::Any(drawing::FillStyle_SOLID)));
1176 }
1177 }
1178 catch ( const uno::Exception & )
1179 {
1180 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table style correction");
1181 }
1182 }
1183 }
1184 }
1185
1186 if (!aProps.empty())
1187 {
1188 // 2. Get all properties directly defined in the paragraph
1190 rParaProp.m_rPropertySet->getPropertySetInfo(), uno::UNO_SET_THROW);
1191 auto props = xPropSetInfo->getProperties();
1192 uno::Sequence<OUString> propNames(props.getLength());
1193 std::transform(props.begin(), props.end(), propNames.getArray(),
1194 [](const beans::Property& prop) { return prop.Name; });
1196 uno::UNO_QUERY_THROW);
1197 // getDirectPropertyValuesTolerant requires a sorted sequence.
1198 // Let's hope XPropertySetInfo::getProperties returns a sorted sequence.
1199 for (auto& val : xTolPara->getDirectPropertyValuesTolerant(propNames))
1200 {
1201 // 3. Add them to aProps, unless such properties are already there
1202 // (which means, that 'val' comes from docDefault)
1203 if (val.Result == beans::TolerantPropertySetResultType::SUCCESS
1204 && val.State == beans::PropertyState_DIRECT_VALUE
1205 && isAbsent(aProps, val.Name))
1206 {
1207 aProps.push_back(comphelper::makePropertyValue(val.Name, val.Value));
1208 }
1209 }
1210
1211 // 4. Create an autostyle, and assign it to the paragraph. The hidden ParaAutoStyleDef
1212 // property is handled in SwXTextCursor::setPropertyValue.
1214 rParaProp.m_rEndParagraph->getText()->createTextCursorByRange(
1215 rParaProp.m_rEndParagraph),
1216 uno::UNO_QUERY_THROW);
1217 xCursorProps->setPropertyValue("ParaAutoStyleDef",
1219 }
1220}
1221
1222// convert formula range identifier ABOVE, BELOW, LEFT and RIGHT
1224{
1225 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
1226 uno::Reference<container::XIndexAccess> xTableRows(xTable->getRows(), uno::UNO_QUERY_THROW);
1227 sal_Int32 nRows = xTableRows->getCount();
1228 for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1229 {
1230 for (sal_Int16 nCol = 0; nCol < MAXTABLECELLS; ++nCol)
1231 {
1232 try
1233 {
1234 uno::Reference<beans::XPropertySet> xCellProperties(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY_THROW);
1236 xCellProperties->getPropertyValue("CellInteropGrabBag") >>= aCellGrabBag;
1237 OUString sFormula;
1238 bool bReplace = false;
1239 for (const auto& rProp : std::as_const(aCellGrabBag))
1240 {
1241 if ( rProp.Name == "CellFormulaConverted" )
1242 {
1243 rProp.Value >>= sFormula;
1244 struct RangeDirection
1245 {
1246 OUString m_sName;
1247 sal_Int16 m_nCol;
1248 sal_Int16 m_nRow;
1249 };
1250 static const RangeDirection pDirections[] =
1251 {
1252 { OUString(" LEFT "), -1, 0},
1253 { OUString(" RIGHT "), 1, 0},
1254 { OUString(" ABOVE "), 0, -1},
1255 { OUString(" BELOW "), 0, 1 }
1256 };
1257 for (const RangeDirection& rRange : pDirections)
1258 {
1259 if ( sFormula.indexOf(rRange.m_sName) > -1 )
1260 {
1261 // range starts at the first cell above/below/left/right, but ends at the
1262 // table border or at the first non-value cell after a value cell
1263 bool bFoundFirst = false;
1264 OUString sNextCell;
1265 OUString sLastCell;
1266 OUString sLastValueCell;
1267 // walk through the cells of the range
1268 try
1269 {
1270 sal_Int32 nCell = 0;
1271 while (++nCell)
1272 {
1274 xCellRange->getCellByPosition(nCol + nCell * rRange.m_nCol, nRow + nCell * rRange.m_nRow),
1275 uno::UNO_QUERY_THROW);
1276 // empty cell or cell with text content is end of the range
1277 uno::Reference<text::XText> xText(xCell, uno::UNO_QUERY_THROW);
1278 sLastCell = xCell->getPropertyValue("CellName").get<OUString>();
1279 if (sNextCell.isEmpty())
1280 sNextCell = sLastCell;
1281
1282 // accept numbers with comma and percent
1283 OUString sCellText = xText->getString().replace(',', '.');
1284 if (sCellText.endsWith("%"))
1285 sCellText = sCellText.copy(0, sCellText.getLength()-1);
1286
1287 rtl_math_ConversionStatus eConversionStatus;
1288 sal_Int32 nParsedEnd;
1289 rtl::math::stringToDouble(sCellText, '.', ',', &eConversionStatus, &nParsedEnd);
1290 if ( eConversionStatus != rtl_math_ConversionStatus_Ok || nParsedEnd == 0 )
1291 {
1292 if ( !bFoundFirst )
1293 {
1294 // still search value cells
1295 continue;
1296 }
1297 else
1298 {
1299 // end of range
1300 break;
1301 }
1302 }
1303 sLastValueCell = sLastCell;
1304 bFoundFirst = true;
1305 }
1306 }
1307 catch ( const lang::IndexOutOfBoundsException & )
1308 {
1309 }
1310
1311 if ( !sNextCell.isEmpty() )
1312 {
1313 OUString sRange = "<" + sNextCell + ":" +
1314 ( sLastValueCell.isEmpty() ? sLastCell : sLastValueCell ) + ">";
1315 sFormula = sFormula.replaceAll(rRange.m_sName, sRange);
1316 bReplace = true;
1317 }
1318 }
1319 }
1320
1321 // update formula field
1322 if (bReplace)
1323 {
1324 uno::Reference<text::XText> xCell(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY);
1325 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell, uno::UNO_QUERY);
1326 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
1327 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
1328 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1329 while ( xRunEnum->hasMoreElements() )
1330 {
1331 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
1332 uno::Reference< beans::XPropertySet > xRunProperties( xRun, uno::UNO_QUERY_THROW );
1333 if ( xRunProperties->getPropertyValue("TextPortionType") == uno::Any(OUString("TextField")) )
1334 {
1335 uno::Reference<text::XTextField> const xField(xRunProperties->getPropertyValue("TextField").get<uno::Reference<text::XTextField>>());
1336 uno::Reference< beans::XPropertySet > xFieldProperties( xField, uno::UNO_QUERY_THROW );
1337 // cell can contain multiple text fields, but only one is handled now (~formula cell)
1338 if ( rProp.Value != xFieldProperties->getPropertyValue("Content") )
1339 continue;
1340 xFieldProperties->setPropertyValue("Content", uno::Any(sFormula));
1341 // update grab bag
1342 auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aCellGrabBag);
1343 beans::PropertyValue aValue;
1344 aValue.Name = "CellFormulaConverted";
1345 aValue.Value <<= sFormula;
1346 aGrabBag.push_back(aValue);
1347 xCellProperties->setPropertyValue("CellInteropGrabBag", uno::Any(comphelper::containerToSequence(aGrabBag)));
1348 }
1349 }
1350 }
1351 }
1352 }
1353 }
1354 catch ( const lang::IndexOutOfBoundsException & )
1355 {
1356 // jump to next table row
1357 break;
1358 }
1359 }
1360 }
1361}
1362
1363void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
1364{
1365#ifdef DBG_UTIL
1366 TagLogger::getInstance().startElement("tablehandler.endTable");
1367#endif
1368
1369 // If we want to make this table a floating one.
1370 std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >
1372 TableInfo aTableInfo;
1373 aTableInfo.nNestLevel = nestedTableLevel;
1374
1375 // non-floating tables need floating in footnotes and endnotes, because
1376 // Writer core cannot handle (i.e. save in ODT, copy, edit etc.) them otherwise
1377 bool bConvertToFloating = aFrameProperties.empty() &&
1378 nestedTableLevel <= 1 &&
1380 bool bFloating = !aFrameProperties.empty() || bConvertToFloating;
1381
1382 aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties, bConvertToFloating);
1383 // expands to uno::Sequence< Sequence< beans::PropertyValues > >
1384
1385 std::vector<HorizontallyMergedCell> aMerges;
1386 CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges);
1387
1388 css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties();
1389
1390#ifdef DBG_UTIL
1391 lcl_DumpPropertyValueSeq(aRowProperties);
1392#endif
1393
1394 if (!m_aTableRanges.empty())
1395 {
1398
1399 // fill empty frame properties to create an invisible frame around the table:
1400 // hide frame borders and zero inner and outer frame margins
1401 if (bConvertToFloating)
1402 DomainMapper_Impl::fillEmptyFrameProperties(aFrameProperties, true);
1403
1404 // OOXML table style may contain paragraph properties, apply these on cell paragraphs
1405 if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
1406 {
1407 // collect all paragraph properties used in table styles
1408 PropertyMapPtr pAllTableProps( new PropertyMap );
1409 pAllTableProps->InsertProps(aTableInfo.pTableDefaults);
1410 if ( aTableInfo.pTableStyle )
1411 pAllTableProps->InsertProps(aTableInfo.pTableStyle->GetProperties( CNF_ALL ));
1412 for (const auto& eId : pAllTableProps->GetPropertyIds())
1413 {
1414 if ( !isParagraphProperty(eId) && !isCharacterProperty(eId) )
1415 pAllTableProps->Erase(eId);
1416 }
1417 std::vector< PropertyIds > aAllTableParaProperties = pAllTableProps->GetPropertyIds();
1418
1419 if ( !aAllTableParaProperties.empty() )
1420 {
1422 for (size_t nRow = 0; nRow < m_aTableRanges.size(); ++nRow)
1423 {
1424 // Note that this is "cell" since you must not treat it as "column".
1425 for (size_t nCell = 0; nCell < m_aTableRanges[nRow].size(); ++nCell)
1426 {
1427 auto rStartPara = m_aTableRanges[nRow][nCell][0];
1428 if (!rStartPara.is())
1429 continue;
1430 auto rEndPara = m_aTableRanges[nRow][nCell][1];
1431 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rStartPara->getText(), uno::UNO_QUERY);
1432 bool bApply = false;
1433 // search paragraphs of the cell
1434 std::vector<TableParagraph>::iterator aIt = pTableParagraphs->begin();
1435 while ( aIt != pTableParagraphs->end() ) try
1436 {
1437 if (!bApply && xTextRangeCompare->compareRegionStarts(rStartPara, aIt->m_rStartParagraph) == 0)
1438 bApply = true;
1439 if (bApply)
1440 {
1441 bool bEndOfApply = (xTextRangeCompare->compareRegionEnds(rEndPara, aIt->m_rEndParagraph) == 0);
1442 // tdf#153891 handle missing cell properties (exception in style handling?)
1443 if ( nCell < sal::static_int_cast<std::size_t>(aCellProperties[nRow].getLength()) )
1444 ApplyParagraphPropertiesFromTableStyle(*aIt, aAllTableParaProperties, aCellProperties[nRow][nCell]);
1445 // erase processed paragraph from list of pending paragraphs
1446 aIt = pTableParagraphs->erase(aIt);
1447 if (bEndOfApply)
1448 break;
1449 }
1450 else
1451 ++aIt;
1452 }
1453 catch( const lang::IllegalArgumentException & )
1454 {
1455 // skip compareRegion with nested tables
1456 ++aIt;
1457 }
1458 }
1459 }
1460 }
1461 }
1462
1463 // Additional checks: if we can do this.
1464 if (bFloating && m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements())
1465 {
1466 xStart = m_aTableRanges[0][0][0];
1468 if (rLastRow.hasElements())
1469 {
1470 const uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1];
1471 xEnd = rLastCell[1];
1472 }
1473 }
1475 try
1476 {
1477 if (m_xText.is())
1478 {
1479 xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties);
1480
1481 if (xTable.is())
1482 {
1483 if (!aMerges.empty())
1484 {
1485 static const std::vector<std::u16string_view> aBorderNames
1486 = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
1487
1488 // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
1489 for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it)
1490 {
1491 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
1493 xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow),
1494 uno::UNO_QUERY_THROW);
1495 OUString aFirst
1496 = xFirstCell->getPropertyValue("CellName").get<OUString>();
1497 // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
1498 if (it->m_nLastCol != -1)
1499 {
1500 // Save border properties of the first cell
1501 // before merge.
1502 table::BorderLine2 aBorderValues[4];
1503 for (size_t i = 0; i < aBorderNames.size(); ++i)
1504 xFirstCell->getPropertyValue(OUString(aBorderNames[i]))
1505 >>= aBorderValues[i];
1506
1508 xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow),
1509 uno::UNO_QUERY_THROW);
1510 OUString aLast
1511 = xLastCell->getPropertyValue("CellName").get<OUString>();
1512
1513 uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst);
1514 xCursor->gotoCellByName(aLast, true);
1515
1516 xCursor->mergeRange();
1517
1518 // Handle conflicting properties: mergeRange()
1519 // takes the last cell, Word takes the first
1520 // cell.
1521 for (size_t i = 0; i < aBorderNames.size(); ++i)
1522 {
1523 if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE)
1524 xFirstCell->setPropertyValue(
1525 OUString(aBorderNames[i]), uno::Any(aBorderValues[i]));
1526 }
1527 }
1528 }
1529 }
1530
1531 // convert special range IDs ABOVE, BELOW, LEFT and RIGHT
1533 }
1534 }
1535 }
1536 catch ( const lang::IllegalArgumentException & )
1537 {
1538 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Conversion to table error");
1539#ifdef DBG_UTIL
1540 TagLogger::getInstance().chars(std::string("failed to import table!"));
1541#endif
1542 }
1543 catch ( const uno::Exception & )
1544 {
1545 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table creation");
1546 }
1547
1548 // If we have a table with a start and an end position, we should make it a floating one.
1549 // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
1550 if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote)
1551 {
1552 uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
1553 bool bIsRelative = false;
1554 xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative;
1555 if (!bIsRelative)
1556 {
1557 beans::PropertyValue aValue;
1558 aValue.Name = "Width";
1559 aValue.Value = xTableProperties->getPropertyValue("Width");
1560 aFrameProperties.push_back(aValue);
1561 }
1562 else
1563 {
1564 beans::PropertyValue aValue;
1565 aValue.Name = "FrameWidthPercent";
1566 aValue.Value = xTableProperties->getPropertyValue("RelativeWidth");
1567 aFrameProperties.push_back(aValue);
1568
1569 // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
1570 xTableProperties->setPropertyValue("RelativeWidth", uno::Any(sal_Int16(100)));
1571 }
1572
1573 // A non-zero left margin would move the table out of the frame, move the frame itself instead.
1574 xTableProperties->setPropertyValue("LeftMargin", uno::Any(sal_Int32(0)));
1575
1576 style::BreakType eBreakType{};
1577 xTableProperties->getPropertyValue("BreakType") >>= eBreakType;
1578 if (eBreakType != style::BreakType_NONE)
1579 {
1580 // A break before the table was requested. Reset that break here, since the table
1581 // will be at the start of the fly frame, not in the body frame.
1582 xTableProperties->setPropertyValue("BreakType", uno::Any(style::BreakType_NONE));
1583 }
1584
1585 if (nestedTableLevel >= 2)
1586 {
1587 // Floating tables inside a table always stay inside the cell.
1588 aFrameProperties.push_back(
1589 comphelper::makePropertyValue("IsFollowingTextFlow", true));
1590 }
1591
1592 if (nestedTableLevel <= 1)
1593 {
1594 // A text frame created for floating tables is allowed to split if it's a toplevel
1595 // table.
1596 aFrameProperties.push_back(comphelper::makePropertyValue("IsSplitAllowed", true));
1597 }
1598
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 // Don't execute the conversion for nested tables anchored at a cell start: that
1606 // currently invalidates the cell start / end references and the outer table conversion
1607 // would fail.
1609 if (xTextAppendAndConvert.is() && !(nestedTableLevel >= 2 && bTableStartsAtCellStart))
1610 {
1611 std::deque<css::uno::Any> aFramedRedlines = m_rDMapper_Impl.m_aStoredRedlines[StoredRedlines::FRAME];
1612 std::vector<sal_Int32> redPos, redLen;
1613 std::vector<OUString> redCell;
1614 std::vector<OUString> redTable;
1615 BeforeConvertToTextFrame(aFramedRedlines, redPos, redLen, redCell, redTable);
1616
1617 uno::Reference<text::XTextContent> xContent = xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties));
1618 xFrameAnchor.set(xContent->getAnchor(), uno::UNO_QUERY);
1619
1620 bool bConvertToFloatingInFootnote = false;
1621 if (xContent.is() && xContent->getAnchor().is())
1622 {
1623 uno::Reference<lang::XServiceInfo> xText(xContent->getAnchor()->getText(), uno::UNO_QUERY);
1624 if (xText.is())
1625 {
1626 bConvertToFloatingInFootnote = xText->supportsService("com.sun.star.text.Footnote");
1627 }
1628 }
1629
1630 // paragraph of the anchoring point of the floating table needs zero top and bottom
1631 // margins, if the table was a not floating table in the footnote, otherwise
1632 // docDefault margins could result bigger vertical spaces around the table
1633 if ( bConvertToFloatingInFootnote && xContent.is() )
1634 {
1636 xContent->getAnchor(), uno::UNO_QUERY);
1637 if ( xParagraph.is() )
1638 {
1639 xParagraph->setPropertyValue("ParaTopMargin",
1640 uno::Any(static_cast<sal_Int32>(0)));
1641 xParagraph->setPropertyValue("ParaBottomMargin",
1642 uno::Any(static_cast<sal_Int32>(0)));
1643 }
1644 }
1645
1646 if (xContent.is())
1647 {
1648 // By the time the frame is created, the anchor's paragraph marker character
1649 // properties are already imported. Check if we need to disable "vanish", that
1650 // would lead to a hidden floating table in Writer, but it does not in Word.
1651 uno::Reference<beans::XPropertySet> xParagraph(xContent->getAnchor(),
1652 uno::UNO_QUERY);
1653 if (xParagraph.is())
1654 {
1655 bool bCharHidden{};
1656 xParagraph->getPropertyValue("CharHidden") >>= bCharHidden;
1657 if (bCharHidden)
1658 {
1659 xParagraph->setPropertyValue("CharHidden", uno::Any(false));
1660 }
1661 }
1662 }
1663
1664 AfterConvertToTextFrame(m_rDMapper_Impl, aFramedRedlines, redPos, redLen, redCell, redTable);
1665 }
1666
1667 if (xFrameAnchor.is() && eBreakType != style::BreakType_NONE)
1668 {
1669 // A break before the table was requested. Restore that on the anchor.
1670 xFrameAnchor->setPropertyValue("BreakType", uno::Any(eBreakType));
1671 }
1672 }
1673 }
1674
1676 m_aCellProperties.clear();
1677 m_aRowProperties.clear();
1678 m_bHadFootOrEndnote = false;
1679
1680#ifdef DBG_UTIL
1683#endif
1684}
1685
1687{
1688 m_aRowProperties.push_back( pProps.get() );
1689 m_aCellProperties.emplace_back( );
1690
1691#ifdef DBG_UTIL
1692 TagLogger::getInstance().startElement("table.row");
1693 if (pProps != nullptr)
1694 pProps->dumpXml();
1695#endif
1696
1697 m_aRowRanges.clear();
1698}
1699
1701{
1703#ifdef DBG_UTIL
1705#endif
1706}
1707
1708void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start,
1709 const TablePropertyMapPtr& pProps )
1710{
1711 sal_uInt32 nRow = m_aRowProperties.size();
1712 if ( pProps )
1713 m_aCellProperties[nRow - 1].push_back( pProps.get() );
1714 else
1715 {
1716 // Adding an empty cell properties map to be able to get
1717 // the table defaults properties
1718 TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) );
1719 m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() );
1720 }
1721
1722#ifdef DBG_UTIL
1723 TagLogger::getInstance().startElement("table.cell");
1724 TagLogger::getInstance().startElement("table.cell.start");
1727 if (pProps)
1728 pProps->printProperties();
1729#endif
1730
1731 //add a new 'row' of properties
1732 m_aCellRange.clear();
1734 if (start)
1735 xStart = start->getStart();
1736 m_aCellRange.push_back(xStart);
1737}
1738
1739void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end)
1740{
1741#ifdef DBG_UTIL
1742 TagLogger::getInstance().startElement("table.cell.end");
1746#endif
1747
1749 if (end)
1750 xEnd = end->getEnd();
1751 m_aCellRange.push_back(xEnd);
1753}
1754
1756{
1757 m_bHadFootOrEndnote = bHadFootOrEndnote;
1758}
1759
1761{
1762 return m_rDMapper_Impl;
1763}
1764
1765}
1766
1767/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define CNF_LAST_ROW_LAST_COLUMN
#define CNF_LAST_COLUMN
#define CNF_ALL
#define CNF_FIRST_COLUMN
#define CNF_EVEN_HBAND
#define CNF_ODD_VBAND
#define CNF_FIRST_ROW_FIRST_COLUMN
#define MAXTABLECELLS
#define CNF_EVEN_VBAND
#define CNF_ODD_HBAND
#define CNF_FIRST_ROW
#define CNF_LAST_ROW_FIRST_COLUMN
#define CNF_LAST_ROW
#define DEF_BORDER_DIST
#define CNF_FIRST_ROW_LAST_COLUMN
css::uno::Sequence< css::beans::PropertyValue > getAsConstPropertyValueList() const
T * get() const
static TagLogger & getInstance()
Definition: TagLogger.cxx:95
void startElement(const std::string &name)
Definition: TagLogger.cxx:140
void chars(const std::string &chars)
Definition: TagLogger.cxx:215
void attribute(const std::string &name, const std::string &value)
Definition: TagLogger.cxx:150
bool m_bHadFootOrEndnote
Did we have a foot or endnote in this table?
void endCell(const css::uno::Reference< css::text::XTextRange > &end)
Handle end of cell.
void ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableProperties, const css::beans::PropertyValues rCellProperties)
void startCell(const css::uno::Reference< css::text::XTextRange > &start, const TablePropertyMapPtr &pProps)
Handle start of cell.
TableStyleSheetEntry * endTableGetTableStyle(TableInfo &rInfo, std::vector< css::beans::PropertyValue > &rFrameProperties, bool bConvertToFloating)
css::uno::Reference< css::text::XTextAppendAndConvert > m_xText
std::vector< css::uno::Reference< css::text::XTextRange > > m_aCellRange
void startRow(const TablePropertyMapPtr &pProps)
Handle start of row.
void endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
Handle end of table.
CellPropertyValuesSeq_t endTableGetCellProperties(TableInfo &rInfo, std::vector< HorizontallyMergedCell > &rMerges)
css::uno::Sequence< css::beans::PropertyValues > endTableGetRowProperties()
void startTable(const TablePropertyMapPtr &pProps)
Handle start of table.
DomainMapperTableHandler(css::uno::Reference< css::text::XTextAppendAndConvert > xText, DomainMapper_Impl &rDMapper_Impl)
css::uno::Sequence< css::beans::PropertyValue > getCurrentTablePosition()
static void fillEmptyFrameProperties(std::vector< css::beans::PropertyValue > &rFrameProperties, bool bSetAnchorToChar)
css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool *bIsDocDefault=nullptr)
DomainMapperTableManager & getTableManager()
css::uno::Reference< css::text::XTextAppend > const & GetTopTextAppend()
std::deque< css::uno::Any > m_aStoredRedlines[StoredRedlines::NONE]
StyleSheetTablePtr const & GetStyleSheetTable()
sal_uInt32 getGridAfter(sal_uInt32 nRow)
sal_uInt32 findColumn(const sal_uInt32 nRow, const sal_uInt32 nCell)
Given a zero-based row/cell, return the zero-based grid it belongs to, or SAL_MAX_UINT16 for invalid.
sal_uInt32 findColumnCell(const sal_uInt32 nRow, const sal_uInt32 nCol)
Given a zero-based row/col, return the zero-based cell describing that grid, or SAL_MAX_UINT16 for in...
sal_uInt32 getGridBefore(sal_uInt32 nRow)
PropertyMapPtr GetProperties(sal_Int32 nMask)
#define TOOLS_WARN_EXCEPTION(area, stream)
#define TOOLS_INFO_EXCEPTION(area, stream)
OUString m_sName
OString sFormula
float u
sal_Int16 nValue
const char * name
void * p
sal_Int64 n
#define SAL_WARN(area, stream)
tools::Long const nLeftMargin
double getLength(const B2DPolygon &rCandidate)
size
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
end
dictionary props
OUString getPropertyName(PropertyIds eId)
bool isCharacterProperty(const PropertyIds eId)
bool isParagraphProperty(const PropertyIds eId)
css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValues > > CellPropertyValuesSeq_t
std::shared_ptr< std::vector< TableParagraph > > TableParagraphVectorPtr
void lcl_DumpPropertyValueSeq(css::uno::Sequence< css::beans::PropertyValues > const &rPropValSeq)
css::uno::Sequence< CellSequence_t > RowSequence_t
static bool lcl_emptyRow(std::vector< RowSequence_t > &rTableRanges, sal_Int32 nRow)
Are all cells in this row empty?
void BeforeConvertToTextFrame(std::deque< css::uno::Any > &rFramedRedlines, std::vector< sal_Int32 > &redPos, std::vector< sal_Int32 > &redLen, std::vector< OUString > &redCell, std::vector< OUString > &redTable)
static bool lcl_hideMarks(PropertyMapVector1 &rCellProperties)
Do all cells in this row have a CellHideMark property?
tools::SvRef< PropertyMap > PropertyMapPtr
static void lcl_debug_BorderLine(table::BorderLine const &rLine)
static void lcl_mergeBorder(PropertyIds nId, const PropertyMapPtr &pOrig, const PropertyMapPtr &pDest)
static void lcl_debug_TableBorder(table::TableBorder const &rBorder)
static void lcl_convertFormulaRanges(const uno::Reference< text::XTextTable > &xTable)
void AfterConvertToTextFrame(DomainMapper_Impl &rDM_Impl, std::deque< css::uno::Any > &aFramedRedlines, std::vector< sal_Int32 > &redPos, std::vector< sal_Int32 > &redLen, std::vector< OUString > &redCell, std::vector< OUString > &redTable)
css::uno::Sequence< css::uno::Reference< css::text::XTextRange > > CellSequence_t
void lcl_DumpPropertyValues(beans::PropertyValues const &rValues)
static void lcl_computeCellBorders(const PropertyMapPtr &pTableBorders, const PropertyMapPtr &pCellProps, sal_uInt32 nCell, sal_uInt32 nFirstCell, sal_uInt32 nLastCell, sal_Int32 nRow, bool bIsEndRow, bool bMergedVertically)
std::vector< PropertyMapPtr > PropertyMapVector1
static bool isAbsent(const std::vector< beans::PropertyValue > &propvals, const OUString &name)
std::string XTextRangeToString(uno::Reference< text::XTextRange > const &textRange)
Definition: util.cxx:27
sal_Int16 nId
bool hasValue()
A horizontally merged cell is in fact a range of cells till its merge is performed.
css::beans::PropertyValues aTableProperties
std::vector< PropertyIds > aTablePropertyIds
Information about a paragraph to be finished after a table end.
css::uno::Reference< css::text::XTextRange > m_rEndParagraph
css::uno::Reference< css::beans::XPropertySet > m_rPropertySet
LineStyle