sc: factor out some more code
[LibreOffice.git] / sw / source / filter / xml / xmltbli.cxx
blob5d55d355b76b177d72d8100b7a2ea104fa282c24
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
22 #include <com/sun/star/frame/XModel.hpp>
23 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
24 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 #include <com/sun/star/text/XTextTable.hpp>
26 #include <com/sun/star/table/XCellRange.hpp>
27 #include <comphelper/servicehelper.hxx>
28 #include <o3tl/numeric.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <sal/log.hxx>
31 #include <svl/itemset.hxx>
32 #include <svl/numformat.hxx>
33 #include <svl/zformat.hxx>
34 #include <sax/tools/converter.hxx>
35 #include <comphelper/configuration.hxx>
36 #include <utility>
37 #include <xmloff/xmlnamespace.hxx>
38 #include <xmloff/namespacemap.hxx>
40 #include <xmloff/families.hxx>
41 #include <xmloff/xmluconv.hxx>
42 #include <xmloff/i18nmap.hxx>
43 #include <editeng/protitem.hxx>
44 #include <editeng/lrspitem.hxx>
45 #include <poolfmt.hxx>
46 #include <fmtfsize.hxx>
47 #include <fmtornt.hxx>
48 #include <fmtfordr.hxx>
49 #include <doc.hxx>
50 #include <IDocumentFieldsAccess.hxx>
51 #include <IDocumentLayoutAccess.hxx>
52 #include <IDocumentStylePoolAccess.hxx>
53 #include <swtable.hxx>
54 #include <swtblfmt.hxx>
55 #include <pam.hxx>
56 #include <unoprnms.hxx>
57 #include <unotbl.hxx>
58 #include <unotextrange.hxx>
59 #include <cellatr.hxx>
60 #include <swddetbl.hxx>
61 #include <ddefld.hxx>
62 #include <sfx2/linkmgr.hxx>
63 #include "xmlimp.hxx"
64 #include "xmltbli.hxx"
65 #include <vcl/svapp.hxx>
66 #include <ndtxt.hxx>
67 #include <unotextcursor.hxx>
68 #include <SwStyleNameMapper.hxx>
69 #include <IDocumentSettingAccess.hxx>
70 #include <names.hxx>
72 #include <algorithm>
73 #include <vector>
74 #include <memory>
76 #include <limits.h>
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::uno;
81 using namespace ::com::sun::star::lang;
82 using namespace ::com::sun::star::text;
83 using namespace ::com::sun::star::table;
84 using namespace ::com::sun::star::xml::sax;
85 using namespace ::xmloff::token;
87 class SwXMLTableCell_Impl
89 OUString m_aStyleName;
91 OUString m_StringValue;
93 OUString m_sFormula; // cell formula; valid if length > 0
94 double m_dValue; // formula value
96 rtl::Reference<SwXMLTableContext> m_xSubTable;
98 const SwStartNode *m_pStartNode;
99 sal_uInt32 m_nRowSpan;
100 sal_uInt32 m_nColSpan;
102 bool m_bProtected : 1;
103 bool m_bHasValue; // determines whether dValue attribute is valid
104 bool mbCovered;
105 bool m_bHasStringValue;
107 public:
109 SwXMLTableCell_Impl( sal_uInt32 nRSpan=1, sal_uInt32 nCSpan=1 ) :
110 m_dValue( 0.0 ),
111 m_pStartNode( nullptr ),
112 m_nRowSpan( nRSpan ),
113 m_nColSpan( nCSpan ),
114 m_bProtected( false ),
115 m_bHasValue( false ),
116 mbCovered( false )
117 , m_bHasStringValue(false)
120 inline void Set( const OUString& rStyleName,
121 sal_uInt32 nRSpan, sal_uInt32 nCSpan,
122 const SwStartNode *pStNd, SwXMLTableContext *pTable,
123 bool bProtect,
124 const OUString* pFormula,
125 bool bHasValue,
126 bool bCovered,
127 double dVal,
128 OUString const*const pStringValue);
130 bool IsUsed() const { return m_pStartNode!=nullptr ||
131 m_xSubTable.is() || m_bProtected;}
133 sal_uInt32 GetRowSpan() const { return m_nRowSpan; }
134 void SetRowSpan( sal_uInt32 nSet ) { m_nRowSpan = nSet; }
135 sal_uInt32 GetColSpan() const { return m_nColSpan; }
136 void SetStyleName(const OUString& rStyleName) { m_aStyleName = rStyleName; }
137 const OUString& GetStyleName() const { return m_aStyleName; }
138 const OUString& GetFormula() const { return m_sFormula; }
139 double GetValue() const { return m_dValue; }
140 bool HasValue() const { return m_bHasValue; }
141 bool IsProtected() const { return m_bProtected; }
142 bool IsCovered() const { return mbCovered; }
143 bool HasStringValue() const { return m_bHasStringValue; }
144 OUString const* GetStringValue() const {
145 return m_bHasStringValue ? &m_StringValue : nullptr;
148 const SwStartNode *GetStartNode() const { return m_pStartNode; }
149 inline void SetStartNode( const SwStartNode *pSttNd );
151 inline SwXMLTableContext *GetSubTable() const;
153 inline void Dispose();
156 inline void SwXMLTableCell_Impl::Set( const OUString& rStyleName,
157 sal_uInt32 nRSpan, sal_uInt32 nCSpan,
158 const SwStartNode *pStNd,
159 SwXMLTableContext *pTable,
160 bool bProtect,
161 const OUString* pFormula,
162 bool bHasVal,
163 bool bCov,
164 double dVal,
165 OUString const*const pStringValue )
167 m_aStyleName = rStyleName;
168 m_nRowSpan = nRSpan;
169 m_nColSpan = nCSpan;
170 m_pStartNode = pStNd;
171 m_xSubTable = pTable;
172 m_dValue = dVal;
173 m_bHasValue = bHasVal;
174 mbCovered = bCov;
175 if (pStringValue)
177 m_StringValue = *pStringValue;
179 m_bHasStringValue = (pStringValue != nullptr);
180 m_bProtected = bProtect;
182 // set formula, if valid
183 if (pFormula != nullptr)
185 m_sFormula = *pFormula;
189 inline void SwXMLTableCell_Impl::SetStartNode( const SwStartNode *pSttNd )
191 m_pStartNode = pSttNd;
192 m_xSubTable = nullptr;
195 inline SwXMLTableContext *SwXMLTableCell_Impl::GetSubTable() const
197 return m_xSubTable.get();
200 inline void SwXMLTableCell_Impl::Dispose()
202 if( m_xSubTable.is() )
203 m_xSubTable = nullptr;
206 class SwXMLTableRow_Impl
208 OUString m_aStyleName;
209 OUString m_aDefaultCellStyleName;
210 std::vector<std::unique_ptr<SwXMLTableCell_Impl>> m_Cells;
211 bool m_bSplitable;
213 public:
215 SwXMLTableRow_Impl( OUString aStyleName, sal_uInt32 nCells,
216 const OUString *pDfltCellStyleName = nullptr );
218 inline SwXMLTableCell_Impl *GetCell( sal_uInt32 nCol );
220 inline void Set( const OUString& rStyleName,
221 const OUString& rDfltCellStyleName );
223 void Expand( sal_uInt32 nCells, bool bOneCell );
225 void SetSplitable( bool bSet ) { m_bSplitable = bSet; }
226 bool IsSplitable() const { return m_bSplitable; }
228 const OUString& GetStyleName() const { return m_aStyleName; }
229 const OUString& GetDefaultCellStyleName() const { return m_aDefaultCellStyleName; }
231 void Dispose();
234 SwXMLTableRow_Impl::SwXMLTableRow_Impl( OUString aStyleName,
235 sal_uInt32 nCells,
236 const OUString *pDfltCellStyleName ) :
237 m_aStyleName(std::move( aStyleName )),
238 m_bSplitable( false )
240 if( pDfltCellStyleName )
241 m_aDefaultCellStyleName = *pDfltCellStyleName;
242 OSL_ENSURE( nCells <= USHRT_MAX,
243 "SwXMLTableRow_Impl::SwXMLTableRow_Impl: too many cells" );
244 if( nCells > USHRT_MAX )
245 nCells = USHRT_MAX;
247 for( sal_uInt32 i=0U; i<nCells; ++i )
249 m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>());
253 inline SwXMLTableCell_Impl *SwXMLTableRow_Impl::GetCell( sal_uInt32 nCol )
255 OSL_ENSURE( nCol < USHRT_MAX,
256 "SwXMLTableRow_Impl::GetCell: column number is too big" );
257 // #i95726# - some fault tolerance
258 OSL_ENSURE( nCol < m_Cells.size(),
259 "SwXMLTableRow_Impl::GetCell: column number is out of bound" );
260 return nCol < m_Cells.size() ? m_Cells[nCol].get() : nullptr;
263 void SwXMLTableRow_Impl::Expand( sal_uInt32 nCells, bool bOneCell )
265 OSL_ENSURE( nCells <= USHRT_MAX,
266 "SwXMLTableRow_Impl::Expand: too many cells" );
267 if( nCells > USHRT_MAX )
268 nCells = USHRT_MAX;
270 sal_uInt32 nColSpan = nCells - m_Cells.size();
271 for (size_t i = m_Cells.size(); i < nCells; ++i)
273 m_Cells.push_back(std::make_unique<SwXMLTableCell_Impl>(
274 1UL, bOneCell ? nColSpan : 1UL));
275 nColSpan--;
278 OSL_ENSURE( nCells <= m_Cells.size(),
279 "SwXMLTableRow_Impl::Expand: wrong number of cells" );
282 inline void SwXMLTableRow_Impl::Set( const OUString& rStyleName,
283 const OUString& rDfltCellStyleName )
285 m_aStyleName = rStyleName;
286 m_aDefaultCellStyleName = rDfltCellStyleName;
289 void SwXMLTableRow_Impl::Dispose()
291 for (auto & pCell : m_Cells)
293 pCell->Dispose();
297 namespace {
299 class SwXMLTableCellContext_Impl : public SvXMLImportContext
301 OUString m_aStyleName;
302 OUString m_sFormula;
303 OUString m_sSaveParaDefault;
304 OUString m_StringValue;
306 rtl::Reference<SwXMLTableContext> m_xMyTable;
308 double m_fValue;
309 bool m_bHasValue;
310 bool m_bHasStringValue;
311 bool m_bValueTypeIsString;
312 bool m_bProtect;
314 sal_uInt32 m_nRowSpan;
315 sal_uInt32 m_nColSpan;
316 sal_uInt32 m_nColRepeat;
318 bool m_bHasTextContent : 1;
319 bool m_bHasTableContent : 1;
321 SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
323 bool HasContent() const { return m_bHasTextContent || m_bHasTableContent; }
324 inline void InsertContent_();
325 inline void InsertContent();
326 inline void InsertContent( SwXMLTableContext *pTable );
328 public:
330 SwXMLTableCellContext_Impl(
331 SwXMLImport& rImport, sal_Int32 nElement,
332 const Reference< xml::sax::XFastAttributeList > & xAttrList,
333 SwXMLTableContext *pTable );
335 virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
336 sal_Int32 nElement,
337 const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
338 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
340 SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
343 /// Handles <table:covered-table-cell>.
344 class SwXMLCoveredTableCellContext : public SvXMLImportContext
346 public:
347 SwXMLCoveredTableCellContext(SwXMLImport& rImport,
348 const Reference<xml::sax::XFastAttributeList>& xAttrList,
349 SwXMLTableContext& rTable);
352 SwXMLCoveredTableCellContext::SwXMLCoveredTableCellContext(
353 SwXMLImport& rImport, const Reference<xml::sax::XFastAttributeList>& xAttrList,
354 SwXMLTableContext& rTable)
355 : SvXMLImportContext(rImport)
357 OUString aStyleName;
358 for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
360 switch (rIter.getToken())
362 case XML_ELEMENT(TABLE, XML_STYLE_NAME):
363 aStyleName = rIter.toString();
364 break;
368 if (!aStyleName.isEmpty())
370 rTable.InsertCoveredCell(aStyleName);
375 SwXMLTableCellContext_Impl::SwXMLTableCellContext_Impl(
376 SwXMLImport& rImport, sal_Int32 /*nElement*/,
377 const Reference< xml::sax::XFastAttributeList > & xAttrList,
378 SwXMLTableContext *pTable ) :
379 SvXMLImportContext( rImport ),
380 m_xMyTable( pTable ),
381 m_fValue( 0.0 ),
382 m_bHasValue( false ),
383 m_bHasStringValue(false),
384 m_bValueTypeIsString(false),
385 m_bProtect( false ),
386 m_nRowSpan( 1 ),
387 m_nColSpan( 1 ),
388 m_nColRepeat( 1 ),
389 m_bHasTextContent( false ),
390 m_bHasTableContent( false )
392 m_sSaveParaDefault = GetImport().GetTextImport()->GetCellParaStyleDefault();
393 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
395 switch( aIter.getToken() )
397 case XML_ELEMENT(TABLE, XML_STYLE_NAME):
398 m_aStyleName = aIter.toString();
399 GetImport().GetTextImport()->SetCellParaStyleDefault(m_aStyleName);
400 break;
401 case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_SPANNED):
402 m_nColSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
403 if (m_nColSpan > 256)
405 SAL_INFO("sw.xml", "ignoring huge table:number-columns-spanned " << m_nColSpan);
406 m_nColSpan = 1;
408 break;
409 case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_SPANNED):
410 m_nRowSpan = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
411 if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && comphelper::IsFuzzing()))
413 SAL_INFO("sw.xml", "ignoring huge table:number-rows-spanned " << m_nRowSpan);
414 m_nRowSpan = 1;
416 break;
417 case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
418 m_nColRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
419 if (m_nColRepeat > 256)
421 SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << m_nColRepeat);
422 m_nColRepeat = 1;
424 break;
425 case XML_ELEMENT(TABLE, XML_FORMULA):
427 OUString sTmp;
428 const sal_uInt16 nPrefix2 = GetImport().GetNamespaceMap().
429 GetKeyByAttrValueQName(aIter.toString(), &sTmp);
430 m_sFormula = XML_NAMESPACE_OOOW == nPrefix2 ? sTmp : aIter.toString();
432 break;
433 case XML_ELEMENT(OFFICE, XML_VALUE):
435 // Writer wrongly uses DBL_MAX to flag error but fails to
436 // check for it after import, so check that here, tdf#139126.
437 double fTmp;
438 if (::sax::Converter::convertDouble(fTmp, aIter.toView()) && fTmp < DBL_MAX)
440 m_fValue = fTmp;
441 m_bHasValue = true;
444 break;
445 case XML_ELEMENT(OFFICE, XML_TIME_VALUE):
447 double fTmp;
448 if (::sax::Converter::convertDuration(fTmp, aIter.toView()))
450 m_fValue = fTmp;
451 m_bHasValue = true;
454 break;
455 case XML_ELEMENT(OFFICE, XML_DATE_VALUE):
457 double fTmp;
458 if (GetImport().GetMM100UnitConverter().convertDateTime(fTmp,
459 aIter.toView()))
461 m_fValue = fTmp;
462 m_bHasValue = true;
465 break;
466 case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
468 bool bTmp(false);
469 if (::sax::Converter::convertBool(bTmp, aIter.toView()))
471 m_fValue = (bTmp ? 1.0 : 0.0);
472 m_bHasValue = true;
475 break;
476 case XML_ELEMENT(TABLE, XML_PROTECT): // for backwards compatibility with SRC629 (and before)
477 case XML_ELEMENT(TABLE, XML_PROTECTED):
479 bool bTmp(false);
480 if (::sax::Converter::convertBool(bTmp, aIter.toView()))
482 m_bProtect = bTmp;
485 break;
486 case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
488 m_StringValue = aIter.toString();
489 m_bHasStringValue = true;
491 break;
492 case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
494 if ("string" == aIter.toView())
496 m_bValueTypeIsString = true;
498 // ignore other types - it would be correct to require
499 // matching value-type and $type-value attributes,
500 // but we've been reading those without checking forever.
502 break;
503 default:
504 SAL_WARN("sw", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString());
509 inline void SwXMLTableCellContext_Impl::InsertContent_()
511 SwStartNode const*const pStartNode( GetTable()->InsertTableSection(nullptr,
512 (m_bHasStringValue && m_bValueTypeIsString &&
513 !m_aStyleName.isEmpty()) ? & m_aStyleName : nullptr) );
514 GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan,
515 pStartNode,
516 nullptr, m_bProtect, &m_sFormula, m_bHasValue, m_fValue,
517 (m_bHasStringValue && m_bValueTypeIsString) ? &m_StringValue : nullptr);
520 inline void SwXMLTableCellContext_Impl::InsertContent()
522 OSL_ENSURE( !HasContent(), "content already there" );
523 m_bHasTextContent = true;
524 InsertContent_();
527 inline void SwXMLTableCellContext_Impl::InsertContent(
528 SwXMLTableContext *pTable )
530 GetTable()->InsertCell( m_aStyleName, m_nRowSpan, m_nColSpan, nullptr, pTable, m_bProtect );
531 m_bHasTableContent = true;
534 css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableCellContext_Impl::createFastChildContext(
535 sal_Int32 nElement,
536 const Reference< xml::sax::XFastAttributeList > & xAttrList )
538 SvXMLImportContext *pContext = nullptr;
540 bool bSubTable = false;
541 if( nElement == XML_ELEMENT(TABLE, XML_TABLE) )
543 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
545 if( aIter.getToken() == XML_ELEMENT(TABLE, XML_IS_SUB_TABLE) )
547 if ( IsXMLToken( aIter, XML_TRUE ) )
548 bSubTable = true;
550 //FIXME: RDFa
554 if( bSubTable )
556 if( !HasContent() )
558 SwXMLTableContext *pTableContext =
559 new SwXMLTableContext( GetSwImport(), GetTable() );
560 pContext = pTableContext;
561 if( GetTable()->IsValid() )
562 InsertContent( pTableContext );
564 GetTable()->SetHasSubTables( true );
567 else
569 if( GetTable()->IsValid() && !HasContent() )
570 InsertContent();
571 // fdo#60842: "office:string-value" overrides text content -> no import
572 if (!(m_bValueTypeIsString && m_bHasStringValue))
574 pContext = GetImport().GetTextImport()->CreateTextChildContext(
575 GetImport(), nElement, xAttrList,
576 XMLTextType::Cell );
580 return pContext;
583 void SwXMLTableCellContext_Impl::endFastElement(sal_Int32 )
585 if( GetTable()->IsValid() )
587 if( m_bHasTextContent )
589 GetImport().GetTextImport()->DeleteParagraph();
590 if( m_nColRepeat > 1 && m_nColSpan == 1 )
592 // The original text is invalid after deleting the last
593 // paragraph
594 Reference < XTextCursor > xSrcTextCursor =
595 GetImport().GetTextImport()->GetText()->createTextCursor();
596 xSrcTextCursor->gotoEnd( true );
598 // Until we have an API for copying we have to use the core.
599 OTextCursorHelper *pSrcTextCursor = dynamic_cast<OTextCursorHelper*>(xSrcTextCursor.get());
600 assert(pSrcTextCursor && "SwXTextCursor missing");
601 SwDoc *pDoc = pSrcTextCursor->GetDoc();
602 const SwPaM *pSrcPaM = pSrcTextCursor->GetPaM();
604 while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() )
606 InsertContent_();
608 OTextCursorHelper *pDstTextCursor = dynamic_cast<OTextCursorHelper*>(GetImport().GetTextImport()->GetCursor().get());
609 assert(pDstTextCursor && "SwXTextCursor missing");
610 SwPaM aSrcPaM(*pSrcPaM->GetMark(), *pSrcPaM->GetPoint());
611 SwPosition aDstPos( *pDstTextCursor->GetPaM()->GetPoint() );
612 pDoc->getIDocumentContentOperations().CopyRange(aSrcPaM, aDstPos, SwCopyFlags::CheckPosInFly);
614 m_nColRepeat--;
618 else if( !m_bHasTableContent )
620 InsertContent();
621 if( m_nColRepeat > 1 && m_nColSpan == 1 )
623 while( m_nColRepeat > 1 && GetTable()->IsInsertCellPossible() )
625 InsertContent_();
626 m_nColRepeat--;
631 GetImport().GetTextImport()->SetCellParaStyleDefault(m_sSaveParaDefault);
634 namespace {
636 class SwXMLTableColContext_Impl : public SvXMLImportContext
638 rtl::Reference<SwXMLTableContext> m_xMyTable;
640 SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
642 public:
644 SwXMLTableColContext_Impl(
645 SwXMLImport& rImport,
646 const Reference< xml::sax::XFastAttributeList > & xAttrList,
647 SwXMLTableContext *pTable );
649 SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
654 SwXMLTableColContext_Impl::SwXMLTableColContext_Impl(
655 SwXMLImport& rImport,
656 const Reference< xml::sax::XFastAttributeList > & xAttrList,
657 SwXMLTableContext *pTable ) :
658 SvXMLImportContext( rImport ),
659 m_xMyTable( pTable )
661 sal_uInt32 nColRep = 1;
662 OUString aStyleName, aDfltCellStyleName;
664 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
666 switch (aIter.getToken())
668 case XML_ELEMENT(TABLE, XML_STYLE_NAME):
669 aStyleName = aIter.toString();
670 break;
671 case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
673 nColRep = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
674 if (nColRep > 256)
676 SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << nColRep);
677 nColRep = 1;
679 break;
681 case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME):
682 aDfltCellStyleName = aIter.toString();
683 break;
684 case XML_ELEMENT(XML, XML_ID):
686 //FIXME where to put this??? columns do not actually exist in writer...
687 break;
689 default:
690 XMLOFF_WARN_UNKNOWN("sw", aIter);
694 sal_Int32 nWidth = MINLAY;
695 bool bRelWidth = true;
696 if( !aStyleName.isEmpty() )
698 const SwFormatFrameSize *pSize;
699 const SfxItemSet *pAutoItemSet = nullptr;
700 if( GetSwImport().FindAutomaticStyle(
701 XmlStyleFamily::TABLE_COLUMN,
702 aStyleName, &pAutoItemSet ) &&
703 pAutoItemSet &&
704 (pSize = pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false )) )
706 nWidth = pSize->GetWidth();
707 bRelWidth = SwFrameSize::Variable == pSize->GetHeightSizeType();
711 if( nWidth )
713 while( nColRep-- && GetTable()->IsInsertColPossible() )
714 GetTable()->InsertColumn( nWidth, bRelWidth, &aDfltCellStyleName );
718 namespace {
720 class SwXMLTableColsContext_Impl : public SvXMLImportContext
722 rtl::Reference<SwXMLTableContext> m_xMyTable;
724 SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
726 public:
728 SwXMLTableColsContext_Impl(
729 SwXMLImport& rImport,
730 SwXMLTableContext *pTable );
732 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
733 sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
735 SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
740 SwXMLTableColsContext_Impl::SwXMLTableColsContext_Impl(
741 SwXMLImport& rImport,
742 SwXMLTableContext *pTable ) :
743 SvXMLImportContext( rImport ),
744 m_xMyTable( pTable )
748 css::uno::Reference< css::xml::sax::XFastContextHandler > SwXMLTableColsContext_Impl::createFastChildContext(
749 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList )
751 SvXMLImportContext *pContext = nullptr;
753 if( nElement == XML_ELEMENT(TABLE, XML_TABLE_COLUMN) &&
754 GetTable()->IsInsertColPossible() )
755 pContext = new SwXMLTableColContext_Impl( GetSwImport(), xAttrList, GetTable() );
756 else
757 XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement);
759 return pContext;
762 namespace {
764 class SwXMLTableRowContext_Impl : public SvXMLImportContext
766 rtl::Reference<SwXMLTableContext> m_xMyTable;
768 sal_uInt32 m_nRowRepeat;
770 SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
772 public:
774 SwXMLTableRowContext_Impl(
775 SwXMLImport& rImport, sal_Int32 nElement,
776 const Reference< xml::sax::XFastAttributeList > & xAttrList,
777 SwXMLTableContext *pTable, bool bInHead=false );
779 virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( sal_Int32 nElement,
780 const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
782 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
784 SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
789 SwXMLTableRowContext_Impl::SwXMLTableRowContext_Impl( SwXMLImport& rImport,
790 sal_Int32 /*nElement*/,
791 const Reference< xml::sax::XFastAttributeList > & xAttrList,
792 SwXMLTableContext *pTable,
793 bool bInHead ) :
794 SvXMLImportContext( rImport ),
795 m_xMyTable( pTable ),
796 m_nRowRepeat( 1 )
798 OUString aStyleName, aDfltCellStyleName;
800 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
802 switch(aIter.getToken())
804 case XML_ELEMENT(TABLE, XML_STYLE_NAME):
805 aStyleName = aIter.toString();
806 break;
807 case XML_ELEMENT(STYLE, XML_NUMBER_ROWS_REPEATED):
809 m_nRowRepeat = static_cast<sal_uInt32>(std::max<sal_Int32>(1, aIter.toInt32()));
810 if (m_nRowRepeat > 8192 || (m_nRowRepeat > 256 && comphelper::IsFuzzing()))
812 SAL_INFO("sw.xml", "ignoring huge table:number-rows-repeated " << m_nRowRepeat);
813 m_nRowRepeat = 1;
815 break;
817 case XML_ELEMENT(STYLE, XML_DEFAULT_CELL_STYLE_NAME):
818 aDfltCellStyleName = aIter.toString();
819 break;
820 case XML_ELEMENT(XML, XML_ID):
821 break;
822 default:
823 XMLOFF_WARN_UNKNOWN("sw", aIter);
826 if( GetTable()->IsValid() )
827 GetTable()->InsertRow( aStyleName, aDfltCellStyleName, bInHead );
830 void SwXMLTableRowContext_Impl::endFastElement(sal_Int32 )
832 if( GetTable()->IsValid() )
834 GetTable()->FinishRow();
836 if( m_nRowRepeat > 1 )
837 GetTable()->InsertRepRows( m_nRowRepeat );
841 css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowContext_Impl::createFastChildContext(
842 sal_Int32 nElement,
843 const Reference< xml::sax::XFastAttributeList > & xAttrList )
845 SvXMLImportContext *pContext = nullptr;
847 if( nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) ||
848 nElement == XML_ELEMENT(LO_EXT, XML_TABLE_CELL) )
850 if( !GetTable()->IsValid() || GetTable()->IsInsertCellPossible() )
851 pContext = new SwXMLTableCellContext_Impl( GetSwImport(), nElement,
852 xAttrList,
853 GetTable() );
855 else if( nElement == XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL) ||
856 nElement == XML_ELEMENT(LO_EXT, XML_COVERED_TABLE_CELL) )
858 if (GetTable()->IsValid() && GetTable()->IsInsertCoveredCellPossible())
860 pContext = new SwXMLCoveredTableCellContext(GetSwImport(), xAttrList, *GetTable());
862 else
864 pContext = new SvXMLImportContext(GetImport());
867 else
868 SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
870 return pContext;
873 namespace {
875 class SwXMLTableRowsContext_Impl : public SvXMLImportContext
877 rtl::Reference<SwXMLTableContext> m_xMyTable;
879 bool m_bHeader;
881 SwXMLTableContext *GetTable() { return m_xMyTable.get(); }
883 public:
885 SwXMLTableRowsContext_Impl( SwXMLImport& rImport,
886 SwXMLTableContext *pTable,
887 bool bHead );
889 virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
890 sal_Int32 nElement,
891 const Reference< xml::sax::XFastAttributeList > & xAttrList ) override;
893 SwXMLImport& GetSwImport() { return static_cast<SwXMLImport&>(GetImport()); }
898 SwXMLTableRowsContext_Impl::SwXMLTableRowsContext_Impl( SwXMLImport& rImport,
899 SwXMLTableContext *pTable,
900 bool bHead ) :
901 SvXMLImportContext( rImport ),
902 m_xMyTable( pTable ),
903 m_bHeader( bHead )
907 css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableRowsContext_Impl::createFastChildContext(
908 sal_Int32 nElement,
909 const Reference< xml::sax::XFastAttributeList > & xAttrList )
911 if( nElement== XML_ELEMENT(TABLE, XML_TABLE_ROW ) &&
912 GetTable()->IsInsertRowPossible() )
913 return new SwXMLTableRowContext_Impl( GetSwImport(), nElement,
914 xAttrList,
915 GetTable(),
916 m_bHeader );
917 SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
918 return nullptr;
921 class SwXMLDDETableContext_Impl : public SvXMLImportContext
923 OUString m_sConnectionName;
924 OUString m_sDDEApplication;
925 OUString m_sDDEItem;
926 OUString m_sDDETopic;
927 bool m_bIsAutomaticUpdate;
929 public:
932 SwXMLDDETableContext_Impl(SwXMLImport& rImport);
934 virtual void SAL_CALL startFastElement(
935 sal_Int32 nElement,
936 const Reference<xml::sax::XFastAttributeList> & xAttrList) override;
938 OUString& GetConnectionName() { return m_sConnectionName; }
939 OUString& GetDDEApplication() { return m_sDDEApplication; }
940 OUString& GetDDEItem() { return m_sDDEItem; }
941 OUString& GetDDETopic() { return m_sDDETopic; }
942 bool GetIsAutomaticUpdate() const { return m_bIsAutomaticUpdate; }
946 SwXMLDDETableContext_Impl::SwXMLDDETableContext_Impl(SwXMLImport& rImport) :
947 SvXMLImportContext(rImport),
948 m_bIsAutomaticUpdate(false)
952 void SwXMLDDETableContext_Impl::startFastElement(
953 sal_Int32 /*nElement*/,
954 const Reference<xml::sax::XFastAttributeList> & xAttrList)
956 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
958 switch (aIter.getToken())
960 case XML_ELEMENT(OFFICE, XML_DDE_APPLICATION):
961 m_sDDEApplication = aIter.toString();
962 break;
963 case XML_ELEMENT(OFFICE, XML_DDE_TOPIC):
964 m_sDDETopic = aIter.toString();
965 break;
966 case XML_ELEMENT(OFFICE, XML_DDE_ITEM):
967 m_sDDEItem = aIter.toString();
968 break;
969 case XML_ELEMENT(OFFICE, XML_NAME):
970 m_sConnectionName = aIter.toString();
971 break;
972 case XML_ELEMENT(OFFICE, XML_AUTOMATIC_UPDATE):
974 bool bTmp(false);
975 if (::sax::Converter::convertBool(bTmp, aIter.toView()))
977 m_bIsAutomaticUpdate = bTmp;
979 break;
981 default:
982 XMLOFF_WARN_UNKNOWN("sw", aIter);
984 // else: unknown attribute namespace
988 // generate a new name for DDE field type (called by lcl_GetDDEFieldType below)
989 static OUString lcl_GenerateFieldTypeName(const OUString& sPrefix, SwTableNode* pTableNode)
991 const OUString sPrefixStr(sPrefix.isEmpty() ? u"_"_ustr : sPrefix);
993 // increase count until we find a name that is not yet taken
994 OUString sName;
995 sal_Int32 nCount = 0;
998 // this is crazy, but just in case all names are taken: exit gracefully
999 if (nCount == SAL_MAX_INT32)
1000 return sName;
1002 ++nCount;
1003 sName = sPrefixStr + OUString::number(nCount);
1005 while (nullptr != pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false));
1007 return sName;
1010 // set table properties
1011 static SwDDEFieldType* lcl_GetDDEFieldType(SwXMLDDETableContext_Impl* pContext,
1012 SwTableNode* pTableNode)
1014 // make command string
1015 const OUString sCommand(pContext->GetDDEApplication()
1016 + OUStringChar(sfx2::cTokenSeparator)
1017 + pContext->GetDDEItem()
1018 + OUStringChar(sfx2::cTokenSeparator)
1019 + pContext->GetDDETopic());
1021 const SfxLinkUpdateMode nType = pContext->GetIsAutomaticUpdate()
1022 ? SfxLinkUpdateMode::ALWAYS
1023 : SfxLinkUpdateMode::ONCALL;
1025 OUString sName(pContext->GetConnectionName());
1027 // field type to be returned
1028 SwDDEFieldType* pType = nullptr;
1030 // valid name?
1031 if (sName.isEmpty())
1033 sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(),
1034 pTableNode);
1036 else
1038 // check for existing DDE field type with the same name
1039 SwDDEFieldType* pOldType = static_cast<SwDDEFieldType*>(pTableNode->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, sName, false));
1040 if (nullptr != pOldType)
1042 // same values -> return old type
1043 if ( (pOldType->GetCmd() == sCommand) &&
1044 (pOldType->GetType() == nType) )
1046 // same name, same values -> return old type!
1047 pType = pOldType;
1049 else
1051 // same name, different values -> think of new name
1052 sName = lcl_GenerateFieldTypeName(pContext->GetDDEApplication(),
1053 pTableNode);
1056 // no old type -> create new one
1059 // create new field type (unless we already have one)
1060 if (nullptr == pType)
1062 // create new field type and return
1063 SwDDEFieldType aDDEFieldType(sName, sCommand, nType);
1064 pType = static_cast<SwDDEFieldType*>(pTableNode->
1065 GetDoc().getIDocumentFieldsAccess().InsertFieldType(aDDEFieldType));
1068 OSL_ENSURE(nullptr != pType, "We really want a SwDDEFieldType here!");
1069 return pType;
1072 class TableBoxIndex
1074 public:
1075 OUString msName;
1076 sal_Int32 mnWidth;
1077 bool mbProtected;
1079 TableBoxIndex( OUString aName, sal_Int32 nWidth,
1080 bool bProtected ) :
1081 msName(std::move( aName )),
1082 mnWidth( nWidth ),
1083 mbProtected( bProtected )
1086 bool operator== ( const TableBoxIndex& rArg ) const
1088 return (rArg.mnWidth == mnWidth) &&
1089 (rArg.mbProtected == mbProtected) &&
1090 (rArg.msName == msName);
1094 class TableBoxIndexHasher
1096 public:
1097 size_t operator() (const TableBoxIndex& rArg) const
1099 return rArg.msName.hashCode() + rArg.mnWidth + (rArg.mbProtected ? 1 : 0);
1103 const SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow,
1104 sal_uInt32 nCol ) const
1106 return (*m_pRows)[nRow]->GetCell( nCol );
1109 SwXMLTableCell_Impl *SwXMLTableContext::GetCell( sal_uInt32 nRow,
1110 sal_uInt32 nCol )
1112 return (*m_pRows)[nRow]->GetCell( nCol );
1116 SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport,
1117 const Reference< xml::sax::XFastAttributeList > & xAttrList ) :
1118 XMLTextTableContext( rImport ),
1119 m_pRows( new SwXMLTableRows_Impl ),
1120 m_pTableNode( nullptr ),
1121 m_pBox1( nullptr ),
1122 m_bOwnsBox1( false ),
1123 m_pSttNd1( nullptr ),
1124 m_pBoxFormat( nullptr ),
1125 m_pLineFormat( nullptr ),
1126 m_bFirstSection( true ),
1127 m_bRelWidth( true ),
1128 m_bHasSubTables( false ),
1129 m_nHeaderRows( 0 ),
1130 m_nCurRow( 0 ),
1131 m_nCurCol( 0 ),
1132 m_nNonMergedCurCol( 0 ),
1133 m_nWidth( 0 )
1135 OUString aName;
1136 OUString sXmlId;
1138 // this method will modify the document directly -> lock SolarMutex
1139 SolarMutexGuard aGuard;
1141 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
1143 const OUString sValue = aIter.toString();
1144 switch(aIter.getToken())
1146 case XML_ELEMENT(TABLE, XML_STYLE_NAME):
1147 m_aStyleName = sValue;
1148 break;
1149 case XML_ELEMENT(TABLE, XML_NAME):
1150 aName = sValue;
1151 break;
1152 case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME):
1153 m_aDfltCellStyleName = sValue;
1154 break;
1155 case XML_ELEMENT(TABLE, XML_TEMPLATE_NAME):
1156 m_aTemplateName = sValue;
1157 break;
1158 case XML_ELEMENT(XML, XML_ID):
1159 sXmlId = sValue;
1160 break;
1161 default:
1162 XMLOFF_WARN_UNKNOWN("sw", aIter);
1166 SwDoc *pDoc = GetSwImport().getDoc();
1168 OUString sTableName;
1169 if( !aName.isEmpty() )
1171 const SwTableFormat *pTableFormat = pDoc->FindTableFormatByName( aName );
1172 if( !pTableFormat )
1173 sTableName = aName;
1175 if( sTableName.isEmpty() )
1177 // Optimization: use import's own map to create unique names, because
1178 // SwDoc::GetUniqueTableName scans all the already present tables,
1179 // builds a bitset using rather complex rules, and that has quadratic
1180 // complexity. Try once, then fallback to SwDoc::GetUniqueTableName
1181 auto& tableNameMap = rImport.GetTableNameMap();
1182 sal_Int32 nextIx = ++tableNameMap[aName];
1183 OUString test = aName.isEmpty()
1184 ? OUString(rImport.GetDefTableName() + OUString::number(nextIx))
1185 : OUString(aName + "_" + OUString::number(nextIx));
1186 if (const SwTableFormat* pExisting = pDoc->FindTableFormatByName(test); !pExisting)
1187 sTableName = test;
1188 else
1189 sTableName = pDoc->GetUniqueTableName();
1190 GetImport().GetTextImport()
1191 ->GetRenameMap().Add( XML_TEXT_RENAME_TYPE_TABLE, aName, sTableName );
1194 Reference< XTextTable > xTable;
1195 SwXTextTable *pXTable = nullptr;
1196 Reference<XMultiServiceFactory> xFactory( GetImport().GetModel(),
1197 UNO_QUERY );
1198 OSL_ENSURE( xFactory.is(), "factory missing" );
1199 if( xFactory.is() )
1201 Reference<XInterface> xIfc = xFactory->createInstance( u"com.sun.star.text.TextTable"_ustr );
1202 OSL_ENSURE( xIfc.is(), "Couldn't create a table" );
1204 if( xIfc.is() )
1205 xTable.set( xIfc, UNO_QUERY );
1208 if( xTable.is() )
1210 xTable->initialize( 1, 1 );
1211 if (auto xPropSet = xTable.query<css::beans::XPropertySet>())
1212 xPropSet->setPropertyValue(UNO_NAME_TABLE_NAME, css::uno::Any(sTableName));
1216 m_xTextContent = xTable;
1217 GetImport().GetTextImport()->InsertTextContent( m_xTextContent );
1219 catch( IllegalArgumentException& )
1221 xTable = nullptr;
1225 if( xTable.is() )
1227 //FIXME
1228 // xml:id for RDF metadata
1229 GetImport().SetXmlId(xTable, sXmlId);
1231 pXTable = dynamic_cast<SwXTextTable*>(xTable.get());
1233 Reference < XCellRange > xCellRange( xTable, UNO_QUERY );
1234 Reference < XCell > xCell = xCellRange->getCellByPosition( 0, 0 );
1235 Reference < XText> xText( xCell, UNO_QUERY );
1236 m_xOldCursor = GetImport().GetTextImport()->GetCursor();
1237 GetImport().GetTextImport()->SetCursor( xText->createTextCursor() );
1239 // take care of open redlines for tables
1240 GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
1242 if( !pXTable )
1243 return;
1245 SwFrameFormat *const pTableFrameFormat = pXTable->GetFrameFormat();
1246 OSL_ENSURE( pTableFrameFormat, "table format missing" );
1247 SwTable *pTable = SwTable::FindTable( pTableFrameFormat );
1248 assert(pTable && "table missing");
1249 m_pTableNode = pTable->GetTableNode();
1250 assert(m_pTableNode && "table node missing");
1252 SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U];
1253 m_pBox1 = pLine1->GetTabBoxes()[0U];
1254 m_pSttNd1 = m_pBox1->GetSttNd();
1257 SwXMLTableContext::SwXMLTableContext( SwXMLImport& rImport,
1258 SwXMLTableContext *pTable ) :
1259 XMLTextTableContext( rImport ),
1260 m_pRows( new SwXMLTableRows_Impl ),
1261 m_pTableNode( pTable->m_pTableNode ),
1262 m_pBox1( nullptr ),
1263 m_bOwnsBox1( false ),
1264 m_pSttNd1( nullptr ),
1265 m_pBoxFormat( nullptr ),
1266 m_pLineFormat( nullptr ),
1267 m_xParentTable( pTable ),
1268 m_bFirstSection( false ),
1269 m_bRelWidth( true ),
1270 m_bHasSubTables( false ),
1271 m_nHeaderRows( 0 ),
1272 m_nCurRow( 0 ),
1273 m_nCurCol( 0 ),
1274 m_nNonMergedCurCol( 0 ),
1275 m_nWidth( 0 )
1279 SwXMLTableContext::~SwXMLTableContext()
1281 if (m_bOwnsBox1)
1282 delete m_pBox1;
1283 m_xColumnDefaultCellStyleNames.reset();
1284 m_pSharedBoxFormats.reset();
1285 m_pRows.reset();
1287 // close redlines on table end nodes
1288 GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
1291 css::uno::Reference<css::xml::sax::XFastContextHandler> SwXMLTableContext::createFastChildContext(
1292 sal_Int32 nElement,
1293 const Reference< xml::sax::XFastAttributeList > & xAttrList )
1295 bool bHeader = false;
1296 switch (nElement)
1298 case XML_ELEMENT(TABLE, XML_TABLE_ROW):
1299 case XML_ELEMENT(LO_EXT, XML_TABLE_ROW):
1300 if( IsInsertRowPossible() )
1301 return new SwXMLTableRowContext_Impl( GetSwImport(), nElement, xAttrList, this );
1302 break;
1303 case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS):
1304 bHeader = true;
1305 [[fallthrough]];
1306 case XML_ELEMENT(TABLE, XML_TABLE_ROWS):
1307 return new SwXMLTableRowsContext_Impl( GetSwImport(), this, bHeader );
1308 case XML_ELEMENT(TABLE, XML_TABLE_HEADER_COLUMNS):
1309 case XML_ELEMENT(TABLE, XML_TABLE_COLUMNS):
1310 // There are slight differences between <table:table-columns> and
1311 // <table:table-columns-groups>. However, none of these are
1312 // supported in Writer (they are Calc-only features), so we
1313 // support column groups by simply using the <table:table-columns>
1314 // token for column groups, too.
1315 case XML_ELEMENT(TABLE, XML_TABLE_COLUMN_GROUP):
1316 if( IsValid() )
1317 return new SwXMLTableColsContext_Impl( GetSwImport(), this );
1318 break;
1319 case XML_ELEMENT(TABLE, XML_TABLE_COLUMN):
1320 case XML_ELEMENT(LO_EXT, XML_TABLE_COLUMN):
1321 if( IsValid() && IsInsertColPossible() )
1322 return new SwXMLTableColContext_Impl( GetSwImport(), xAttrList,
1323 this );
1324 break;
1325 case XML_ELEMENT(OFFICE, XML_DDE_SOURCE):
1326 // save context for later processing (discard old context, if approp.)
1327 if( IsValid() )
1329 m_xDDESource.set(new SwXMLDDETableContext_Impl( GetSwImport() ));
1330 return m_xDDESource;
1332 break;
1334 return nullptr;
1337 void SwXMLTableContext::InsertColumn( sal_Int32 nWidth2, bool bRelWidth2,
1338 const OUString *pDfltCellStyleName )
1340 OSL_ENSURE( m_nCurCol < USHRT_MAX,
1341 "SwXMLTableContext::InsertColumn: no space left" );
1342 if( m_nCurCol >= USHRT_MAX )
1343 return;
1345 if( nWidth2 < MINLAY )
1346 nWidth2 = MINLAY;
1347 else if( nWidth2 > MAX_WIDTH )
1348 nWidth2 = MAX_WIDTH;
1349 m_aColumnWidths.emplace_back(nWidth2, bRelWidth2 );
1350 if( !((pDfltCellStyleName && !pDfltCellStyleName->isEmpty()) ||
1351 m_xColumnDefaultCellStyleNames) )
1352 return;
1354 if( !m_xColumnDefaultCellStyleNames )
1356 m_xColumnDefaultCellStyleNames.emplace();
1357 sal_uLong nCount = m_aColumnWidths.size() - 1;
1358 while( nCount-- )
1359 m_xColumnDefaultCellStyleNames->emplace_back();
1362 if(pDfltCellStyleName)
1363 m_xColumnDefaultCellStyleNames->push_back(*pDfltCellStyleName);
1364 else
1365 m_xColumnDefaultCellStyleNames->push_back(OUString());
1368 sal_Int32 SwXMLTableContext::GetColumnWidth( sal_uInt32 nCol,
1369 sal_uInt32 nColSpan ) const
1371 sal_uInt32 nLast = nCol+nColSpan;
1372 if( nLast > m_aColumnWidths.size() )
1373 nLast = m_aColumnWidths.size();
1375 sal_Int32 nWidth2 = 0;
1376 for( sal_uInt32 i=nCol; i < nLast; ++i )
1377 nWidth2 += m_aColumnWidths[i].width;
1379 return nWidth2;
1382 const OUString & SwXMLTableContext::GetColumnDefaultCellStyleName( sal_uInt32 nCol ) const
1384 if( m_xColumnDefaultCellStyleNames && nCol < m_xColumnDefaultCellStyleNames->size())
1385 return (*m_xColumnDefaultCellStyleNames)[static_cast<size_t>(nCol)];
1387 return EMPTY_OUSTRING;
1390 void SwXMLTableContext::InsertCell( const OUString& rStyleName,
1391 sal_uInt32 nRowSpan, sal_uInt32 nColSpan,
1392 const SwStartNode *pStartNode,
1393 SwXMLTableContext *pTable,
1394 bool bProtect,
1395 const OUString* pFormula,
1396 bool bHasValue,
1397 double fValue,
1398 OUString const*const pStringValue )
1400 OSL_ENSURE( m_nCurCol < GetColumnCount(),
1401 "SwXMLTableContext::InsertCell: row is full" );
1402 OSL_ENSURE( m_nCurRow < USHRT_MAX,
1403 "SwXMLTableContext::InsertCell: table is full" );
1404 if( m_nCurCol >= USHRT_MAX || m_nCurRow > USHRT_MAX )
1405 return;
1407 OSL_ENSURE( nRowSpan >=1, "SwXMLTableContext::InsertCell: row span is 0" );
1408 if( 0 == nRowSpan )
1409 nRowSpan = 1;
1410 OSL_ENSURE( nColSpan >=1, "SwXMLTableContext::InsertCell: col span is 0" );
1411 if( 0 == nColSpan )
1412 nColSpan = 1;
1414 // Until it is possible to add columns here, fix the column span.
1415 sal_uInt32 nColsReq = m_nCurCol + nColSpan;
1416 if( nColsReq > GetColumnCount() )
1418 nColSpan = GetColumnCount() - m_nCurCol;
1419 nColsReq = GetColumnCount();
1422 // Check whether there are cells from a previous line already that reach
1423 // into the current row.
1424 if( m_nCurRow > 0 && nColSpan > 1 )
1426 SwXMLTableRow_Impl *pCurRow = (*m_pRows)[m_nCurRow].get();
1427 sal_uInt32 nLastCol = GetColumnCount() < nColsReq ? GetColumnCount()
1428 : nColsReq;
1429 for( sal_uInt32 i=m_nCurCol+1; i<nLastCol; ++i )
1431 if( pCurRow->GetCell(i)->IsUsed() )
1433 // If this cell is used, the column span is truncated
1434 nColSpan = i - m_nCurCol;
1435 nColsReq = i;
1436 break;
1441 sal_uInt32 nRowsReq = m_nCurRow + nRowSpan;
1442 if( nRowsReq > USHRT_MAX )
1444 nRowSpan = USHRT_MAX - m_nCurRow;
1445 nRowsReq = USHRT_MAX;
1448 // Add columns (if # required columns greater than # columns):
1449 // This should never happen, since we require column definitions!
1450 if ( nColsReq > GetColumnCount() )
1452 for( sal_uInt32 i=GetColumnCount(); i<nColsReq; ++i )
1454 m_aColumnWidths.emplace_back(MINLAY, true );
1456 // adjust columns in *all* rows, if columns must be inserted
1457 for (size_t i = 0; i < m_pRows->size(); ++i)
1458 (*m_pRows)[i]->Expand( nColsReq, i<m_nCurRow );
1461 // Add rows
1462 if (m_pRows->size() < nRowsReq)
1464 for (size_t i = m_pRows->size(); i < nRowsReq; ++i)
1465 m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>(
1466 "", GetColumnCount()));
1469 OUString sStyleName( rStyleName );
1470 if( sStyleName.isEmpty() )
1472 sStyleName = (*m_pRows)[m_nCurRow]->GetDefaultCellStyleName();
1473 if( sStyleName.isEmpty() && m_xColumnDefaultCellStyleNames )
1475 sStyleName = GetColumnDefaultCellStyleName( m_nCurCol );
1476 if( sStyleName.isEmpty() )
1477 sStyleName = m_aDfltCellStyleName;
1481 // Fill the cells
1482 for( sal_uInt32 i=nColSpan; i>0; --i )
1484 for( sal_uInt32 j=nRowSpan; j>0; --j )
1486 const bool bCovered = i != nColSpan || j != nRowSpan;
1487 SwXMLTableCell_Impl *pCell = GetCell( nRowsReq-j, nColsReq-i );
1488 if (!pCell)
1489 throw css::lang::IndexOutOfBoundsException();
1490 pCell->Set( sStyleName, j, i, pStartNode,
1491 pTable, bProtect, pFormula, bHasValue, bCovered, fValue,
1492 pStringValue );
1496 // Set current col to the next (free) column
1497 m_nCurCol = nColsReq;
1498 m_nNonMergedCurCol = nColsReq;
1499 while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
1500 m_nCurCol++;
1503 void SwXMLTableContext::InsertCoveredCell(const OUString& rStyleName)
1505 const IDocumentSettingAccess& rIDSA = GetSwImport().getDoc()->getIDocumentSettingAccess();
1506 bool bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
1507 if (!bWordTableCell)
1509 // Compatibility flag not active, ignore formatting of covered cells.
1510 return;
1513 SwXMLTableCell_Impl* pCell = GetCell(m_nCurRow, m_nNonMergedCurCol);
1514 ++m_nNonMergedCurCol;
1515 if (!pCell)
1517 return;
1520 pCell->SetStyleName(rStyleName);
1523 void SwXMLTableContext::InsertRow( const OUString& rStyleName,
1524 const OUString& rDfltCellStyleName,
1525 bool bInHead )
1527 OSL_ENSURE( m_nCurRow < USHRT_MAX,
1528 "SwXMLTableContext::InsertRow: no space left" );
1529 if( m_nCurRow >= USHRT_MAX )
1530 return;
1532 // Make sure there is at least one column.
1533 if( 0==m_nCurRow && 0 == GetColumnCount() )
1534 InsertColumn( USHRT_MAX, true );
1536 if (m_nCurRow < m_pRows->size())
1538 // The current row has already been inserted because of a row span
1539 // of a previous row.
1540 (*m_pRows)[m_nCurRow]->Set(
1541 rStyleName, rDfltCellStyleName );
1543 else
1545 // add a new row
1546 m_pRows->push_back(std::make_unique<SwXMLTableRow_Impl>(
1547 rStyleName, GetColumnCount(),
1548 &rDfltCellStyleName));
1551 // We start at the first column ...
1552 m_nCurCol=0;
1553 m_nNonMergedCurCol = 0;
1555 // ... but this cell may be occupied already.
1556 while( m_nCurCol<GetColumnCount() && GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
1557 m_nCurCol++;
1559 if( bInHead && m_nHeaderRows == m_nCurRow )
1560 m_nHeaderRows++;
1563 void SwXMLTableContext::InsertRepRows( sal_uInt32 nCount )
1565 const SwXMLTableRow_Impl *pSrcRow = (*m_pRows)[m_nCurRow-1].get();
1566 while( nCount > 1 && IsInsertRowPossible() )
1568 InsertRow( pSrcRow->GetStyleName(), pSrcRow->GetDefaultCellStyleName(),
1569 false );
1570 while( m_nCurCol < GetColumnCount() )
1572 if( !GetCell(m_nCurRow,m_nCurCol)->IsUsed() )
1574 const SwXMLTableCell_Impl *pSrcCell =
1575 GetCell( m_nCurRow-1, m_nCurCol );
1576 InsertCell( pSrcCell->GetStyleName(), 1U,
1577 pSrcCell->GetColSpan(),
1578 InsertTableSection(),
1579 nullptr, pSrcCell->IsProtected(),
1580 &pSrcCell->GetFormula(),
1581 pSrcCell->HasValue(), pSrcCell->GetValue(),
1582 pSrcCell->GetStringValue() );
1585 FinishRow();
1586 nCount--;
1590 void SwXMLTableContext::FinishRow()
1592 // Insert an empty cell at the end of the line if the row is not complete
1593 if( m_nCurCol < GetColumnCount() )
1595 InsertCell( u""_ustr, 1U, GetColumnCount() - m_nCurCol,
1596 InsertTableSection() );
1599 // Move to the next row.
1600 m_nCurRow++;
1603 const SwStartNode *SwXMLTableContext::GetPrevStartNode( sal_uInt32 nRow,
1604 sal_uInt32 nCol ) const
1606 const SwXMLTableCell_Impl *pPrevCell = nullptr;
1607 if( GetColumnCount() == nCol )
1609 // The last cell is the right one here.
1610 pPrevCell = GetCell( m_pRows->size() - 1U, GetColumnCount() - 1 );
1612 else if( nCol > 0 )
1614 // The previous cell in this row.
1615 pPrevCell = GetCell( nRow, nCol-1 );
1617 else if( nRow > 0 )
1619 // The last cell from the previous row.
1620 pPrevCell = GetCell( nRow-1, GetColumnCount()-1 );
1623 const SwStartNode *pSttNd = nullptr;
1624 if( pPrevCell )
1626 if( pPrevCell->GetStartNode() )
1627 pSttNd = pPrevCell->GetStartNode();
1628 // #i95726# - Some fault tolerance
1629 // else
1630 else if ( pPrevCell->GetSubTable() )
1631 pSttNd = pPrevCell->GetSubTable()->GetLastStartNode();
1633 OSL_ENSURE( pSttNd != nullptr,
1634 "table corrupt" );
1637 return pSttNd;
1640 void SwXMLTableContext::FixRowSpan( sal_uInt32 nRow, sal_uInt32 nCol,
1641 sal_uInt32 nColSpan )
1643 sal_uInt32 nLastCol = nCol + nColSpan;
1644 for( sal_uInt32 i = nCol; i < nLastCol; i++ )
1646 sal_uInt32 j = nRow;
1647 sal_uInt32 nRowSpan = 1;
1648 SwXMLTableCell_Impl *pCell = GetCell( j, i );
1649 while( pCell && pCell->GetRowSpan() > 1 )
1651 pCell->SetRowSpan( nRowSpan++ );
1652 pCell = j > 0 ? GetCell( --j, i ) : nullptr;
1657 void SwXMLTableContext::ReplaceWithEmptyCell( sal_uInt32 nRow, sal_uInt32 nCol, bool bRows )
1659 const SwStartNode *pPrevSttNd = GetPrevStartNode( nRow, nCol );
1660 const SwStartNode *pSttNd = InsertTableSection( pPrevSttNd );
1662 const SwXMLTableCell_Impl *pCell = GetCell( nRow, nCol );
1663 sal_uInt32 nLastRow = bRows ? nRow + pCell->GetRowSpan() : nRow + 1;
1664 sal_uInt32 nLastCol = nCol + pCell->GetColSpan();
1666 for( sal_uInt32 i=nRow; i<nLastRow; i++ )
1668 SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
1669 for( sal_uInt32 j=nCol; j<nLastCol; j++ )
1670 pRow->GetCell( j )->SetStartNode( pSttNd );
1675 SwTableBox *SwXMLTableContext::NewTableBox( const SwStartNode *pStNd,
1676 SwTableLine *pUpper )
1678 // The topmost table is the only table that maintains the two members
1679 // pBox1 and bFirstSection.
1680 if( m_xParentTable.is() )
1681 return m_xParentTable->NewTableBox( pStNd, pUpper );
1683 SwTableBox *pBox;
1685 if( m_pBox1 &&
1686 m_pBox1->GetSttNd() == pStNd )
1688 // if the StartNode is equal to the StartNode of the initially
1689 // created box, we use this box
1690 pBox = m_pBox1;
1691 pBox->SetUpper( pUpper );
1692 m_pBox1 = nullptr;
1693 m_bOwnsBox1 = false;
1695 else
1696 pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
1698 return pBox;
1701 SwTableBoxFormat* SwXMLTableContext::GetSharedBoxFormat(
1702 SwTableBox* pBox,
1703 const OUString& rStyleName,
1704 sal_Int32 nColumnWidth,
1705 bool bProtected,
1706 bool bMayShare,
1707 bool& bNew,
1708 bool* pModifyLocked )
1710 if ( m_pSharedBoxFormats == nullptr )
1711 m_pSharedBoxFormats.reset(new map_BoxFormat);
1713 SwTableBoxFormat* pBoxFormat2;
1715 TableBoxIndex aKey( rStyleName, nColumnWidth, bProtected );
1716 map_BoxFormat::iterator aIter = m_pSharedBoxFormats->find( aKey );
1717 if ( aIter == m_pSharedBoxFormats->end() )
1719 // unknown format so far -> construct a new one
1721 // get the old format, and reset all attributes
1722 // (but preserve FillOrder)
1723 pBoxFormat2 = pBox->ClaimFrameFormat();
1724 SwFormatFillOrder aFillOrder( pBoxFormat2->GetFillOrder() );
1725 pBoxFormat2->ResetAllFormatAttr(); // #i73790# - method renamed
1726 pBoxFormat2->SetFormatAttr( aFillOrder );
1727 bNew = true; // it's a new format now
1729 // share this format, if allowed
1730 if ( bMayShare )
1731 (*m_pSharedBoxFormats)[ aKey ] = pBoxFormat2;
1733 else
1735 // set the shared format
1736 pBoxFormat2 = aIter->second;
1737 pBox->ChgFrameFormat( pBoxFormat2, /*bNeedToReregister*/false );
1738 bNew = false; // copied from an existing format
1740 // claim it, if we are not allowed to share
1741 if ( !bMayShare )
1742 pBoxFormat2 = pBox->ClaimFrameFormat();
1745 // lock format (if so desired)
1746 if ( pModifyLocked != nullptr )
1748 (*pModifyLocked) = pBoxFormat2->IsModifyLocked();
1749 pBoxFormat2->LockModify();
1752 return pBoxFormat2;
1755 SwTableBox *SwXMLTableContext::MakeTableBox( SwTableLine *pUpper,
1756 sal_uInt32 nTopRow,
1757 sal_uInt32 nLeftCol,
1758 sal_uInt32 nBottomRow,
1759 sal_uInt32 nRightCol )
1761 //FIXME: here would be a great place to handle XmlId for cell
1762 SwTableBox *pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1764 sal_uInt32 nColSpan = nRightCol - nLeftCol;
1765 sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan );
1767 // TODO: Share formats!
1768 SwFrameFormat *pFrameFormat = pBox->ClaimFrameFormat();
1769 SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() );
1770 pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed
1771 pFrameFormat->SetFormatAttr( aFillOrder );
1773 pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) );
1775 SwTableLines& rLines = pBox->GetTabLines();
1776 bool bSplitted = false;
1778 while( !bSplitted )
1780 sal_uInt32 nStartRow = nTopRow;
1781 sal_uInt32 i;
1783 for( i = nTopRow; i < nBottomRow; i++ )
1785 // Could the table be split behind the current row?
1786 bool bSplit = true;
1787 SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
1788 for( sal_uInt32 j=nLeftCol; j<nRightCol; j++ )
1790 bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() );
1791 if( !bSplit )
1792 break;
1794 if( bSplit && (nStartRow>nTopRow || i+1<nBottomRow) )
1796 SwTableLine *pLine =
1797 MakeTableLine( pBox, nStartRow, nLeftCol, i+1,
1798 nRightCol );
1800 rLines.push_back( pLine );
1802 nStartRow = i+1;
1803 bSplitted = true;
1806 if( !bSplitted )
1808 // No splitting was possible. That for, we have to force it.
1809 // Ruthless!
1811 nStartRow = nTopRow;
1812 while( nStartRow < nBottomRow )
1814 sal_uInt32 nMaxRowSpan = 0;
1815 SwXMLTableRow_Impl *pStartRow = (*m_pRows)[nStartRow].get();
1816 const SwXMLTableCell_Impl *pCell;
1817 for( i=nLeftCol; i<nRightCol; i++ )
1819 pCell = pStartRow->GetCell(i);
1820 if( pCell->GetRowSpan() > nMaxRowSpan )
1821 nMaxRowSpan = pCell->GetRowSpan();
1824 nStartRow += nMaxRowSpan;
1825 if( nStartRow<nBottomRow )
1827 SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[nStartRow - 1U].get();
1828 i = nLeftCol;
1829 while( i < nRightCol )
1831 if( pPrevRow->GetCell(i)->GetRowSpan() > 1 )
1833 const SwXMLTableCell_Impl *pCell2 =
1834 GetCell( nStartRow, i );
1835 const sal_uInt32 nColSpan2 = pCell2->GetColSpan();
1836 FixRowSpan( nStartRow-1, i, nColSpan2 );
1837 ReplaceWithEmptyCell( nStartRow, i, true );
1838 i += nColSpan2;
1840 else
1842 i++;
1847 // and now start over again...
1851 return pBox;
1854 SwTableBox *SwXMLTableContext::MakeTableBox(
1855 SwTableLine *pUpper, const SwXMLTableCell_Impl *pCell,
1856 sal_uInt32 nLeftCol, sal_uInt32 nRightCol )
1858 //FIXME: here would be a great place to handle XmlId for cell
1859 SwTableBox *pBox;
1860 sal_uInt32 nColSpan = nRightCol - nLeftCol;
1861 sal_Int32 nColWidth = GetColumnWidth( nLeftCol, nColSpan );
1863 if( pCell->GetStartNode() )
1865 pBox = NewTableBox( pCell->GetStartNode(), pUpper );
1867 else
1869 // and it is a table: therefore we build a new box and
1870 // put the rows of the table into the rows of the box
1871 pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1872 pCell->GetSubTable()->MakeTable( pBox, nColWidth );
1875 // Share formats!
1876 const OUString& sStyleName = pCell->GetStyleName();
1877 bool bModifyLocked;
1878 bool bNew;
1879 SwTableBoxFormat *pBoxFormat2 = GetSharedBoxFormat(
1880 pBox, sStyleName, nColWidth, pCell->IsProtected(),
1881 pCell->GetStartNode() && pCell->GetFormula().isEmpty() &&
1882 ! pCell->HasValue(),
1883 bNew, &bModifyLocked );
1885 // if a new format was created, then we need to set the style
1886 if ( bNew )
1888 // set style
1889 const SfxItemSet *pAutoItemSet = nullptr;
1890 if( pCell->GetStartNode() && !sStyleName.isEmpty() &&
1891 GetSwImport().FindAutomaticStyle(
1892 XmlStyleFamily::TABLE_CELL, sStyleName, &pAutoItemSet ) )
1894 if( pAutoItemSet )
1895 pBoxFormat2->SetFormatAttr( *pAutoItemSet );
1899 if( pCell->GetStartNode() )
1901 if (pCell->HasStringValue())
1903 SwNodeIndex const aNodeIndex(*(pCell->GetStartNode()), 1);
1904 SwTextNode *const pTextNode(aNodeIndex.GetNode().GetTextNode());
1905 SAL_WARN_IF(!pTextNode, "sw", "Should have a text node in cell?");
1906 if (pTextNode)
1908 SAL_WARN_IF(!pTextNode->GetText().isEmpty(), "sw",
1909 "why text here?");
1910 pTextNode->InsertText(*pCell->GetStringValue(),
1911 SwContentIndex(pTextNode, 0));
1915 // try to rescue broken documents with a certain pattern
1916 // if: 1) the cell has a default number format (number 0)
1917 // 2) the call has no formula
1918 // 3) the value is 0.0
1919 // 4) the text doesn't look anything like 0.0
1920 // [read: length > 10, or length smaller 10 and no 0 in it]
1921 // then make it a text cell!
1922 bool bSuppressNumericContent = false;
1923 if( pCell->HasValue() && (pCell->GetValue() == 0.0) &&
1924 pCell->GetFormula().isEmpty() &&
1925 !sStyleName.isEmpty() )
1927 // default num format?
1928 if( const SwTableBoxNumFormat* pNumFormat = pBoxFormat2->GetItemIfSet( RES_BOXATR_FORMAT, false ) )
1930 if ((pNumFormat->GetValue() % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1932 // only one text node?
1933 SwNodeIndex aNodeIndex( *(pCell->GetStartNode()), 1 );
1934 if( ( aNodeIndex.GetNode().EndOfSectionIndex() -
1935 aNodeIndex.GetNode().StartOfSectionIndex() ) == SwNodeOffset(2) )
1937 SwTextNode* pTextNode= aNodeIndex.GetNode().GetTextNode();
1938 if( pTextNode != nullptr )
1940 // check text: does it look like some form of 0.0?
1941 const OUString& rText = pTextNode->GetText();
1942 if( ( rText.getLength() > 10 ) ||
1943 ( rText.indexOf( '0' ) == -1 ) )
1945 bSuppressNumericContent = true;
1949 else
1950 bSuppressNumericContent = true; // several nodes
1955 if( bSuppressNumericContent )
1957 // suppress numeric content? Then reset number format!
1958 pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMULA );
1959 pBoxFormat2->ResetFormatAttr( RES_BOXATR_FORMAT );
1960 pBoxFormat2->ResetFormatAttr( RES_BOXATR_VALUE );
1962 else
1964 // the normal case: set formula and value (if available)
1966 const OUString& rFormula = pCell->GetFormula();
1967 if (!rFormula.isEmpty())
1969 // formula cell: insert formula if valid
1970 SwTableBoxFormula aFormulaItem( rFormula );
1971 pBoxFormat2->SetFormatAttr( aFormulaItem );
1973 else if (!pCell->HasValue() && pCell->HasStringValue())
1975 // Check for another inconsistency:
1976 // No value but a non-textual format, i.e. a number format
1977 // Solution: the number format will be removed,
1978 // the cell gets the default text format.
1979 if( const SwTableBoxNumFormat* pNumFormat = m_pBoxFormat->GetItemIfSet( RES_BOXATR_FORMAT, false ) )
1981 const SwDoc* pDoc = m_pBoxFormat->GetDoc();
1982 const SvNumberFormatter* pNumberFormatter = pDoc ?
1983 pDoc->GetNumberFormatter() : nullptr;
1984 if( pNumberFormatter && !pNumberFormatter->GetEntry( pNumFormat->GetValue() )->IsTextFormat() )
1985 m_pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1988 // always insert value, even if default
1989 if( pCell->HasValue() )
1991 SwTableBoxValue aValueItem( pCell->GetValue() );
1992 pBoxFormat2->SetFormatAttr( aValueItem );
1996 // update cell content depend on the default language
1997 pBox->ActualiseValueBox();
2000 // table cell protection
2001 if( pCell->IsProtected() )
2003 SvxProtectItem aProtectItem( RES_PROTECT );
2004 aProtectItem.SetContentProtect( true );
2005 pBoxFormat2->SetFormatAttr( aProtectItem );
2008 // restore old modify-lock state
2009 if (! bModifyLocked)
2010 pBoxFormat2->UnlockModify();
2012 pBoxFormat2->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nColWidth ) );
2014 return pBox;
2017 SwTableLine *SwXMLTableContext::MakeTableLine( SwTableBox *pUpper,
2018 sal_uInt32 nTopRow,
2019 sal_uInt32 nLeftCol,
2020 sal_uInt32 nBottomRow,
2021 sal_uInt32 nRightCol )
2023 //FIXME: here would be a great place to handle XmlId for row
2024 SwTableLine *pLine;
2025 if( !pUpper && 0UL==nTopRow )
2027 pLine = m_pTableNode->GetTable().GetTabLines()[0U];
2029 else
2031 pLine = new SwTableLine( m_pLineFormat, 0, pUpper );
2034 // TODO: Share formats!
2035 SwFrameFormat *pFrameFormat = pLine->ClaimFrameFormat();
2036 SwFormatFillOrder aFillOrder( pFrameFormat->GetFillOrder() );
2037 pFrameFormat->ResetAllFormatAttr(); // #i73790# - method renamed
2038 pFrameFormat->SetFormatAttr( aFillOrder );
2040 const SfxItemSet *pAutoItemSet = nullptr;
2041 const OUString& rStyleName = (*m_pRows)[nTopRow]->GetStyleName();
2042 if( 1 == (nBottomRow - nTopRow) &&
2043 !rStyleName.isEmpty() &&
2044 GetSwImport().FindAutomaticStyle(
2045 XmlStyleFamily::TABLE_ROW, rStyleName, &pAutoItemSet ) )
2047 if( pAutoItemSet )
2048 pFrameFormat->SetFormatAttr( *pAutoItemSet );
2051 SwTableBoxes& rBoxes = pLine->GetTabBoxes();
2053 sal_uInt32 nStartCol = nLeftCol;
2054 while( nStartCol < nRightCol )
2056 for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ )
2057 (*m_pRows)[nRow]->SetSplitable( true );
2059 sal_uInt32 nCol = nStartCol;
2060 sal_uInt32 nSplitCol = nRightCol;
2061 bool bSplitted = false;
2062 while( !bSplitted )
2064 OSL_ENSURE( nCol < nRightCol, "Ran too far" );
2066 // Can be split after current HTML table column?
2067 // If yes, can the created region still be split to
2068 // rows if the next column is added to it?
2069 bool bSplit = true;
2070 bool bHoriSplitMayContinue = false;
2071 bool bHoriSplitPossible = false;
2073 if ( m_bHasSubTables )
2075 // Convert row spans if the table has subtables:
2076 for( sal_uInt32 nRow=nTopRow; nRow<nBottomRow; nRow++ )
2078 SwXMLTableCell_Impl *pCell = GetCell(nRow,nCol);
2079 // Could the table fragment be split horizontally behind
2080 // the current line?
2081 bool bHoriSplit = (*m_pRows)[nRow]->IsSplitable() &&
2082 nRow+1 < nBottomRow &&
2083 1 == pCell->GetRowSpan();
2084 (*m_pRows)[nRow]->SetSplitable( bHoriSplit );
2086 // Could the table fragment be split vertically behind the
2087 // current column (uptp the current line?
2088 bSplit &= ( 1 == pCell->GetColSpan() );
2089 if( bSplit )
2091 bHoriSplitPossible |= bHoriSplit;
2093 // Could the current table fragment be split
2094 // horizontally behind the next column, too?
2095 bHoriSplit &= (nCol+1 < nRightCol &&
2096 1 == GetCell(nRow,nCol+1)->GetRowSpan());
2097 bHoriSplitMayContinue |= bHoriSplit;
2101 else
2103 // No subtables: we use the new table model.
2104 SwXMLTableCell_Impl *pCell = GetCell(nTopRow,nCol);
2106 // #i95726# - some fault tolerance
2107 if ( pCell == nullptr )
2109 OSL_FAIL( "table seems to be corrupt." );
2110 break;
2113 // Could the table fragment be split vertically behind the
2114 // current column (uptp the current line?
2115 bSplit = 1 == pCell->GetColSpan();
2118 #if OSL_DEBUG_LEVEL > 0
2119 if( nCol == nRightCol-1 )
2121 OSL_ENSURE( bSplit, "Split-Flag wrong" );
2122 if ( m_bHasSubTables )
2124 OSL_ENSURE( !bHoriSplitMayContinue,
2125 "HoriSplitMayContinue-Flag wrong" );
2126 SwXMLTableCell_Impl *pTmpCell = GetCell( nTopRow, nStartCol );
2127 OSL_ENSURE( pTmpCell->GetRowSpan() != (nBottomRow-nTopRow) ||
2128 !bHoriSplitPossible, "HoriSplitPossible-Flag wrong" );
2131 #endif
2133 OSL_ENSURE( !m_bHasSubTables || !bHoriSplitMayContinue || bHoriSplitPossible,
2134 "bHoriSplitMayContinue, but not bHoriSplitPossible" );
2136 if( bSplit )
2138 SwTableBox* pBox = nullptr;
2139 SwXMLTableCell_Impl *pCell = GetCell( nTopRow, nStartCol );
2140 // #i95726# - some fault tolerance
2141 if( ( !m_bHasSubTables || ( pCell->GetRowSpan() == (nBottomRow-nTopRow) ) ) &&
2142 pCell->GetColSpan() == (nCol+1-nStartCol) &&
2143 ( pCell->GetStartNode() || pCell->GetSubTable() ) )
2145 // insert new empty cell for covered cells:
2146 sal_Int32 nBoxRowSpan = 1;
2147 if ( !m_bHasSubTables )
2149 nBoxRowSpan = pCell->GetRowSpan();
2150 if ( pCell->IsCovered() )
2152 nBoxRowSpan = -1 * nBoxRowSpan;
2153 ReplaceWithEmptyCell( nTopRow, nStartCol, false );
2157 // The remaining box neither contains lines nor rows (i.e.
2158 // is a content box
2159 nSplitCol = nCol + 1;
2161 pBox = MakeTableBox( pLine, pCell, nStartCol, nSplitCol );
2163 if ( 1 != nBoxRowSpan )
2164 pBox->setRowSpan( nBoxRowSpan );
2166 bSplitted = true;
2168 else if( m_bHasSubTables && bHoriSplitPossible && bHoriSplitMayContinue )
2170 // The table fragment could be split behind the current
2171 // column, and the remaining fragment could be divided
2172 // into lines. Anyway, it could be that this applies to
2173 // the next column, too. That for, we check the next
2174 // column but remember the current one as a good place to
2175 // split.
2176 nSplitCol = nCol + 1;
2178 else if ( m_bHasSubTables )
2180 // If the table resulting table fragment could be divided
2181 // into lines if splitting behind the current column, but
2182 // this doesn't apply for thr next column, we split begind
2183 // the current column. This applies for the last column,
2184 // too.
2185 // If the resulting box cannot be split into rows,
2186 // the split at the last split position we remembered.
2187 if( bHoriSplitPossible || nSplitCol > nCol+1 )
2189 OSL_ENSURE( !bHoriSplitMayContinue,
2190 "bHoriSplitMayContinue==true" );
2191 OSL_ENSURE( bHoriSplitPossible || nSplitCol == nRightCol,
2192 "bHoriSplitPossible flag should be set" );
2194 nSplitCol = nCol + 1;
2197 pBox = MakeTableBox( pLine, nTopRow, nStartCol,
2198 nBottomRow, nSplitCol );
2199 bSplitted = true;
2202 OSL_ENSURE( m_bHasSubTables || pBox, "Colspan trouble" );
2204 if( pBox )
2205 rBoxes.push_back( pBox );
2207 nCol++;
2209 nStartCol = nSplitCol;
2212 return pLine;
2215 void SwXMLTableContext::MakeTable_( SwTableBox *pBox )
2217 // fix column widths
2218 sal_uInt32 nCols = GetColumnCount();
2220 // If there are empty rows (because of some row span of previous rows)
2221 // the have to be deleted. The previous rows have to be truncated.
2223 if (m_pRows->size() > m_nCurRow)
2225 SwXMLTableRow_Impl *pPrevRow = (*m_pRows)[m_nCurRow - 1U].get();
2226 const SwXMLTableCell_Impl *pCell;
2227 for( size_t i = 0; i < m_aColumnWidths.size(); ++i )
2229 pCell = pPrevRow->GetCell(i);
2230 if( pCell->GetRowSpan() > 1 )
2232 FixRowSpan( m_nCurRow-1, i, 1UL );
2235 for (size_t i = m_pRows->size() - 1; i >= m_nCurRow; --i)
2236 m_pRows->pop_back();
2239 if (m_pRows->empty())
2241 InsertCell( u""_ustr, 1U, nCols, InsertTableSection() );
2244 // TODO: Do we have to keep both values, the relative and the absolute
2245 // width?
2246 sal_Int32 nAbsWidth = 0;
2247 sal_Int32 nMinAbsColWidth = 0;
2248 sal_Int32 nRelWidth = 0;
2249 sal_Int32 nMinRelColWidth = 0;
2250 sal_uInt32 nRelCols = 0;
2251 for( const auto& rCol : m_aColumnWidths)
2253 if( rCol.isRelative )
2255 nRelWidth += rCol.width;
2256 if( 0 == nMinRelColWidth || rCol.width < nMinRelColWidth )
2257 nMinRelColWidth = rCol.width;
2258 nRelCols++;
2260 else
2262 nAbsWidth += rCol.width;
2263 if( 0 == nMinAbsColWidth || rCol.width < nMinAbsColWidth )
2264 nMinAbsColWidth = rCol.width;
2267 sal_uInt32 nAbsCols = nCols - nRelCols;
2269 if( m_bRelWidth )
2271 // If there a columns that have an absolute width, we have to
2272 // calculate a relative one for them.
2273 if( nAbsCols > 0 )
2275 // All column that have absolute widths get relative widths;
2276 // these widths relate to each over like the original absolute
2277 // widths. The smallest column gets a width that has the same
2278 // value as the smallest column that has a relative width
2279 // already.
2280 if( 0 == nMinRelColWidth )
2281 nMinRelColWidth = nMinAbsColWidth;
2283 for( auto& rCol : m_aColumnWidths)
2285 if( !rCol.isRelative )
2287 if (nMinAbsColWidth == 0)
2288 throw o3tl::divide_by_zero();
2289 sal_Int32 nVal;
2290 if (o3tl::checked_multiply<sal_Int32>(rCol.width, nMinRelColWidth, nVal))
2291 throw std::overflow_error("overflow in multiply");
2292 sal_Int32 nRelCol = nVal / nMinAbsColWidth;
2293 rCol.width = nRelCol;
2294 rCol.isRelative = true;
2295 nRelWidth += nRelCol;
2296 nAbsCols--;
2297 if (nAbsCols <= 0)
2298 break;
2303 if( !m_nWidth )
2305 // This happens only for percentage values for the table itself.
2306 // In this case, the columns get the correct width even if
2307 // the sum of the relative widths is smaller than the available
2308 // width in TWIP. Therefore, we can use the relative width.
2309 m_nWidth = std::min(nRelWidth, MAX_WIDTH);
2311 if( nRelWidth != m_nWidth && nRelWidth && nCols )
2313 double n = static_cast<double>(m_nWidth) / static_cast<double>(nRelWidth);
2314 nRelWidth = 0;
2315 for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter)
2317 sal_Int32 nW = static_cast<sal_Int32>( colIter->width * n);
2318 colIter->width = o3tl::narrowing<sal_uInt16>(nW);
2319 nRelWidth += nW;
2321 m_aColumnWidths.back().width = (m_nWidth-nRelWidth);
2324 else
2326 // If there are columns that have relative widths, we have to
2327 // calculate an absolute widths for them.
2328 if( nRelCols > 0 )
2330 // The absolute space that is available for all columns with a
2331 // relative width.
2332 sal_Int32 nAbsForRelWidth =
2333 m_nWidth > nAbsWidth ? m_nWidth - nAbsWidth : sal_Int32(0L);
2335 // The relative width that has to be distributed in addition to
2336 // equally widthed columns.
2337 sal_Int32 nExtraRel = nRelWidth - (nRelCols * nMinRelColWidth);
2339 // The absolute space that may be distributed in addition to
2340 // minimum widthed columns.
2341 sal_Int32 nMinAbs = nRelCols * MINLAY;
2342 sal_Int32 nExtraAbs =
2343 nAbsForRelWidth > nMinAbs ? nAbsForRelWidth - nMinAbs : sal_Int32(0L);
2345 bool bMin = false; // Do all columns get the minimum width?
2346 bool bMinExtra = false; // Do all columns get the minimum width plus
2347 // some extra space?
2349 if( nAbsForRelWidth <= nMinAbs )
2351 // If there is not enough space left for all columns to
2352 // get the minimum width, they get the minimum width, anyway.
2353 nAbsForRelWidth = nMinAbs;
2354 bMin = true;
2356 else if( nAbsForRelWidth <= (nRelWidth * MINLAY) /
2357 nMinRelColWidth )
2359 // If there is enough space for all columns to get the
2360 // minimum width, but not to get a width that takes the
2361 // relative width into account, each column gets the minimum
2362 // width plus some extra space that is based on the additional
2363 // space that is available.
2364 bMinExtra = true;
2366 // Otherwise, if there is enough space for every column, every
2367 // column gets this space.
2369 for( auto& rCol : m_aColumnWidths )
2371 if( rCol.isRelative )
2373 sal_Int32 nAbsCol;
2374 if( 1 == nRelCols )
2376 // The last column that has a relative width gets
2377 // all absolute space that is left.
2378 nAbsCol = nAbsForRelWidth;
2380 else
2382 if( bMin )
2384 nAbsCol = MINLAY;
2386 else if( bMinExtra )
2388 sal_Int32 nExtraRelCol = rCol.width - nMinRelColWidth;
2389 nAbsCol = MINLAY + (nExtraRelCol * nExtraAbs) /
2390 nExtraRel;
2392 else
2394 nAbsCol = ( rCol.width * nAbsForRelWidth) / nRelWidth;
2397 rCol.width = nAbsCol;
2398 rCol.isRelative = false;
2399 nAbsForRelWidth -= nAbsCol;
2400 nAbsWidth += nAbsCol;
2401 nRelCols--;
2402 if (nRelCols <= 0)
2403 break;
2408 if( nCols && nAbsWidth )
2410 if( nAbsWidth < m_nWidth )
2412 // If the table's width is larger than the sum of the absolute
2413 // column widths, every column get some extra width.
2414 sal_Int32 nExtraAbs = m_nWidth - nAbsWidth;
2415 sal_Int32 nAbsLastCol = m_aColumnWidths.back().width + nExtraAbs;
2416 for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter )
2418 sal_Int32 nAbsCol = colIter->width;
2419 sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) /
2420 nAbsWidth;
2421 nAbsCol += nExtraAbsCol;
2422 colIter->width = nAbsCol;
2423 nAbsLastCol -= nExtraAbsCol;
2425 m_aColumnWidths.back().width = nAbsLastCol;
2427 else if( nAbsWidth > m_nWidth )
2429 // If the table's width is smaller than the sum of the absolute
2430 // column widths, every column needs to shrink.
2431 // Every column gets the minimum width plus some extra width.
2432 sal_Int32 nExtraAbs = m_nWidth - (nCols * MINLAY);
2433 sal_Int32 nAbsLastCol = MINLAY + nExtraAbs;
2434 for( auto colIter = m_aColumnWidths.begin(); colIter != (m_aColumnWidths.end() - 1); ++colIter )
2436 sal_Int32 nAbsCol = colIter->width;
2437 sal_Int32 nExtraAbsCol = (nAbsCol * nExtraAbs) /
2438 nAbsWidth;
2439 nAbsCol = MINLAY + nExtraAbsCol;
2440 colIter->width = nAbsCol;
2441 nAbsLastCol -= nExtraAbsCol;
2443 m_aColumnWidths.back().width = nAbsLastCol;
2448 SwTableLines& rLines =
2449 pBox ? pBox->GetTabLines()
2450 : m_pTableNode->GetTable().GetTabLines();
2452 sal_uInt32 nStartRow = 0;
2453 sal_uInt32 nRows = m_pRows->size();
2454 for(sal_uInt32 i=0; i<nRows; ++i )
2456 // Could we split the table behind the current line?
2457 bool bSplit = true;
2458 if ( m_bHasSubTables )
2460 SwXMLTableRow_Impl *pRow = (*m_pRows)[i].get();
2461 for( sal_uInt32 j=0; j<nCols; j++ )
2463 bSplit = ( 1 == pRow->GetCell(j)->GetRowSpan() );
2464 if( !bSplit )
2465 break;
2469 if( bSplit )
2471 SwTableLine *pLine =
2472 MakeTableLine( pBox, nStartRow, 0UL, i+1, nCols );
2473 if( pBox || nStartRow>0 )
2474 rLines.push_back( pLine );
2475 nStartRow = i+1;
2480 void SwXMLTableContext::MakeTable()
2482 // this method will modify the document directly -> lock SolarMutex
2483 // This will call all other MakeTable*(..) methods, so
2484 // those don't need to be locked separately.
2485 SolarMutexGuard aGuard;
2487 // #i97274# handle invalid tables
2488 if (!m_pRows || m_pRows->empty() || !GetColumnCount())
2490 OSL_FAIL("invalid table: no cells; deleting...");
2491 m_pTableNode->GetDoc().getIDocumentContentOperations().DeleteSection( m_pTableNode );
2492 m_pTableNode = nullptr;
2493 m_pBox1 = nullptr;
2494 m_bOwnsBox1 = false;
2495 m_pSttNd1 = nullptr;
2496 return;
2499 SwXMLImport& rSwImport = GetSwImport();
2501 SwFrameFormat *pFrameFormat = m_pTableNode->GetTable().GetFrameFormat();
2503 sal_Int16 eHoriOrient = text::HoriOrientation::FULL;
2504 bool bSetHoriOrient = false;
2506 sal_uInt8 nPercentWidth = 0U;
2508 OUString sStyleName;
2509 SwStyleNameMapper::FillUIName( ProgName(m_aTemplateName), sStyleName, SwGetPoolIdFromName::TabStyle );
2510 m_pTableNode->GetTable().SetTableStyleName( sStyleName );
2511 m_pTableNode->GetTable().SetRowsToRepeat( m_nHeaderRows );
2512 m_pTableNode->GetTable().SetTableModel( !m_bHasSubTables );
2514 const SfxItemSet *pAutoItemSet = nullptr;
2515 if( !m_aStyleName.isEmpty() &&
2516 rSwImport.FindAutomaticStyle(
2517 XmlStyleFamily::TABLE_TABLE, m_aStyleName, &pAutoItemSet ) &&
2518 pAutoItemSet )
2520 const SvxLRSpaceItem *pLRSpace =
2521 pAutoItemSet->GetItemIfSet( RES_LR_SPACE, false );
2523 if( const SwFormatHoriOrient* pItem = pAutoItemSet->GetItemIfSet( RES_HORI_ORIENT, false ) )
2525 eHoriOrient = pItem->GetHoriOrient();
2526 switch( eHoriOrient )
2528 case text::HoriOrientation::FULL:
2529 if( pLRSpace )
2531 eHoriOrient = text::HoriOrientation::NONE;
2532 bSetHoriOrient = true;
2534 break;
2535 case text::HoriOrientation::LEFT:
2536 if( pLRSpace )
2538 eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
2539 bSetHoriOrient = true;
2541 break;
2542 default:
2546 else
2548 bSetHoriOrient = true;
2551 const SwFormatFrameSize *pSize =
2552 pAutoItemSet->GetItemIfSet( RES_FRM_SIZE, false );
2554 switch( eHoriOrient )
2556 case text::HoriOrientation::FULL:
2557 case text::HoriOrientation::NONE:
2558 // For text::HoriOrientation::NONE we would prefer to use the sum
2559 // of the relative column widths as reference width.
2560 // Unfortunately this works only if this sum interpreted as
2561 // twip value is larger than the space that is available.
2562 // We don't know that space, so we have to use MAX_WIDTH, too.
2563 // Even if a size is specified, it will be ignored!
2564 m_nWidth = MAX_WIDTH;
2565 break;
2566 default:
2567 if( pSize )
2569 if( pSize->GetWidthPercent() )
2571 // The width will be set in MakeTable_
2572 nPercentWidth = pSize->GetWidthPercent();
2574 else
2576 m_nWidth = pSize->GetWidth();
2577 sal_Int32 const min = static_cast<sal_Int32>(
2578 std::min<sal_uInt32>(GetColumnCount() * MINLAY, MAX_WIDTH));
2579 if( m_nWidth < min )
2581 m_nWidth = min;
2583 else if( m_nWidth > MAX_WIDTH )
2585 m_nWidth = MAX_WIDTH;
2587 m_bRelWidth = false;
2590 else
2592 eHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH == eHoriOrient
2593 ? text::HoriOrientation::NONE : text::HoriOrientation::FULL;
2594 bSetHoriOrient = true;
2595 m_nWidth = MAX_WIDTH;
2597 break;
2600 pFrameFormat->SetFormatAttr( *pAutoItemSet );
2602 else
2604 bSetHoriOrient = true;
2605 m_nWidth = MAX_WIDTH;
2608 SwTableLine *pLine1 = m_pTableNode->GetTable().GetTabLines()[0U];
2609 assert(m_pBox1 == pLine1->GetTabBoxes()[0] && !m_bOwnsBox1 && "Why is box 1 change?");
2610 m_pBox1->m_pStartNode = m_pSttNd1;
2611 pLine1->GetTabBoxes().erase( pLine1->GetTabBoxes().begin() );
2612 m_bOwnsBox1 = true;
2614 m_pLineFormat = pLine1->GetFrameFormat();
2615 m_pBoxFormat = m_pBox1->GetFrameFormat();
2617 MakeTable_();
2619 if( bSetHoriOrient )
2620 pFrameFormat->SetFormatAttr( SwFormatHoriOrient( 0, eHoriOrient ) );
2622 // This must be after the call to MakeTable_, because nWidth might be
2623 // changed there.
2624 pFrameFormat->LockModify();
2625 SwFormatFrameSize aSize( SwFrameSize::Variable, m_nWidth );
2626 aSize.SetWidthPercent( nPercentWidth );
2627 pFrameFormat->SetFormatAttr( aSize );
2628 pFrameFormat->UnlockModify();
2630 for (std::unique_ptr<SwXMLTableRow_Impl> & rRow : *m_pRows)
2631 rRow->Dispose();
2633 // now that table is complete, change into DDE table (if appropriate)
2634 if (m_xDDESource.is())
2636 // change existing table into DDE table:
2637 // 1) Get DDE field type (get data from dde-source context),
2638 SwDDEFieldType* pFieldType = lcl_GetDDEFieldType( m_xDDESource.get(),
2639 m_pTableNode );
2641 // 2) release the DDE source context,
2642 m_xDDESource.clear();
2644 // 3) create new DDE table, and
2645 std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( m_pTableNode->GetTable(),
2646 pFieldType, false ) );
2648 // 4) set new (DDE)table at node.
2649 m_pTableNode->SetNewTable(std::move(pDDETable), false);
2652 // ??? this is always false: root frame is only created in SwViewShell::Init
2653 if( m_pTableNode->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() )
2655 m_pTableNode->DelFrames();
2656 m_pTableNode->MakeOwnFrames();
2660 void SwXMLTableContext::MakeTable( SwTableBox *pBox, sal_Int32 nW )
2662 //FIXME: here would be a great place to handle XmlId for subtable
2663 m_pLineFormat = GetParentTable()->m_pLineFormat;
2664 m_pBoxFormat = GetParentTable()->m_pBoxFormat;
2665 m_nWidth = nW;
2666 m_bRelWidth = GetParentTable()->m_bRelWidth;
2668 MakeTable_( pBox );
2670 for (std::unique_ptr<SwXMLTableRow_Impl> & rpRow : *m_pRows)
2672 // i#113600, to break the cyclic reference to SwXMLTableContext object
2673 rpRow->Dispose();
2677 const SwStartNode *SwXMLTableContext::InsertTableSection(
2678 const SwStartNode *const pPrevSttNd,
2679 OUString const*const pStringValueStyleName)
2681 // The topmost table is the only table that maintains the two members
2682 // pBox1 and bFirstSection.
2683 if( m_xParentTable.is() )
2684 return m_xParentTable->InsertTableSection(pPrevSttNd, pStringValueStyleName);
2686 const SwStartNode *pStNd;
2688 if( m_bFirstSection )
2690 Reference<XInterface> xCursorTunnel( GetImport().GetTextImport()->GetCursor(),
2691 UNO_QUERY);
2692 OSL_ENSURE( xCursorTunnel.is(), "missing XUnoTunnel for Cursor" );
2693 OTextCursorHelper *pTextCursor = dynamic_cast<OTextCursorHelper*>(xCursorTunnel.get());
2694 assert(pTextCursor && "SwXTextCursor missing");
2696 // The Cursor already is in the first section
2697 pStNd = pTextCursor->GetPaM()->GetPointNode().FindTableBoxStartNode();
2698 m_bFirstSection = false;
2699 GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
2700 GetImport().GetTextImport()->GetCursor(), u"Standard"_ustr, true );
2702 else
2704 SwDoc* pDoc = GetSwImport().getDoc();
2705 const SwEndNode *pEndNd = pPrevSttNd ? pPrevSttNd->EndOfSectionNode()
2706 : m_pTableNode->EndOfSectionNode();
2707 // #i78921# - make code robust
2708 OSL_ENSURE( pDoc, "<SwXMLTableContext::InsertTableSection(..)> - no <pDoc> at <SwXTextCursor> instance - <SwXTextCurosr> doesn't seem to be registered at a <SwUnoCursor> instance." );
2709 if ( !pDoc )
2711 pDoc = &const_cast<SwDoc&>(pEndNd->GetDoc());
2713 SwNodeOffset nOffset(pPrevSttNd ? 1 : 0);
2714 SwNodeIndex aIdx( *pEndNd, nOffset );
2715 SwTextFormatColl *pColl =
2716 pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD, false );
2717 pStNd = pDoc->GetNodes().MakeTextSection( aIdx.GetNode(), SwTableBoxStartNode,
2718 pColl );
2719 // Consider the case that a table is defined without a row.
2720 if( !pPrevSttNd && m_pBox1 != nullptr )
2722 m_pBox1->m_pStartNode = pStNd;
2723 SwContentNode *pCNd = pDoc->GetNodes()[ pStNd->GetIndex() + 1 ]
2724 ->GetContentNode();
2725 SwFrameFormat *const pTableFormat = m_pTableNode->GetTable().GetFrameFormat();
2726 rtl::Reference<SwXCell> xParent = SwXCell::CreateXCell( pTableFormat, m_pBox1 );
2727 DBG_TESTSOLARMUTEX();
2728 SwPaM aPam(*pCNd, *pCNd);
2729 rtl::Reference<SwXTextCursor> xTextCursor =
2730 new SwXTextCursor(*pDoc, xParent, CursorType::TableText,
2731 *aPam.GetPoint(), aPam.GetMark());
2732 GetImport().GetTextImport()->SetCursor( static_cast<XWordCursor*>(xTextCursor.get()) );
2736 if (pStringValueStyleName)
2737 { // fdo#62147: apply style to paragraph on string-value cell
2738 GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
2739 GetImport().GetTextImport()->GetCursor(), *pStringValueStyleName,
2740 true, false, -1, false); // parameters same as sCellParaStyleName
2743 return pStNd;
2746 void SwXMLTableContext::endFastElement(sal_Int32 )
2748 if( IsValid() && !m_xParentTable.is() )
2750 MakeTable();
2751 GetImport().GetTextImport()->SetCursor( m_xOldCursor );
2755 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */