Bug Summary

File:home/maarten/src/libreoffice/core/include/tools/ref.hxx
Warning:line 97, column 56
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name DomainMapperTableHandler.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/oox/generated -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/writerfilter/source -I /home/maarten/src/libreoffice/core/writerfilter/inc -I /home/maarten/src/libreoffice/core/writerfilter/source -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx

/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx

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
24#include "DomainMapperTableHandler.hxx"
25#include "DomainMapper_Impl.hxx"
26#include "StyleSheetTable.hxx"
27#include <com/sun/star/style/ParagraphAdjust.hpp>
28#include <com/sun/star/table/TableBorderDistances.hpp>
29#include <com/sun/star/table/TableBorder.hpp>
30#include <com/sun/star/table/BorderLine2.hpp>
31#include <com/sun/star/table/BorderLineStyle.hpp>
32#include <com/sun/star/table/XCellRange.hpp>
33#include <com/sun/star/text/HoriOrientation.hpp>
34#include <com/sun/star/text/SizeType.hpp>
35#include <com/sun/star/text/XTextField.hpp>
36#include <com/sun/star/text/XTextRangeCompare.hpp>
37#include <com/sun/star/beans/XPropertySet.hpp>
38#include <com/sun/star/beans/XPropertyState.hpp>
39#include <com/sun/star/container/XEnumeration.hpp>
40#include <com/sun/star/container/XEnumerationAccess.hpp>
41#include <com/sun/star/drawing/FillStyle.hpp>
42#include "TablePositionHandler.hxx"
43#include "TagLogger.hxx"
44#include "util.hxx"
45#include <osl/diagnose.h>
46#include <sal/log.hxx>
47#include <tools/diagnose_ex.h>
48#include <comphelper/sequence.hxx>
49#include <comphelper/propertyvalue.hxx>
50#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
51#include <boost/lexical_cast.hpp>
52
53#ifdef DBG_UTIL
54#include "PropertyMapHelper.hxx"
55#include <rtl/ustring.hxx>
56#endif
57
58namespace writerfilter::dmapper {
59
60using namespace ::com::sun::star;
61using namespace ::std;
62
63#define DEF_BORDER_DIST190 190 //0,19cm
64#define CNF_FIRST_ROW0x800 0x800
65#define CNF_LAST_ROW0x400 0x400
66#define CNF_FIRST_COLUMN0x200 0x200
67#define CNF_LAST_COLUMN0x100 0x100
68#define CNF_ODD_VBAND0x080 0x080
69#define CNF_EVEN_VBAND0x040 0x040
70#define CNF_ODD_HBAND0x020 0x020
71#define CNF_EVEN_HBAND0x010 0x010
72#define CNF_FIRST_ROW_LAST_COLUMN0x008 0x008
73#define CNF_FIRST_ROW_FIRST_COLUMN0x004 0x004
74#define CNF_LAST_ROW_LAST_COLUMN0x002 0x002
75#define CNF_LAST_ROW_FIRST_COLUMN0x001 0x001
76#define CNF_ALL0xFFF 0xFFF
77
78// total number of table columns
79#define MAXTABLECELLS63 63
80
81DomainMapperTableHandler::DomainMapperTableHandler(
82 css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
83 DomainMapper_Impl& rDMapper_Impl)
84 : m_xText(xText),
85 m_rDMapper_Impl( rDMapper_Impl ),
86 m_bHadFootOrEndnote(false)
87{
88}
89
90DomainMapperTableHandler::~DomainMapperTableHandler()
91{
92}
93
94void DomainMapperTableHandler::startTable(const TablePropertyMapPtr& pProps)
95{
96 m_aTableProperties = pProps;
97 m_aTableRanges.clear();
98
99#ifdef DBG_UTIL
100 TagLogger::getInstance().startElement("tablehandler.table");
101
102 if (pProps)
103 pProps->dumpXml();
104#endif
105}
106
107static void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest )
108{
109 std::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId);
110
111 if ( pOrigVal )
112 {
113 pDest->Insert( nId, pOrigVal->second, false );
114 }
115}
116
117static void lcl_computeCellBorders( const PropertyMapPtr& pTableBorders, const PropertyMapPtr& pCellProps,
118 sal_uInt32 nCell, sal_uInt32 nFirstCell, sal_uInt32 nLastCell, sal_Int32 nRow, bool bIsEndRow, bool bMergedVertically )
119{
120 const bool bIsStartCol = nCell == nFirstCell;
121 const bool bIsEndCol = nCell == nLastCell;
122 std::optional<PropertyMap::Property> pVerticalVal = pCellProps->getProperty(META_PROP_VERTICAL_BORDER);
123 std::optional<PropertyMap::Property> pHorizontalVal = pCellProps->getProperty(META_PROP_HORIZONTAL_BORDER);
124
125 // Handle the vertical and horizontal borders
126 uno::Any aVertProp;
127 if ( !pVerticalVal)
128 {
129 pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER);
130 if ( pVerticalVal )
131 aVertProp = pVerticalVal->second;
132 }
133 else
134 {
135 aVertProp = pVerticalVal->second;
136 pCellProps->Erase( pVerticalVal->first );
137 }
138
139 uno::Any aHorizProp;
140 if ( !pHorizontalVal )
141 {
142 pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER);
143 if ( pHorizontalVal )
144 aHorizProp = pHorizontalVal->second;
145 }
146 else
147 {
148 aHorizProp = pHorizontalVal->second;
149 pCellProps->Erase( pHorizontalVal->first );
150 }
151
152 if ( bIsStartCol )
153 lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps );
154
155 if ( bIsEndCol )
156 lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps );
157
158 // <w:insideV> counts if there are multiple cells in this row.
159 if ( pVerticalVal )
160 {
161 if ( !bIsEndCol && nCell >= nFirstCell )
162 pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
163 if ( !bIsStartCol && nCell <= nLastCell )
164 pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
165 }
166
167 if ( nRow == 0 )
168 {
169 lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps );
170 if ( pHorizontalVal && !bMergedVertically )
171 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
172 }
173
174 if ( bMergedVertically )
175 lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
176
177 if ( bIsEndRow )
178 {
179 lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
180 if ( pHorizontalVal )
181 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
182 }
183
184 if ( nRow > 0 && !bIsEndRow )
185 {
186 if ( pHorizontalVal )
187 {
188 pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
189 pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
190 }
191 }
192}
193
194#ifdef DBG_UTIL
195
196static void lcl_debug_BorderLine(table::BorderLine const & rLine)
197{
198 TagLogger::getInstance().startElement("BorderLine");
199 TagLogger::getInstance().attribute("Color", rLine.Color);
200 TagLogger::getInstance().attribute("InnerLineWidth", rLine.InnerLineWidth);
201 TagLogger::getInstance().attribute("OuterLineWidth", rLine.OuterLineWidth);
202 TagLogger::getInstance().attribute("LineDistance", rLine.LineDistance);
203 TagLogger::getInstance().endElement();
204}
205
206static void lcl_debug_TableBorder(table::TableBorder const & rBorder)
207{
208 TagLogger::getInstance().startElement("TableBorder");
209 lcl_debug_BorderLine(rBorder.TopLine);
210 TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder.IsTopLineValid));
211 lcl_debug_BorderLine(rBorder.BottomLine);
212 TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder.IsBottomLineValid));
213 lcl_debug_BorderLine(rBorder.LeftLine);
214 TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder.IsLeftLineValid));
215 lcl_debug_BorderLine(rBorder.RightLine);
216 TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder.IsRightLineValid));
217 lcl_debug_BorderLine(rBorder.VerticalLine);
218 TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder.IsVerticalLineValid));
219 lcl_debug_BorderLine(rBorder.HorizontalLine);
220 TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder.IsHorizontalLineValid));
221 TagLogger::getInstance().attribute("Distance", rBorder.Distance);
222 TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder.IsDistanceValid));
223 TagLogger::getInstance().endElement();
224}
225#endif
226
227struct TableInfo
228{
229 sal_Int32 nLeftBorderDistance;
230 sal_Int32 nRightBorderDistance;
231 sal_Int32 nTopBorderDistance;
232 sal_Int32 nBottomBorderDistance;
233 sal_Int32 nTblLook;
234 sal_Int32 nNestLevel;
235 PropertyMapPtr pTableDefaults;
236 PropertyMapPtr pTableBorders;
237 TableStyleSheetEntry* pTableStyle;
238 css::beans::PropertyValues aTableProperties;
239 std::vector< PropertyIds > aTablePropertyIds;
240
241 TableInfo()
242 : nLeftBorderDistance(DEF_BORDER_DIST190)
243 , nRightBorderDistance(DEF_BORDER_DIST190)
244 , nTopBorderDistance(0)
245 , nBottomBorderDistance(0)
246 , nTblLook(0x4a0)
247 , nNestLevel(0)
248 , pTableDefaults(new PropertyMap)
249 , pTableBorders(new PropertyMap)
250 , pTableStyle(nullptr)
251 {
252 }
253
254};
255
256namespace
257{
258
259bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine)
260{
261 if (!pTableProperties)
262 return false;
263
264 const std::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId);
265 if( aTblBorder )
266 {
267 OSL_VERIFY(aTblBorder->second >>= rLine)do { if (!(aTblBorder->second >>= rLine)) do { if (true
&& (!(0))) { sal_detail_logFormat((SAL_DETAIL_LOG_LEVEL_WARN
), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "267" ": "), "OSL_ASSERT: %s", "0"); } } while (false); }
while (0)
;
268
269 rInfo.pTableBorders->Insert( nId, uno::makeAny( rLine ) );
270 rInfo.pTableDefaults->Erase( nId );
271
272 return true;
273 }
274
275 return false;
276}
277
278void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient)
279{
280 // Shifts the frame left by the given value.
281 for (const beans::PropertyValue & rFrameProperty : rFrameProperties)
282 {
283 if (rFrameProperty.Name == "HoriOrient")
284 {
285 sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>();
286 if (nValue != text::HoriOrientation::NONE)
287 nHoriOrient = nValue;
288 return;
289 }
290 }
291}
292
293void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount)
294{
295 // Shifts the frame left by the given value.
296 for (beans::PropertyValue & rPropertyValue : rFrameProperties)
297 {
298 if (rPropertyValue.Name == "HoriOrientPosition")
299 {
300 sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>();
301 nValue -= nAmount;
302 rPropertyValue.Value <<= nValue;
303 return;
304 }
305 }
306}
307
308void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftBorder,
309 const table::BorderLine2& rRightBorder)
310{
311 // MS Word appears to do these things to adjust the cell horizontal area:
312 //
313 // bll = left borderline width
314 // blr = right borderline width
315 // cea = cell's edit area rectangle
316 // cea_w = cea width
317 // cml = cell's left margin (padding) defined in cell settings
318 // cmr = cell's right margin (padding) defined in cell settings
319 // cw = cell width (distance between middles of left borderline and right borderline)
320 // pad_l = actual cea left padding = (its left pos relative to middle of bll)
321 // pad_r = actual cea right padding = abs (its right pos relative to middle of blr)
322 //
323 // pad_l = max(bll/2, cml) -> cea does not overlap left borderline
324 // cea_w = cw-max(pad_l+blr/2, cml+cmr) -> cea does not overlap right borderline
325 // pad_r = max(pad_l+blr/2, cml+cmr) - pad_l
326 //
327 // It means that e.g. for border widths of 6 pt (~2.12 mm), left margin 0 mm, and right margin
328 // 2 mm, actual left and right margins will (unexpectedly) coincide with inner edges of cell's
329 // borderlines - the right margin won't create spacing between right of edit rectangle and the
330 // inner edge of right borderline.
331
332 const sal_Int32 nActualL
333 = std::max<sal_Int32>(rLeftBorder.LineWidth / 2, rInfo.nLeftBorderDistance);
334 const sal_Int32 nActualR
335 = std::max<sal_Int32>(nActualL + rRightBorder.LineWidth / 2,
336 rInfo.nLeftBorderDistance + rInfo.nRightBorderDistance)
337 - nActualL;
338 rInfo.nLeftBorderDistance = nActualL;
339 rInfo.nRightBorderDistance = nActualR;
340}
341
342}
343
344TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
345{
346 // will receive the table style if any
347 TableStyleSheetEntry* pTableStyle = nullptr;
348
349 if( m_aTableProperties )
2
Taking true branch
350 {
351 //create properties from the table attributes
352 //...pPropMap->Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin - m_nGapHalf ));
353 //pPropMap->Insert( PROP_HORI_ORIENT, uno::makeAny( text::HoriOrientation::RIGHT ));
354 sal_Int32 nGapHalf = 0;
355 sal_Int32 nLeftMargin = 0;
356
357 comphelper::SequenceAsHashMap aGrabBag;
358
359 if (nullptr != m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition())
3
Assuming the condition is false
4
Taking false branch
360 {
361 TablePositionHandler *pTablePositions = m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition();
362
363 uno::Sequence< beans::PropertyValue > aGrabBagTS( 10 );
364
365 aGrabBagTS[0].Name = "bottomFromText";
366 aGrabBagTS[0].Value <<= pTablePositions->getBottomFromText();
367
368 aGrabBagTS[1].Name = "horzAnchor";
369 aGrabBagTS[1].Value <<= pTablePositions->getHorzAnchor();
370
371 aGrabBagTS[2].Name = "leftFromText";
372 aGrabBagTS[2].Value <<= pTablePositions->getLeftFromText();
373
374 aGrabBagTS[3].Name = "rightFromText";
375 aGrabBagTS[3].Value <<= pTablePositions->getRightFromText();
376
377 aGrabBagTS[4].Name = "tblpX";
378 aGrabBagTS[4].Value <<= pTablePositions->getX();
379
380 aGrabBagTS[5].Name = "tblpXSpec";
381 aGrabBagTS[5].Value <<= pTablePositions->getXSpec();
382
383 aGrabBagTS[6].Name = "tblpY";
384 aGrabBagTS[6].Value <<= pTablePositions->getY();
385
386 aGrabBagTS[7].Name = "tblpYSpec";
387 aGrabBagTS[7].Value <<= pTablePositions->getYSpec();
388
389 aGrabBagTS[8].Name = "topFromText";
390 aGrabBagTS[8].Value <<= pTablePositions->getTopFromText();
391
392 aGrabBagTS[9].Name = "vertAnchor";
393 aGrabBagTS[9].Value <<= pTablePositions->getVertAnchor();
394
395 aGrabBag["TablePosition"] <<= aGrabBagTS;
396 }
397
398 std::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
399 if(aTableStyleVal)
5
Assuming the condition is false
6
Taking false branch
400 {
401 // Apply table style properties recursively
402 OUString sTableStyleName;
403 aTableStyleVal->second >>= sTableStyleName;
404 StyleSheetTablePtr pStyleSheetTable = m_rDMapper_Impl.GetStyleSheetTable();
405 const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName );
406 pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) );
407 m_aTableProperties->Erase( aTableStyleVal->first );
408
409 aGrabBag["TableStyleName"] <<= sTableStyleName;
410
411 if( pStyleSheet )
412 {
413 // First get the style properties, then the table ones
414 PropertyMapPtr pTableProps( m_aTableProperties.get() );
415 TablePropertyMapPtr pEmptyProps( new TablePropertyMap );
416
417 m_aTableProperties = pEmptyProps;
418
419 PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);
420
421 table::BorderLine2 aBorderLine;
422 TableInfo rStyleInfo;
423 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine))
424 {
425 aGrabBag["TableStyleTopBorder"] <<= aBorderLine;
426 }
427 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine))
428 {
429 aGrabBag["TableStyleBottomBorder"] <<= aBorderLine;
430 }
431 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine))
432 {
433 aGrabBag["TableStyleLeftBorder"] <<= aBorderLine;
434 }
435 if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine))
436 {
437 aGrabBag["TableStyleRightBorder"] <<= aBorderLine;
438 }
439
440#ifdef DBG_UTIL
441 TagLogger::getInstance().startElement("mergedProps");
442 if (pMergedProperties)
443 pMergedProperties->dumpXml();
444 TagLogger::getInstance().endElement();
445#endif
446
447 m_aTableProperties->InsertProps(pMergedProperties);
448 m_aTableProperties->InsertProps(pTableProps);
449
450#ifdef DBG_UTIL
451 TagLogger::getInstance().startElement("TableProperties");
452 m_aTableProperties->dumpXml();
453 TagLogger::getInstance().endElement();
454#endif
455 if (pTableStyle)
456 {
457 // apply tblHeader setting of the table style
458 PropertyMapPtr pHeaderStyleProps = pTableStyle->GetProperties(CNF_FIRST_ROW0x800);
459 if ( pHeaderStyleProps->getProperty(PROP_HEADER_ROW_COUNT) )
460 m_aTableProperties->Insert(PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(1)), false);
461 }
462 }
463 }
464
465 // This is the one preserving just all the table look attributes.
466 std::optional<PropertyMap::Property> oTableLook = m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
467 if (oTableLook)
7
Assuming the condition is false
8
Taking false branch
468 {
469 aGrabBag["TableStyleLook"] = oTableLook->second;
470 m_aTableProperties->Erase(oTableLook->first);
471 }
472
473 // This is just the "val" attribute's numeric value.
474 const std::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK);
475 if(aTblLook)
9
Assuming the condition is false
10
Taking false branch
476 {
477 aTblLook->second >>= rInfo.nTblLook;
478 m_aTableProperties->Erase( aTblLook->first );
479 }
480
481 // apply cell margin settings of the table style
482 const std::optional<PropertyMap::Property> oLeftMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_LEFT);
483 if (oLeftMargin)
11
Assuming the condition is false
12
Taking false branch
484 {
485 oLeftMargin->second >>= rInfo.nLeftBorderDistance;
486 m_aTableProperties->Erase(oLeftMargin->first);
487 }
488 const std::optional<PropertyMap::Property> oRightMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_RIGHT);
489 if (oRightMargin)
13
Assuming the condition is false
14
Taking false branch
490 {
491 oRightMargin->second >>= rInfo.nRightBorderDistance;
492 m_aTableProperties->Erase(oRightMargin->first);
493 }
494 const std::optional<PropertyMap::Property> oTopMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_TOP);
495 if (oTopMargin)
15
Assuming the condition is false
16
Taking false branch
496 {
497 oTopMargin->second >>= rInfo.nTopBorderDistance;
498 m_aTableProperties->Erase(oTopMargin->first);
499 }
500 const std::optional<PropertyMap::Property> oBottomMargin = m_aTableProperties->getProperty(META_PROP_CELL_MAR_BOTTOM);
501 if (oBottomMargin)
17
Assuming the condition is false
18
Taking false branch
502 {
503 oBottomMargin->second >>= rInfo.nBottomBorderDistance;
504 m_aTableProperties->Erase(oBottomMargin->first);
505 }
506
507 // Set the table default attributes for the cells
508 rInfo.pTableDefaults->InsertProps(m_aTableProperties.get());
19
Calling '~SvRef'
29
Returning from '~SvRef'
509
510#ifdef DBG_UTIL
511 TagLogger::getInstance().startElement("TableDefaults");
512 rInfo.pTableDefaults->dumpXml();
513 TagLogger::getInstance().endElement();
514#endif
515
516 if (!aGrabBag.empty())
30
Assuming the condition is false
31
Taking false branch
517 {
518 m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::makeAny( aGrabBag.getAsConstPropertyValueList() ) );
519 }
520
521 m_aTableProperties->getValue( TablePropertyMap::GAP_HALF, nGapHalf );
32
Calling 'SvRef::operator->'
522
523 std::optional<PropertyMap::Property> oLeftMarginFromStyle = m_aTableProperties->getProperty(PROP_LEFT_MARGIN);
524 if (oLeftMarginFromStyle)
525 {
526 oLeftMarginFromStyle->second >>= nLeftMargin;
527 // don't need to erase, we will push back the adjusted value
528 // of this (or the direct formatting, if that exists) later
529 }
530 m_aTableProperties->getValue( TablePropertyMap::LEFT_MARGIN, nLeftMargin );
531
532 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_LEFT,
533 rInfo.nLeftBorderDistance );
534 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_RIGHT,
535 rInfo.nRightBorderDistance );
536 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_TOP,
537 rInfo.nTopBorderDistance );
538 m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_BOTTOM,
539 rInfo.nBottomBorderDistance );
540
541 table::TableBorderDistances aDistances;
542 aDistances.IsTopDistanceValid =
543 aDistances.IsBottomDistanceValid =
544 aDistances.IsLeftDistanceValid =
545 aDistances.IsRightDistanceValid = true;
546 aDistances.TopDistance = static_cast<sal_Int16>( rInfo.nTopBorderDistance );
547 aDistances.BottomDistance = static_cast<sal_Int16>( rInfo.nBottomBorderDistance );
548 aDistances.LeftDistance = static_cast<sal_Int16>( rInfo.nLeftBorderDistance );
549 aDistances.RightDistance = static_cast<sal_Int16>( rInfo.nRightBorderDistance );
550
551 m_aTableProperties->Insert( PROP_TABLE_BORDER_DISTANCES, uno::makeAny( aDistances ) );
552
553 if (!rFrameProperties.empty())
554 lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance);
555
556 // Set table above/bottom spacing to 0.
557 m_aTableProperties->Insert( PROP_TOP_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );
558 m_aTableProperties->Insert( PROP_BOTTOM_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );
559
560 //table border settings
561 table::TableBorder aTableBorder;
562 table::BorderLine2 aBorderLine, aLeftBorder, aRightBorder;
563
564 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine))
565 {
566 aTableBorder.TopLine = aBorderLine;
567 aTableBorder.IsTopLineValid = true;
568 }
569 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine))
570 {
571 aTableBorder.BottomLine = aBorderLine;
572 aTableBorder.IsBottomLineValid = true;
573 }
574 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder))
575 {
576 aTableBorder.LeftLine = aLeftBorder;
577 aTableBorder.IsLeftLineValid = true;
578 }
579 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo,
580 aRightBorder))
581 {
582 aTableBorder.RightLine = aRightBorder;
583 aTableBorder.IsRightLineValid = true;
584 }
585 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine))
586 {
587 aTableBorder.HorizontalLine = aBorderLine;
588 aTableBorder.IsHorizontalLineValid = true;
589 }
590 if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine))
591 {
592 aTableBorder.VerticalLine = aBorderLine;
593 aTableBorder.IsVerticalLineValid = true;
594 }
595
596 aTableBorder.Distance = 0;
597 aTableBorder.IsDistanceValid = false;
598
599 m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::makeAny( aTableBorder ) );
600
601#ifdef DBG_UTIL
602 lcl_debug_TableBorder(aTableBorder);
603#endif
604
605 // Table position in Office is computed in 2 different ways :
606 // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd),
607 // so table's position depends on table's cells margin
608 // - nested tables: the goal is to have left-most border starting at table_indent pos
609
610 // Only top level table position depends on border width of Column A.
611 if ( !m_aCellProperties.empty() && !m_aCellProperties[0].empty() )
612 {
613 // aLeftBorder already contains tblBorder; overwrite if cell is different.
614 std::optional<PropertyMap::Property> aCellBorder
615 = m_aCellProperties[0][0]->getProperty(PROP_LEFT_BORDER);
616 if ( aCellBorder )
617 aCellBorder->second >>= aLeftBorder;
618 aCellBorder = m_aCellProperties[0][0]->getProperty(PROP_RIGHT_BORDER);
619 if (aCellBorder)
620 aCellBorder->second >>= aRightBorder;
621 }
622 if (rInfo.nNestLevel == 1 && aLeftBorder.LineWidth && !rFrameProperties.empty())
623 {
624 lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5);
625 }
626 lcl_adjustBorderDistance(rInfo, aLeftBorder, aRightBorder);
627
628 // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
629 // the default behavior when DOCX doesn't define "compatibilityMode" option is to add the cell spacing
630
631 // Undefined should not be possible any more for DOCX, but it is for RTF.
632 // In any case, continue to treat undefined as version 12 during import.
633 sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();
634
635 if (((nMode < 0) || (0 < nMode && nMode <= 14)) && rInfo.nNestLevel == 1)
636 {
637 const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf - rInfo.nLeftBorderDistance;
638 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nAdjustedMargin ) );
639 }
640 else
641 {
642 // Writer starts a table in the middle of the border.
643 // Word starts a table at the left edge of the border,
644 // so emulate that by adding the half the width. (also see docxattributeoutput)
645 if ( rInfo.nNestLevel > 1 && nLeftMargin < 0 )
646 nLeftMargin = 0;
647 const sal_Int32 nAdjustedMargin = nLeftMargin - nGapHalf + (aLeftBorder.LineWidth / 2);
648 m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nAdjustedMargin ) );
649 }
650
651 sal_Int32 nTableWidth = 0;
652 sal_Int32 nTableWidthType = text::SizeType::FIX;
653 m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH, nTableWidth );
654 m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType );
655 if( nTableWidthType == text::SizeType::FIX )
656 {
657 if( nTableWidth > 0 )
658 m_aTableProperties->Insert( PROP_WIDTH, uno::makeAny( nTableWidth ));
659 else
660 {
661 // tdf#109524: If there is no width for the table, make it simply 100% by default.
662 // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87)
663 nTableWidth = 100;
664 nTableWidthType = text::SizeType::VARIABLE;
665 }
666 }
667 if (nTableWidthType != text::SizeType::FIX)
668 {
669 m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::makeAny( sal_Int16( nTableWidth ) ) );
670 m_aTableProperties->Insert( PROP_IS_WIDTH_RELATIVE, uno::makeAny( true ) );
671 }
672
673 sal_Int32 nHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
674 // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties
675 if ( !m_aTableProperties->getValue( TablePropertyMap::HORI_ORIENT, nHoriOrient ) )
676 lcl_extractHoriOrient( rFrameProperties, nHoriOrient );
677 m_aTableProperties->Insert( PROP_HORI_ORIENT, uno::makeAny( sal_Int16(nHoriOrient) ) );
678 //fill default value - if not available
679 m_aTableProperties->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(0)), false);
680
681 // if table is only a single row, and row is set as don't split, set the same value for the whole table.
682 if( m_aRowProperties.size() == 1 && m_aRowProperties[0] )
683 {
684 std::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED);
685 if( oSplitAllowed )
686 {
687 bool bRowCanSplit = true;
688 oSplitAllowed->second >>= bRowCanSplit;
689 if( !bRowCanSplit )
690 m_aTableProperties->Insert( PROP_SPLIT, uno::makeAny(bRowCanSplit) );
691 }
692 }
693
694 rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();
695 rInfo.aTablePropertyIds = m_aTableProperties->GetPropertyIds();
696
697#ifdef DBG_UTIL
698 TagLogger::getInstance().startElement("debug.tableprops");
699 m_aTableProperties->dumpXml();
700 TagLogger::getInstance().endElement();
701#endif
702
703 }
704
705 return pTableStyle;
706}
707
708CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges)
709{
710#ifdef DBG_UTIL
711 TagLogger::getInstance().startElement("getCellProperties");
712#endif
713
714 CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() );
715
716 if ( m_aCellProperties.empty() )
717 {
718 #ifdef DBG_UTIL
719 TagLogger::getInstance().endElement();
720 #endif
721 return aCellProperties;
722 }
723 // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties
724 PropertyMapVector2::const_iterator aRowOfCellsIterator = m_aCellProperties.begin();
725 PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd = m_aCellProperties.end();
726 PropertyMapVector2::const_iterator aLastRowIterator = m_aCellProperties.end() - 1;
727 sal_Int32 nRow = 0;
728
729 css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray();
730 PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
731 while( aRowOfCellsIterator != aRowOfCellsIteratorEnd )
732 {
733 //aRowOfCellsIterator points to a vector of PropertyMapPtr
734 PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin();
735 PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end();
736
737 sal_Int32 nRowStyleMask = 0;
738
739 if (aRowOfCellsIterator==m_aCellProperties.begin())
740 {
741 if(rInfo.nTblLook&0x20)
742 nRowStyleMask |= CNF_FIRST_ROW0x800; // first row style used
743 }
744 else if (aRowOfCellsIterator==aLastRowIterator)
745 {
746 if(rInfo.nTblLook&0x40)
747 nRowStyleMask |= CNF_LAST_ROW0x400; // last row style used
748 }
749 else if (*aRowIter && (*aRowIter)->isSet(PROP_TBL_HEADER))
750 nRowStyleMask |= CNF_FIRST_ROW0x800; // table header implies first row
751 if(!nRowStyleMask) // if no row style used yet
752 {
753 // banding used only if not first and or last row style used
754 if(!(rInfo.nTblLook&0x200))
755 { // hbanding used
756 int n = nRow + 1;
757 if(rInfo.nTblLook&0x20)
758 n++;
759 if(n & 1)
760 nRowStyleMask = CNF_ODD_HBAND0x020;
761 else
762 nRowStyleMask = CNF_EVEN_HBAND0x010;
763 }
764 }
765
766 // Note that this is intentionally called "cell" and not "column".
767 // Don't make the mistake that all cell x's will be in the same column.
768 // Merged cells (grid span) in a row will affect the actual column. (fake cells were added to handle gridBefore/After)
769 sal_Int32 nCell = 0;
770 pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
771 beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
772
773 while( aCellIterator != aCellIteratorEnd )
774 {
775 PropertyMapPtr pAllCellProps( new PropertyMap );
776
777 PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1;
778 bool bIsEndCol = aCellIterator == aLastCellIterator;
779 bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator;
780
781 //aCellIterator points to a PropertyMapPtr;
782 if( *aCellIterator )
783 {
784 // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177)
785 (*aCellIterator)->Erase(META_PROP_VERTICAL_BORDER);
786 (*aCellIterator)->Erase(META_PROP_HORIZONTAL_BORDER);
787
788 pAllCellProps->InsertProps(rInfo.pTableDefaults);
789
790 sal_Int32 nCellStyleMask = 0;
791 if (aCellIterator==aRowOfCellsIterator->begin())
792 {
793 if(rInfo.nTblLook&0x80)
794 nCellStyleMask = CNF_FIRST_COLUMN0x200; // first col style used
795 }
796 else if (bIsEndCol)
797 {
798 if(rInfo.nTblLook&0x100)
799 nCellStyleMask = CNF_LAST_COLUMN0x100; // last col style used
800 }
801 if(!nCellStyleMask) // if no cell style is used yet
802 {
803 if(!(rInfo.nTblLook&0x400))
804 { // vbanding used
805 int n = nCell + 1;
806 if(rInfo.nTblLook&0x80)
807 n++;
808 if(n & 1)
809 nCellStyleMask = CNF_ODD_VBAND0x080;
810 else
811 nCellStyleMask = CNF_EVEN_VBAND0x040;
812 }
813 }
814 sal_Int32 nCnfStyleMask = nCellStyleMask + nRowStyleMask;
815 if(nCnfStyleMask == CNF_FIRST_COLUMN0x200 + CNF_FIRST_ROW0x800)
816 nCnfStyleMask |= CNF_FIRST_ROW_FIRST_COLUMN0x004;
817 else if(nCnfStyleMask == CNF_FIRST_COLUMN0x200 + CNF_LAST_ROW0x400)
818 nCnfStyleMask |= CNF_LAST_ROW_FIRST_COLUMN0x001;
819 else if(nCnfStyleMask == CNF_LAST_COLUMN0x100 + CNF_FIRST_ROW0x800)
820 nCnfStyleMask |= CNF_FIRST_ROW_LAST_COLUMN0x008;
821 else if(nCnfStyleMask == CNF_LAST_COLUMN0x100 + CNF_LAST_ROW0x400)
822 nCnfStyleMask |= CNF_LAST_ROW_LAST_COLUMN0x002;
823
824 if ( rInfo.pTableStyle )
825 {
826 PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask );
827
828 // Check if we need to clean up some empty border definitions to match what Word does.
829 static const PropertyIds pBorders[] =
830 {
831 PROP_TOP_BORDER, PROP_LEFT_BORDER, PROP_BOTTOM_BORDER, PROP_RIGHT_BORDER
832 };
833 for (const PropertyIds& rBorder : pBorders)
834 {
835 std::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder);
836 std::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder);
837 if (oStyleCellBorder && oDirectCellBorder)
838 {
839 // We have a cell border from the table style and as direct formatting as well.
840 table::BorderLine2 aStyleCellBorder = oStyleCellBorder->second.get<table::BorderLine2>();
841 table::BorderLine2 aDirectCellBorder = oDirectCellBorder->second.get<table::BorderLine2>();
842 if (aStyleCellBorder.LineStyle != table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE)
843 {
844 // The style one would be visible, but then cleared away as direct formatting.
845 // Delete both, so that table formatting can become visible.
846 pStyleProps->Erase(rBorder);
847 (*aCellIterator)->Erase(rBorder);
848 }
849 else
850 {
851 std::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder);
852 if (oTableBorder)
853 {
854 table::BorderLine2 aTableBorder = oTableBorder->second.get<table::BorderLine2>();
855 // Both style and direct formatting says that the cell has no border.
856 bool bNoCellBorder = aStyleCellBorder.LineStyle == table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE;
857 if (aTableBorder.LineStyle != table::BorderLineStyle::NONE && bNoCellBorder)
858 {
859 // But at a table-level, there is a border, then again delete both cell properties.
860 pStyleProps->Erase(rBorder);
861 (*aCellIterator)->Erase(rBorder);
862 }
863 }
864 }
865 }
866 }
867
868 pAllCellProps->InsertProps( pStyleProps );
869 }
870
871 // Remove properties from style/row that aren't allowed in cells
872 pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
873 pAllCellProps->Erase( PROP_TBL_HEADER );
874
875 // Then add the cell properties
876 pAllCellProps->InsertProps(*aCellIterator);
877 std::swap(*(*aCellIterator), *pAllCellProps );
878
879#ifdef DBG_UTIL
880 TagLogger::getInstance().startElement("cell");
881 TagLogger::getInstance().attribute("cell", nCell);
882 TagLogger::getInstance().attribute("row", nRow);
883#endif
884
885 // Do not apply horizontal and vertical borders to a one cell table.
886 if (m_aCellProperties.size() <= 1 && aRowOfCellsIterator->size() <= 1)
887 {
888 rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER);
889 rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER);
890 }
891 // Do not apply vertical borders to a one column table.
892 else if (m_aCellProperties.size() > 1 && aRowOfCellsIterator->size() <= 1)
893 {
894 bool isOneCol = true;
895 for (size_t i = nRow; i < m_aCellProperties.size(); i++)
896 {
897 if (m_aCellProperties[i].size() > 1)
898 {
899 isOneCol = false;
900 break;
901 }
902 }
903 if (isOneCol)
904 rInfo.pTableBorders->Erase(META_PROP_VERTICAL_BORDER);
905 }
906 // Do not apply horizontal borders to a one row table.
907 else if (m_aCellProperties.size() == 1 && aRowOfCellsIterator->size() > 1)
908 {
909 rInfo.pTableBorders->Erase(META_PROP_HORIZONTAL_BORDER);
910 }
911
912 // tdf#129452 Checking if current cell is vertically merged with all the other cells below to the bottom.
913 // This must be done in order to apply the bottom border of the table to the first cell in a vertical merge.
914 std::optional<PropertyMap::Property> oProp = m_aCellProperties[nRow][nCell]->getProperty(PROP_VERTICAL_MERGE);
915 bool bMergedVertically = oProp && oProp->second.get<bool>(); // starting cell
916 if ( bMergedVertically )
917 {
918 const sal_uInt32 nColumn = m_rDMapper_Impl.getTableManager().findColumn(nRow, nCell);
919 sal_Int32 nLastMergedRow = 0;
920 for (size_t i = nRow + 1; bMergedVertically && i < m_aCellProperties.size(); i++)
921 {
922 const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(i, nColumn);
923 if ( m_aCellProperties[i].size() > sal::static_int_cast<std::size_t>(nColumnCell) )
924 {
925 oProp = m_aCellProperties[i][nColumnCell]->getProperty(PROP_VERTICAL_MERGE);
926 bMergedVertically = oProp && !oProp->second.get<bool>(); //continuing cell
927 if ( bMergedVertically )
928 nLastMergedRow = i;
929 }
930 else
931 bMergedVertically = false;
932 }
933
934 // Only consider the bottom border setting from the last merged cell.
935 // 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.
936 if ( nRow < nLastMergedRow )
937 {
938 (*aCellIterator)->Erase(PROP_BOTTOM_BORDER);
939 const sal_uInt32 nColumnCell = m_rDMapper_Impl.getTableManager().findColumnCell(nLastMergedRow, nColumn);
940 lcl_mergeBorder( PROP_BOTTOM_BORDER, m_aCellProperties[nLastMergedRow][nColumnCell], *aCellIterator );
941 }
942 }
943
944 const sal_uInt32 nFirstCell = m_rDMapper_Impl.getTableManager().getGridBefore(nRow);
945 const sal_uInt32 nLastCell = m_aCellProperties[nRow].size() - m_rDMapper_Impl.getTableManager().getGridAfter(nRow) - 1;
946 lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nFirstCell, nLastCell, nRow, bIsEndRow, bMergedVertically );
947
948 //now set the default left+right border distance TODO: there's an sprm containing the default distance!
949 aCellIterator->get()->Insert( PROP_LEFT_BORDER_DISTANCE,
950 uno::makeAny(rInfo.nLeftBorderDistance ), false);
951 aCellIterator->get()->Insert( PROP_RIGHT_BORDER_DISTANCE,
952 uno::makeAny(rInfo.nRightBorderDistance ), false);
953 aCellIterator->get()->Insert( PROP_TOP_BORDER_DISTANCE,
954 uno::makeAny(rInfo.nTopBorderDistance ), false);
955 aCellIterator->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE,
956 uno::makeAny(rInfo.nBottomBorderDistance ), false);
957
958 // Horizontal merge is not a UNO property, extract that info here to rMerges, and then remove it from the map.
959 const std::optional<PropertyMap::Property> aHorizontalMergeVal = (*aCellIterator)->getProperty(PROP_HORIZONTAL_MERGE);
960 if (aHorizontalMergeVal)
961 {
962 if (aHorizontalMergeVal->second.get<bool>())
963 {
964 // first cell in a merge
965 HorizontallyMergedCell aMerge(nRow, nCell);
966 rMerges.push_back(aMerge);
967 }
968 else if (!rMerges.empty())
969 {
970 // resuming an earlier merge
971 HorizontallyMergedCell& rMerge = rMerges.back();
972 rMerge.m_nLastRow = nRow;
973 rMerge.m_nLastCol = nCell;
974 }
975 (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE);
976 }
977 pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues();
978#ifdef DBG_UTIL
979 TagLogger::getInstance().endElement();
980#endif
981 }
982 ++nCell;
983 ++aCellIterator;
984 }
985 ++nRow;
986 ++aRowOfCellsIterator;
987 ++aRowIter;
988 }
989
990#ifdef DBG_UTIL
991 TagLogger::getInstance().endElement();
992#endif
993
994 return aCellProperties;
995}
996
997/// Do all cells in this row have a CellHideMark property?
998static bool lcl_hideMarks(PropertyMapVector1& rCellProperties)
999{
1000 for (const PropertyMapPtr & p : rCellProperties)
1001 {
1002 // if anything is vertically merged, the row must not be set to fixed
1003 // as Writer's layout doesn't handle that well
1004 if (!p->isSet(PROP_CELL_HIDE_MARK) || p->isSet(PROP_VERTICAL_MERGE))
1005 return false;
1006 }
1007 return true;
1008}
1009
1010/// Are all cells in this row empty?
1011static bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow)
1012{
1013 if (nRow >= static_cast<sal_Int32>(rTableRanges.size()))
1014 {
1015 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1015" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "m_aCellProperties not in sync with rTableRanges?";
::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1015" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "m_aCellProperties not in sync with rTableRanges?"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1015" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "m_aCellProperties not in sync with rTableRanges?";
::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1015" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1016 return false;
1017 }
1018
1019 RowSequence_t rRowSeq = rTableRanges[nRow];
1020 if (!rRowSeq.hasElements())
1021 {
1022 SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1022" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "m_aCellProperties not in sync with rTableRanges?";
::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1022" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "m_aCellProperties not in sync with rTableRanges?"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1022" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "m_aCellProperties not in sync with rTableRanges?"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "m_aCellProperties not in sync with rTableRanges?";
::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1022" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1023 return false;
1024 }
1025
1026 if (!rRowSeq[0][0].is())
1027 {
1028 // This can happen when we can't import the table, e.g. we're inside a
1029 // comment.
1030 SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "rRowSeq[0][0] is an empty reference"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1030" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "rRowSeq[0][0] is an empty reference"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "rRowSeq[0][0] is an empty reference"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1030" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "rRowSeq[0][0] is an empty reference") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1030" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "rRowSeq[0][0] is an empty reference"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "rRowSeq[0][0] is an empty reference"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1030" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1031 return false;
1032 }
1033
1034 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY);
1035 try
1036 {
1037 // See SwXText::Impl::ConvertCell(), we need to compare the start of
1038 // the start and the end of the end. However for our text ranges, only
1039 // the starts are set, so compareRegionStarts() does what we need.
1040 bool bRangesAreNotEqual = std::any_of(rRowSeq.begin(), rRowSeq.end(),
1041 [&xTextRangeCompare](const CellSequence_t& rCellSeq) {
1042 return xTextRangeCompare->compareRegionStarts(rCellSeq[0], rCellSeq[1]) != 0; });
1043 if (bRangesAreNotEqual)
1044 return false;
1045 }
1046 catch (const lang::IllegalArgumentException&)
1047 {
1048 TOOLS_WARN_EXCEPTION( "writerfilter.dmapper", "compareRegionStarts() failed")do { css::uno::Any tools_warn_exception( DbgGetCaughtException
() ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "compareRegionStarts() failed"
<< " " << exceptionToString(tools_warn_exception
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), (
"writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1048" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "compareRegionStarts() failed" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "compareRegionStarts() failed" << " " <<
exceptionToString(tools_warn_exception); ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1048" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "compareRegionStarts() failed" << " " <<
exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1048" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "compareRegionStarts() failed" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "compareRegionStarts() failed" << " " <<
exceptionToString(tools_warn_exception); ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_WARN), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1048" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false); } while (false)
;
1049 return false;
1050 }
1051 return true;
1052}
1053
1054css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties()
1055{
1056#ifdef DBG_UTIL
1057 TagLogger::getInstance().startElement("getRowProperties");
1058#endif
1059
1060 css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() );
1061 sal_Int32 nRow = 0;
1062 for( const auto& rRow : m_aRowProperties )
1063 {
1064#ifdef DBG_UTIL
1065 TagLogger::getInstance().startElement("rowProps.row");
1066#endif
1067 if (rRow)
1068 {
1069 //set default to 'break across pages"
1070 rRow->Insert( PROP_IS_SPLIT_ALLOWED, uno::makeAny(true ), false );
1071 // tblHeader is only our property, remove before the property map hits UNO
1072 rRow->Erase(PROP_TBL_HEADER);
1073
1074 if (lcl_hideMarks(m_aCellProperties[nRow]) && lcl_emptyRow(m_aTableRanges, nRow))
1075 {
1076 // We have CellHideMark on all cells, and also all cells are empty:
1077 // Force the row height to be exactly as specified, and not just as the minimum suggestion.
1078 rRow->Insert(PROP_SIZE_TYPE, uno::makeAny(text::SizeType::FIX));
1079 }
1080
1081 aRowProperties[nRow] = rRow->GetPropertyValues();
1082#ifdef DBG_UTIL
1083 rRow->dumpXml();
1084 lcl_DumpPropertyValues(aRowProperties[nRow]);
1085#endif
1086 }
1087 ++nRow;
1088#ifdef DBG_UTIL
1089 TagLogger::getInstance().endElement();
1090#endif
1091 }
1092
1093#ifdef DBG_UTIL
1094 TagLogger::getInstance().endElement();
1095#endif
1096
1097 return aRowProperties;
1098}
1099
1100// table style has got bigger precedence than docDefault style,
1101// but lower precedence than the paragraph styles and direct paragraph formatting
1102void DomainMapperTableHandler::ApplyParagraphPropertiesFromTableStyle(TableParagraph rParaProp, std::vector< PropertyIds > aAllTableParaProperties, css::beans::PropertyValues rCellProperties)
1103{
1104 for( auto const& eId : aAllTableParaProperties )
1105 {
1106 // apply paragraph and character properties of the table style on table paragraphs
1107 // if there is no direct paragraph formatting
1108 bool bIsParaLevel = rParaProp.m_pPropertyMap->isSet(eId);
1109 if ( !bIsParaLevel || isCharacterProperty(eId) )
1110 {
1111 if ( (eId == PROP_PARA_LEFT_MARGIN || eId == PROP_PARA_FIRST_LINE_INDENT) &&
1112 rParaProp.m_pPropertyMap->isSet(PROP_NUMBERING_RULES) )
1113 {
1114 // indentation of direct numbering has bigger precedence, than table style
1115 continue;
1116 }
1117
1118 OUString sPropertyName = getPropertyName(eId);
1119
1120 if ( bIsParaLevel && ( rParaProp.m_aParaOverrideApplied.find(sPropertyName) != rParaProp.m_aParaOverrideApplied.end() ||
1121 sPropertyName.startsWith("CharFontName") ) )
1122 {
1123 // don't apply table style, if this character property was applied on paragraph level
1124 // (or in the case of paragraph level font name settings to avoid regressions)
1125 continue;
1126 }
1127
1128 auto pCellProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
1129 [&](const beans::PropertyValue& rProp) { return rProp.Name == sPropertyName; });
1130 // this cell applies the table style property
1131 if (pCellProp != rCellProperties.end())
1132 {
1133 bool bDocDefault;
1134 // handle paragraph background color defined in CellColorHandler
1135 if (eId == PROP_FILL_COLOR)
1136 {
1137 // table style defines paragraph background color, use the correct property name
1138 auto pFillStyleProp = std::find_if(rCellProperties.begin(), rCellProperties.end(),
1139 [&](const beans::PropertyValue& rProp) { return rProp.Name == "FillStyle"; });
1140 if ( pFillStyleProp != rCellProperties.end() &&
1141 pFillStyleProp->Value == uno::makeAny(drawing::FillStyle_SOLID) )
1142 {
1143 sPropertyName = "ParaBackColor";
1144 }
1145 else
1146 {
1147 // FillStyle_NONE, skip table style usage for paragraph background color
1148 continue;
1149 }
1150 }
1151 OUString sParaStyleName;
1152 rParaProp.m_rPropertySet->getPropertyValue("ParaStyleName") >>= sParaStyleName;
1153 StyleSheetEntryPtr pEntry = m_rDMapper_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(sParaStyleName);
1154 uno::Any aParaStyle = m_rDMapper_Impl.GetPropertyFromStyleSheet(eId, pEntry, true, true, &bDocDefault);
1155 // A very strange compatibility rule says that the DEFAULT style's specified fontsize of 11 or 12
1156 // or a specified left justify will always be overridden by the table-style.
1157 // Normally this rule is applied, so always do this unless a compatSetting indicates otherwise.
1158 bool bCompatOverride = false;
1159 if ( (eId == PROP_CHAR_HEIGHT || eId == PROP_PARA_ADJUST) && sParaStyleName == m_rDMapper_Impl.GetDefaultParaStyleName() )
1160 {
1161 if ( eId == PROP_CHAR_HEIGHT )
1162 bCompatOverride = aParaStyle == uno::Any(double(11)) || aParaStyle == uno::Any(double(12));
1163 else if ( eId == PROP_PARA_ADJUST )
1164 {
1165 style::ParagraphAdjust eAdjust(style::ParagraphAdjust_CENTER);
1166 aParaStyle >>= eAdjust;
1167 bCompatOverride = eAdjust == style::ParagraphAdjust_LEFT;
1168 }
1169
1170 // The wording is confusing here. Normally, the paragraph style DOES override the table-style.
1171 // But for these two special situations, do not override the table-style. So the default is false.
1172 // If false, then "CompatOverride" the normal behaviour, and apply the table-style's value.
1173 bCompatOverride &= !m_rDMapper_Impl.GetSettingsTable()->GetCompatSettingValue("overrideTableStyleFontSizeAndJustification");
1174 }
1175
1176 // use table style when no paragraph style setting or a docDefault value is applied instead of it
1177 if ( aParaStyle == uno::Any() || bDocDefault || bCompatOverride ) try
1178 {
1179 // check property state of paragraph
1180 uno::Reference<text::XParagraphCursor> xParagraph(
1181 rParaProp.m_rEndParagraph->getText()->createTextCursorByRange(rParaProp.m_rEndParagraph), uno::UNO_QUERY_THROW );
1182 // select paragraph
1183 xParagraph->gotoStartOfParagraph( true );
1184 uno::Reference< beans::XPropertyState > xParaProperties( xParagraph, uno::UNO_QUERY_THROW );
1185 if ( xParaProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE )
1186 {
1187 if ( eId != PROP_FILL_COLOR )
1188 {
1189 // apply style setting when the paragraph doesn't modify it
1190 rParaProp.m_rPropertySet->setPropertyValue( sPropertyName, pCellProp->Value );
1191 }
1192 else
1193 {
1194 // we need this for complete import of table-style based paragraph background color
1195 rParaProp.m_rPropertySet->setPropertyValue( "FillColor", pCellProp->Value );
1196 rParaProp.m_rPropertySet->setPropertyValue( "FillStyle", uno::makeAny(drawing::FillStyle_SOLID) );
1197 }
1198 }
1199 else
1200 {
1201 // apply style setting only on text portions without direct modification of it
1202 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xParagraph, uno::UNO_QUERY);
1203 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
1204 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
1205 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1206 while ( xRunEnum->hasMoreElements() )
1207 {
1208 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
1209 uno::Reference< beans::XPropertyState > xRunProperties( xRun, uno::UNO_QUERY_THROW );
1210 if ( xRunProperties->getPropertyState(sPropertyName) == css::beans::PropertyState_DEFAULT_VALUE )
1211 {
1212 uno::Reference< beans::XPropertySet > xRunPropertySet( xRun, uno::UNO_QUERY_THROW );
1213 xRunPropertySet->setPropertyValue( sPropertyName, pCellProp->Value );
1214 }
1215 }
1216 }
1217 }
1218 catch ( const uno::Exception & )
1219 {
1220 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table style correction")do { css::uno::Any tools_warn_exception( DbgGetCaughtException
() ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "Exception during table style correction"
<< " " << exceptionToString(tools_warn_exception
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1220" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Exception during table style correction"
<< " " << exceptionToString(tools_warn_exception
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Exception during table style correction" << " "
<< exceptionToString(tools_warn_exception); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1220" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Exception during table style correction" <<
" " << exceptionToString(tools_warn_exception)) == 1) {
::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1220" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Exception during table style correction"
<< " " << exceptionToString(tools_warn_exception
)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Exception during table style correction" << " "
<< exceptionToString(tools_warn_exception); ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"
), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1220" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false); } while (false)
;
1221 }
1222 }
1223 }
1224 }
1225}
1226
1227// convert formula range identifier ABOVE, BELOW, LEFT and RIGHT
1228static void lcl_convertFormulaRanges(const uno::Reference<text::XTextTable> & xTable)
1229{
1230 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
1231 uno::Reference<container::XIndexAccess> xTableRows(xTable->getRows(), uno::UNO_QUERY_THROW);
1232 sal_Int32 nRows = xTableRows->getCount();
1233 for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
1234 {
1235 for (sal_Int16 nCol = 0; nCol < MAXTABLECELLS63; ++nCol)
1236 {
1237 try
1238 {
1239 uno::Reference<beans::XPropertySet> xCellProperties(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY_THROW);
1240 uno::Sequence<beans::PropertyValue> aCellGrabBag;
1241 xCellProperties->getPropertyValue("CellInteropGrabBag") >>= aCellGrabBag;
1242 OUString sFormula;
1243 bool bReplace = false;
1244 for (const auto& rProp : std::as_const(aCellGrabBag))
1245 {
1246 if ( rProp.Name == "CellFormulaConverted" )
1247 {
1248 rProp.Value >>= sFormula;
1249 struct RangeDirection
1250 {
1251 OUString m_sName;
1252 sal_Int16 m_nCol;
1253 sal_Int16 m_nRow;
1254 };
1255 static const RangeDirection pDirections[] =
1256 {
1257 { OUString(" LEFT "), -1, 0},
1258 { OUString(" RIGHT "), 1, 0},
1259 { OUString(" ABOVE "), 0, -1},
1260 { OUString(" BELOW "), 0, 1 }
1261 };
1262 for (const RangeDirection& rRange : pDirections)
1263 {
1264 if ( sFormula.indexOf(rRange.m_sName) > -1 )
1265 {
1266 // range starts at the first cell above/below/left/right, but ends at the
1267 // table border or at the first non-value cell after a value cell
1268 bool bFoundFirst = false;
1269 OUString sNextCell;
1270 OUString sLastCell;
1271 OUString sLastValueCell;
1272 // walk through the cells of the range
1273 try
1274 {
1275 sal_Int32 nCell = 0;
1276 while (++nCell)
1277 {
1278 uno::Reference<beans::XPropertySet> xCell(
1279 xCellRange->getCellByPosition(nCol + nCell * rRange.m_nCol, nRow + nCell * rRange.m_nRow),
1280 uno::UNO_QUERY_THROW);
1281 // empty cell or cell with text content is end of the range
1282 uno::Reference<text::XText> xText(xCell, uno::UNO_QUERY_THROW);
1283 sLastCell = xCell->getPropertyValue("CellName").get<OUString>();
1284 if (sNextCell.isEmpty())
1285 sNextCell = sLastCell;
1286 try
1287 {
1288 // accept numbers with comma and percent
1289 OUString sCellText = xText->getString().replace(',', '.');
1290 if (sCellText.endsWith("%"))
1291 sCellText = sCellText.copy(0, sCellText.getLength()-1);
1292 boost::lexical_cast<double>(sCellText);
1293 }
1294 catch( boost::bad_lexical_cast const& )
1295 {
1296 if ( !bFoundFirst )
1297 {
1298 // still search value cells
1299 continue;
1300 }
1301 else
1302 {
1303 // end of range
1304 break;
1305 }
1306 }
1307 sLastValueCell = sLastCell;
1308 bFoundFirst = true;
1309 }
1310 }
1311 catch ( const lang::IndexOutOfBoundsException & )
1312 {
1313 }
1314
1315 if ( !sNextCell.isEmpty() )
1316 {
1317 OUString sRange = "<" + sNextCell + ":" +
1318 ( sLastValueCell.isEmpty() ? sLastCell : sLastValueCell ) + ">";
1319 sFormula = sFormula.replaceAll(rRange.m_sName, sRange);
1320 bReplace = true;
1321 }
1322 }
1323 }
1324
1325 // update formula field
1326 if (bReplace)
1327 {
1328 uno::Reference<text::XText> xCell(xCellRange->getCellByPosition(nCol, nRow), uno::UNO_QUERY);
1329 uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xCell, uno::UNO_QUERY);
1330 uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
1331 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
1332 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1333 while ( xRunEnum->hasMoreElements() )
1334 {
1335 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
1336 uno::Reference< beans::XPropertySet > xRunProperties( xRun, uno::UNO_QUERY_THROW );
1337 if ( xRunProperties->getPropertyValue("TextPortionType") == uno::makeAny(OUString("TextField")) )
1338 {
1339 uno::Reference<text::XTextField> const xField(xRunProperties->getPropertyValue("TextField").get<uno::Reference<text::XTextField>>());
1340 uno::Reference< beans::XPropertySet > xFieldProperties( xField, uno::UNO_QUERY_THROW );
1341 // cell can contain multiple text fields, but only one is handled now (~formula cell)
1342 if ( rProp.Value != xFieldProperties->getPropertyValue("Content") )
1343 continue;
1344 xFieldProperties->setPropertyValue("Content", uno::makeAny(sFormula));
1345 // update grab bag
1346 auto aGrabBag = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aCellGrabBag);
1347 beans::PropertyValue aValue;
1348 aValue.Name = "CellFormulaConverted";
1349 aValue.Value <<= sFormula;
1350 aGrabBag.push_back(aValue);
1351 xCellProperties->setPropertyValue("CellInteropGrabBag", uno::makeAny(comphelper::containerToSequence(aGrabBag)));
1352 }
1353 }
1354 }
1355 }
1356 }
1357 }
1358 catch ( const lang::IndexOutOfBoundsException & )
1359 {
1360 // jump to next table row
1361 break;
1362 }
1363 }
1364 }
1365}
1366
1367void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
1368{
1369#ifdef DBG_UTIL
1370 TagLogger::getInstance().startElement("tablehandler.endTable");
1371#endif
1372
1373 // If we want to make this table a floating one.
1374 std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >
1375 (m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
1376 TableInfo aTableInfo;
1377 aTableInfo.nNestLevel = nestedTableLevel;
1378 aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties);
1
Calling 'DomainMapperTableHandler::endTableGetTableStyle'
1379 // expands to uno::Sequence< Sequence< beans::PropertyValues > >
1380
1381 std::vector<HorizontallyMergedCell> aMerges;
1382 CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges);
1383
1384 css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties();
1385
1386#ifdef DBG_UTIL
1387 lcl_DumpPropertyValueSeq(aRowProperties);
1388#endif
1389
1390 if (!m_aTableRanges.empty())
1391 {
1392 uno::Reference<text::XTextRange> xStart;
1393 uno::Reference<text::XTextRange> xEnd;
1394
1395 bool bFloating = !aFrameProperties.empty();
1396
1397 // OOXML table style may contain paragraph properties, apply these on cell paragraphs
1398 if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
1399 {
1400 // collect all paragraph properties used in table styles
1401 PropertyMapPtr pAllTableProps( new PropertyMap );
1402 pAllTableProps->InsertProps(aTableInfo.pTableDefaults);
1403 if ( aTableInfo.pTableStyle )
1404 pAllTableProps->InsertProps(aTableInfo.pTableStyle->GetProperties( CNF_ALL0xFFF ));
1405 for (const auto& eId : pAllTableProps->GetPropertyIds())
1406 {
1407 if ( !isParagraphProperty(eId) && !isCharacterProperty(eId) )
1408 pAllTableProps->Erase(eId);
1409 }
1410 std::vector< PropertyIds > aAllTableParaProperties = pAllTableProps->GetPropertyIds();
1411
1412 if ( !aAllTableParaProperties.empty() )
1413 {
1414 TableParagraphVectorPtr pTableParagraphs = m_rDMapper_Impl.getTableManager().getCurrentParagraphs();
1415 for (size_t nRow = 0; nRow < m_aTableRanges.size(); ++nRow)
1416 {
1417 // Note that this is "cell" since you must not treat it as "column".
1418 for (size_t nCell = 0; nCell < m_aTableRanges[nRow].size(); ++nCell)
1419 {
1420 auto rStartPara = m_aTableRanges[nRow][nCell][0];
1421 if (!rStartPara.is())
1422 continue;
1423 auto rEndPara = m_aTableRanges[nRow][nCell][1];
1424 uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rStartPara->getText(), uno::UNO_QUERY);
1425 bool bApply = false;
1426 // search paragraphs of the cell
1427 std::vector<TableParagraph>::iterator aIt = pTableParagraphs->begin();
1428 while ( aIt != pTableParagraphs->end() ) try
1429 {
1430 if (!bApply && xTextRangeCompare->compareRegionStarts(rStartPara, aIt->m_rStartParagraph) == 0)
1431 bApply = true;
1432 if (bApply)
1433 {
1434 bool bEndOfApply = (xTextRangeCompare->compareRegionEnds(rEndPara, aIt->m_rEndParagraph) == 0);
1435 ApplyParagraphPropertiesFromTableStyle(*aIt, aAllTableParaProperties, aCellProperties[nRow][nCell]);
1436 // erase processed paragraph from list of pending paragraphs
1437 aIt = pTableParagraphs->erase(aIt);
1438 if (bEndOfApply)
1439 break;
1440 }
1441 else
1442 ++aIt;
1443 }
1444 catch( const lang::IllegalArgumentException & )
1445 {
1446 // skip compareRegion with nested tables
1447 ++aIt;
1448 }
1449 }
1450 }
1451 }
1452 }
1453
1454 // Additional checks: if we can do this.
1455 if (bFloating && m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements())
1456 {
1457 xStart = m_aTableRanges[0][0][0];
1458 uno::Sequence< uno::Sequence< uno::Reference<text::XTextRange> > >& rLastRow = m_aTableRanges[m_aTableRanges.size() - 1];
1459 if (rLastRow.hasElements())
1460 {
1461 uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1];
1462 xEnd = rLastCell[1];
1463 }
1464 }
1465 uno::Reference<text::XTextTable> xTable;
1466 try
1467 {
1468 if (m_xText.is())
1469 {
1470 xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties);
1471
1472 if (xTable.is())
1473 {
1474 if (!aMerges.empty())
1475 {
1476 static const std::vector<std::u16string_view> aBorderNames
1477 = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
1478
1479 // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
1480 for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it)
1481 {
1482 uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
1483 uno::Reference<beans::XPropertySet> xFirstCell(
1484 xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow),
1485 uno::UNO_QUERY_THROW);
1486 OUString aFirst
1487 = xFirstCell->getPropertyValue("CellName").get<OUString>();
1488 // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
1489 if (it->m_nLastCol != -1)
1490 {
1491 // Save border properties of the first cell
1492 // before merge.
1493 table::BorderLine2 aBorderValues[4];
1494 for (size_t i = 0; i < aBorderNames.size(); ++i)
1495 xFirstCell->getPropertyValue(aBorderNames[i])
1496 >>= aBorderValues[i];
1497
1498 uno::Reference<beans::XPropertySet> xLastCell(
1499 xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow),
1500 uno::UNO_QUERY_THROW);
1501 OUString aLast
1502 = xLastCell->getPropertyValue("CellName").get<OUString>();
1503
1504 uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst);
1505 xCursor->gotoCellByName(aLast, true);
1506
1507 xCursor->mergeRange();
1508
1509 // Handle conflicting properties: mergeRange()
1510 // takes the last cell, Word takes the first
1511 // cell.
1512 for (size_t i = 0; i < aBorderNames.size(); ++i)
1513 {
1514 if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE)
1515 xFirstCell->setPropertyValue(
1516 aBorderNames[i], uno::makeAny(aBorderValues[i]));
1517 }
1518 }
1519 }
1520 }
1521
1522 // convert special range IDs ABOVE, BELOW, LEFT and RIGHT
1523 lcl_convertFormulaRanges(xTable);
1524 }
1525 }
1526 }
1527 catch ( const lang::IllegalArgumentException & )
1528 {
1529 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Conversion to table error")do { css::uno::Any tools_warn_exception( DbgGetCaughtException
() ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "Conversion to table error"
<< " " << exceptionToString(tools_warn_exception
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1529" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Conversion to table error" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Conversion to table error" << " " << exceptionToString
(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1529" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Conversion to table error" << " " <<
exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1529" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Conversion to table error" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Conversion to table error" << " " << exceptionToString
(tools_warn_exception); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1529" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false); } while (false)
;
1530#ifdef DBG_UTIL
1531 TagLogger::getInstance().chars(std::string("failed to import table!"));
1532#endif
1533 }
1534 catch ( const uno::Exception & )
1535 {
1536 TOOLS_INFO_EXCEPTION("writerfilter.dmapper", "Exception during table creation")do { css::uno::Any tools_warn_exception( DbgGetCaughtException
() ); do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "writerfilter.dmapper")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "Exception during table creation"
<< " " << exceptionToString(tools_warn_exception
)) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_INFO), (
"writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1536" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Exception during table creation" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Exception during table creation" << " " <<
exceptionToString(tools_warn_exception); ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1536" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Exception during table creation" << " " <<
exceptionToString(tools_warn_exception)) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1536" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "Exception during table creation" <<
" " << exceptionToString(tools_warn_exception)), 0); }
else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Exception during table creation" << " " <<
exceptionToString(tools_warn_exception); ::sal::detail::log(
(::SAL_DETAIL_LOG_LEVEL_INFO), ("writerfilter.dmapper"), ("/home/maarten/src/libreoffice/core/writerfilter/source/dmapper/DomainMapperTableHandler.cxx"
":" "1536" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false); } while (false)
;
1537 }
1538
1539 // If we have a table with a start and an end position, we should make it a floating one.
1540 // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
1541 if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote)
1542 {
1543 uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
1544 bool bIsRelative = false;
1545 xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative;
1546 if (!bIsRelative)
1547 {
1548 beans::PropertyValue aValue;
1549 aValue.Name = "Width";
1550 aValue.Value = xTableProperties->getPropertyValue("Width");
1551 aFrameProperties.push_back(aValue);
1552 }
1553 else
1554 {
1555 beans::PropertyValue aValue;
1556 aValue.Name = "FrameWidthPercent";
1557 aValue.Value = xTableProperties->getPropertyValue("RelativeWidth");
1558 aFrameProperties.push_back(aValue);
1559
1560 // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
1561 xTableProperties->setPropertyValue("RelativeWidth", uno::makeAny(sal_Int16(100)));
1562 }
1563
1564 // A non-zero left margin would move the table out of the frame, move the frame itself instead.
1565 xTableProperties->setPropertyValue("LeftMargin", uno::makeAny(sal_Int32(0)));
1566
1567 if (nestedTableLevel >= 2)
1568 {
1569 // Floating tables inside a table always stay inside the cell.
1570 aFrameProperties.push_back(
1571 comphelper::makePropertyValue("IsFollowingTextFlow", true));
1572 }
1573
1574 // In case the document ends with a table, we're called after
1575 // SectionPropertyMap::CloseSectionGroup(), so we'll have no idea
1576 // about the text area width, nor can fix this by delaying the text
1577 // frame conversion: just do it here.
1578 // Also, when the anchor is within a table, then do it here as well,
1579 // as xStart/xEnd would not point to the start/end at conversion
1580 // time anyway.
1581 // Next exception: it's pointless to delay the conversion if the
1582 // table is not in the body text.
1583 sal_Int32 nTableWidth = 0;
1584 m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
1585 sal_Int32 nTableWidthType = text::SizeType::FIX;
1586 m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
1587 if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
1588 m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType);
1589 else
1590 {
1591 // 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.
1592 uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY);
1593 // Only execute the conversion if the table is not anchored at
1594 // the start of an outer table cell, that's not yet
1595 // implemented.
1596 if (xTextAppendAndConvert.is() && !bTableStartsAtCellStart)
1597 xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties));
1598 }
1599 }
1600
1601 // We're right after a table conversion.
1602 m_rDMapper_Impl.m_bConvertedTable = true;
1603 }
1604
1605 m_aTableProperties.clear();
1606 m_aCellProperties.clear();
1607 m_aRowProperties.clear();
1608 m_bHadFootOrEndnote = false;
1609
1610#ifdef DBG_UTIL
1611 TagLogger::getInstance().endElement();
1612 TagLogger::getInstance().endElement();
1613#endif
1614}
1615
1616void DomainMapperTableHandler::startRow(const TablePropertyMapPtr& pProps)
1617{
1618 m_aRowProperties.push_back( pProps.get() );
1619 m_aCellProperties.emplace_back( );
1620
1621#ifdef DBG_UTIL
1622 TagLogger::getInstance().startElement("table.row");
1623 if (pProps != nullptr)
1624 pProps->dumpXml();
1625#endif
1626
1627 m_aRowRanges.clear();
1628}
1629
1630void DomainMapperTableHandler::endRow()
1631{
1632 m_aTableRanges.push_back(comphelper::containerToSequence(m_aRowRanges));
1633#ifdef DBG_UTIL
1634 TagLogger::getInstance().endElement();
1635#endif
1636}
1637
1638void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start,
1639 const TablePropertyMapPtr& pProps )
1640{
1641 sal_uInt32 nRow = m_aRowProperties.size();
1642 if ( pProps )
1643 m_aCellProperties[nRow - 1].push_back( pProps.get() );
1644 else
1645 {
1646 // Adding an empty cell properties map to be able to get
1647 // the table defaults properties
1648 TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) );
1649 m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() );
1650 }
1651
1652#ifdef DBG_UTIL
1653 TagLogger::getInstance().startElement("table.cell");
1654 TagLogger::getInstance().startElement("table.cell.start");
1655 TagLogger::getInstance().chars(XTextRangeToString(start));
1656 TagLogger::getInstance().endElement();
1657 if (pProps)
1658 pProps->printProperties();
1659#endif
1660
1661 //add a new 'row' of properties
1662 m_aCellRange.clear();
1663 uno::Reference<text::XTextRange> xStart;
1664 if (start)
1665 xStart = start->getStart();
1666 m_aCellRange.push_back(xStart);
1667}
1668
1669void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end)
1670{
1671#ifdef DBG_UTIL
1672 TagLogger::getInstance().startElement("table.cell.end");
1673 TagLogger::getInstance().chars(XTextRangeToString(end));
1674 TagLogger::getInstance().endElement();
1675 TagLogger::getInstance().endElement();
1676#endif
1677
1678 uno::Reference<text::XTextRange> xEnd;
1679 if (end)
1680 xEnd = end->getEnd();
1681 m_aCellRange.push_back(xEnd);
1682 m_aRowRanges.push_back(comphelper::containerToSequence(m_aCellRange));
1683}
1684
1685void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote)
1686{
1687 m_bHadFootOrEndnote = bHadFootOrEndnote;
1688}
1689
1690DomainMapper_Impl& DomainMapperTableHandler::getDomainMapperImpl()
1691{
1692 return m_rDMapper_Impl;
1693}
1694
1695}
1696
1697/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/tools/ref.hxx

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#ifndef INCLUDED_TOOLS_REF_HXX
20#define INCLUDED_TOOLS_REF_HXX
21
22#include <sal/config.h>
23#include <cassert>
24#include <tools/toolsdllapi.h>
25#include <utility>
26
27/**
28 This implements similar functionality to boost::intrusive_ptr
29*/
30
31namespace tools {
32
33/** T must be a class that extends SvRefBase */
34template<typename T> class SAL_DLLPUBLIC_RTTI__attribute__ ((type_visibility("default"))) SvRef final {
35public:
36 SvRef(): pObj(nullptr) {}
37
38 SvRef(SvRef&& rObj) noexcept
39 {
40 pObj = rObj.pObj;
41 rObj.pObj = nullptr;
42 }
43
44 SvRef(SvRef const & rObj): pObj(rObj.pObj)
45 {
46 if (pObj != nullptr) pObj->AddNextRef();
47 }
48
49 SvRef(T * pObjP): pObj(pObjP)
50 {
51 if (pObj != nullptr) pObj->AddFirstRef();
52 }
53
54 ~SvRef()
55 {
56 if (pObj != nullptr) pObj->ReleaseRef();
20
Taking true branch
21
Calling 'SvRefBase::ReleaseRef'
28
Returning; memory was released
57 }
58
59 void clear()
60 {
61 if (pObj != nullptr) {
62 T * pRefObj = pObj;
63 pObj = nullptr;
64 pRefObj->ReleaseRef();
65 }
66 }
67
68 SvRef & operator =(SvRef const & rObj)
69 {
70 if (rObj.pObj != nullptr) {
71 rObj.pObj->AddNextRef();
72 }
73 T * pRefObj = pObj;
74 pObj = rObj.pObj;
75 if (pRefObj != nullptr) {
76 pRefObj->ReleaseRef();
77 }
78 return *this;
79 }
80
81 SvRef & operator =(SvRef && rObj)
82 {
83 if (pObj != nullptr) {
84 pObj->ReleaseRef();
85 }
86 pObj = rObj.pObj;
87 rObj.pObj = nullptr;
88 return *this;
89 }
90
91 bool is() const { return pObj != nullptr; }
92
93 explicit operator bool() const { return is(); }
94
95 T * get() const { return pObj; }
96
97 T * operator ->() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail
("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 97, __extension__ __PRETTY_FUNCTION__))
; return pObj; }
33
'?' condition is true
34
Use of memory after it is freed
98
99 T & operator *() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail
("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 99, __extension__ __PRETTY_FUNCTION__))
; return *pObj; }
100
101 bool operator ==(const SvRef<T> &rhs) const { return pObj == rhs.pObj; }
102 bool operator !=(const SvRef<T> &rhs) const { return !(*this == rhs); }
103
104private:
105 T * pObj;
106};
107
108/**
109 * This implements similar functionality to std::make_shared.
110 */
111template<typename T, typename... Args>
112SvRef<T> make_ref(Args&& ... args)
113{
114 return SvRef<T>(new T(std::forward<Args>(args)...));
115}
116
117}
118
119/** Classes that want to be referenced-counted via SvRef<T>, should extend this base class */
120class TOOLS_DLLPUBLIC__attribute__ ((visibility("default"))) SvRefBase
121{
122 // work around a clang 3.5 optimization bug: if the bNoDelete is *first*
123 // it mis-compiles "if (--nRefCount == 0)" and never deletes any object
124 unsigned int nRefCount : 31;
125 // the only reason this is not bool is because MSVC cannot handle mixed type bitfields
126 unsigned int bNoDelete : 1;
127
128protected:
129 virtual ~SvRefBase() COVERITY_NOEXCEPT_FALSE;
130
131public:
132 SvRefBase() : nRefCount(0), bNoDelete(1) {}
133 SvRefBase(const SvRefBase &) : nRefCount(0), bNoDelete(1) {}
134
135 SvRefBase & operator=(const SvRefBase &) { return *this; }
136
137 void RestoreNoDelete()
138 { bNoDelete = 1; }
139
140 void AddNextRef()
141 {
142 assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) &&
"Do not add refs to dead objects") ? void (0) : __assert_fail
("nRefCount < (1 << 30) && \"Do not add refs to dead objects\""
, "/home/maarten/src/libreoffice/core/include/tools/ref.hxx",
142, __extension__ __PRETTY_FUNCTION__))
;
143 ++nRefCount;
144 }
145
146 void AddFirstRef()
147 {
148 assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) &&
"Do not add refs to dead objects") ? void (0) : __assert_fail
("nRefCount < (1 << 30) && \"Do not add refs to dead objects\""
, "/home/maarten/src/libreoffice/core/include/tools/ref.hxx",
148, __extension__ __PRETTY_FUNCTION__))
;
149 if( bNoDelete )
150 bNoDelete = 0;
151 ++nRefCount;
152 }
153
154 void ReleaseRef()
155 {
156 assert( nRefCount >= 1)(static_cast <bool> (nRefCount >= 1) ? void (0) : __assert_fail
("nRefCount >= 1", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 156, __extension__ __PRETTY_FUNCTION__))
;
22
Assuming field 'nRefCount' is >= 1
23
'?' condition is true
157 if( --nRefCount == 0 && !bNoDelete)
24
Assuming the condition is true
25
Assuming field 'bNoDelete' is 0
26
Taking true branch
158 {
159 // I'm not sure about the original purpose of this line, but right now
160 // it serves the purpose that anything that attempts to do an AddRef()
161 // after an object is deleted will trip an assert.
162 nRefCount = 1 << 30;
163 delete this;
27
Memory is released
164 }
165 }
166
167 unsigned int GetRefCount() const
168 { return nRefCount; }
169};
170
171template<typename T>
172class SvCompatWeakBase;
173
174/** SvCompatWeakHdl acts as an intermediary between SvCompatWeakRef<T> and T.
175*/
176template<typename T>
177class SvCompatWeakHdl final : public SvRefBase
178{
179 friend class SvCompatWeakBase<T>;
180 T* _pObj;
181
182 SvCompatWeakHdl( T* pObj ) : _pObj( pObj ) {}
183
184public:
185 void ResetWeakBase( ) { _pObj = nullptr; }
186 T* GetObj() { return _pObj; }
187};
188
189/** We only have one place that extends this, in include/sfx2/frame.hxx, class SfxFrame.
190 Its function is to notify the SvCompatWeakHdl when an SfxFrame object is deleted.
191*/
192template<typename T>
193class SvCompatWeakBase
194{
195 tools::SvRef< SvCompatWeakHdl<T> > _xHdl;
196
197public:
198 /** Does not use initializer due to compiler warnings,
199 because the lifetime of the _xHdl object can exceed the lifetime of this class.
200 */
201 SvCompatWeakBase( T* pObj ) { _xHdl = new SvCompatWeakHdl<T>( pObj ); }
202
203 ~SvCompatWeakBase() { _xHdl->ResetWeakBase(); }
204
205 SvCompatWeakHdl<T>* GetHdl() { return _xHdl.get(); }
206};
207
208/** We only have one weak reference in LO, in include/sfx2/frame.hxx, class SfxFrameWeak.
209*/
210template<typename T>
211class SAL_WARN_UNUSED__attribute__((warn_unused)) SvCompatWeakRef
212{
213 tools::SvRef< SvCompatWeakHdl<T> > _xHdl;
214public:
215 SvCompatWeakRef( ) {}
216 SvCompatWeakRef( T* pObj )
217 { if( pObj ) _xHdl = pObj->GetHdl(); }
218#if defined(__COVERITY__)
219 ~SvCompatWeakRef() COVERITY_NOEXCEPT_FALSE {}
220#endif
221 SvCompatWeakRef& operator = ( T * pObj )
222 { _xHdl = pObj ? pObj->GetHdl() : nullptr; return *this; }
223 bool is() const
224 { return _xHdl.is() && _xHdl->GetObj(); }
225 explicit operator bool() const { return is(); }
226 T* operator -> () const
227 { return _xHdl.is() ? _xHdl->GetObj() : nullptr; }
228 operator T* () const
229 { return _xHdl.is() ? _xHdl->GetObj() : nullptr; }
230};
231
232#endif
233
234/* vim:set shiftwidth=4 softtabstop=4 expandtab: */