1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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 <unotools/configmgr.hxx>
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>
50 #include <IDocumentFieldsAccess.hxx>
51 #include <IDocumentLayoutAccess.hxx>
52 #include <IDocumentStylePoolAccess.hxx>
53 #include <swtable.hxx>
54 #include <swtblfmt.hxx>
56 #include <unoprnms.hxx>
58 #include <unotextrange.hxx>
59 #include <cellatr.hxx>
60 #include <swddetbl.hxx>
62 #include <sfx2/linkmgr.hxx>
64 #include "xmltbli.hxx"
65 #include <vcl/svapp.hxx>
67 #include <unotextcursor.hxx>
68 #include <SwStyleNameMapper.hxx>
69 #include <IDocumentSettingAccess.hxx>
78 using namespace ::com::sun::star
;
79 using namespace ::com::sun::star::uno
;
80 using namespace ::com::sun::star::lang
;
81 using namespace ::com::sun::star::text
;
82 using namespace ::com::sun::star::table
;
83 using namespace ::com::sun::star::xml::sax
;
84 using namespace ::xmloff::token
;
86 class SwXMLTableCell_Impl
88 OUString m_aStyleName
;
90 OUString m_StringValue
;
92 OUString m_sFormula
; // cell formula; valid if length > 0
93 double m_dValue
; // formula value
95 SvXMLImportContextRef m_xSubTable
;
97 const SwStartNode
*m_pStartNode
;
98 sal_uInt32 m_nRowSpan
;
99 sal_uInt32 m_nColSpan
;
101 bool m_bProtected
: 1;
102 bool m_bHasValue
; // determines whether dValue attribute is valid
104 bool m_bHasStringValue
;
108 SwXMLTableCell_Impl( sal_uInt32 nRSpan
=1, sal_uInt32 nCSpan
=1 ) :
110 m_pStartNode( nullptr ),
111 m_nRowSpan( nRSpan
),
112 m_nColSpan( nCSpan
),
113 m_bProtected( false ),
114 m_bHasValue( false ),
116 , m_bHasStringValue(false)
119 inline void Set( const OUString
& rStyleName
,
120 sal_uInt32 nRSpan
, sal_uInt32 nCSpan
,
121 const SwStartNode
*pStNd
, SwXMLTableContext
*pTable
,
123 const OUString
* pFormula
,
127 OUString
const*const pStringValue
);
129 bool IsUsed() const { return m_pStartNode
!=nullptr ||
130 m_xSubTable
.is() || m_bProtected
;}
132 sal_uInt32
GetRowSpan() const { return m_nRowSpan
; }
133 void SetRowSpan( sal_uInt32 nSet
) { m_nRowSpan
= nSet
; }
134 sal_uInt32
GetColSpan() const { return m_nColSpan
; }
135 void SetStyleName(const OUString
& rStyleName
) { m_aStyleName
= rStyleName
; }
136 const OUString
& GetStyleName() const { return m_aStyleName
; }
137 const OUString
& GetFormula() const { return m_sFormula
; }
138 double GetValue() const { return m_dValue
; }
139 bool HasValue() const { return m_bHasValue
; }
140 bool IsProtected() const { return m_bProtected
; }
141 bool IsCovered() const { return mbCovered
; }
142 bool HasStringValue() const { return m_bHasStringValue
; }
143 OUString
const* GetStringValue() const {
144 return m_bHasStringValue
? &m_StringValue
: nullptr;
147 const SwStartNode
*GetStartNode() const { return m_pStartNode
; }
148 inline void SetStartNode( const SwStartNode
*pSttNd
);
150 inline SwXMLTableContext
*GetSubTable() const;
152 inline void Dispose();
155 inline void SwXMLTableCell_Impl::Set( const OUString
& rStyleName
,
156 sal_uInt32 nRSpan
, sal_uInt32 nCSpan
,
157 const SwStartNode
*pStNd
,
158 SwXMLTableContext
*pTable
,
160 const OUString
* pFormula
,
164 OUString
const*const pStringValue
)
166 m_aStyleName
= rStyleName
;
169 m_pStartNode
= pStNd
;
170 m_xSubTable
= pTable
;
172 m_bHasValue
= bHasVal
;
176 m_StringValue
= *pStringValue
;
178 m_bHasStringValue
= (pStringValue
!= nullptr);
179 m_bProtected
= bProtect
;
181 // set formula, if valid
182 if (pFormula
!= nullptr)
184 m_sFormula
= *pFormula
;
188 inline void SwXMLTableCell_Impl::SetStartNode( const SwStartNode
*pSttNd
)
190 m_pStartNode
= pSttNd
;
191 m_xSubTable
= nullptr;
194 inline SwXMLTableContext
*SwXMLTableCell_Impl::GetSubTable() const
196 return static_cast<SwXMLTableContext
*>(m_xSubTable
.get());
199 inline void SwXMLTableCell_Impl::Dispose()
201 if( m_xSubTable
.is() )
202 m_xSubTable
= nullptr;
205 class SwXMLTableRow_Impl
207 OUString m_aStyleName
;
208 OUString m_aDefaultCellStyleName
;
209 std::vector
<std::unique_ptr
<SwXMLTableCell_Impl
>> m_Cells
;
214 SwXMLTableRow_Impl( OUString aStyleName
, sal_uInt32 nCells
,
215 const OUString
*pDfltCellStyleName
= nullptr );
217 inline SwXMLTableCell_Impl
*GetCell( sal_uInt32 nCol
);
219 inline void Set( const OUString
& rStyleName
,
220 const OUString
& rDfltCellStyleName
);
222 void Expand( sal_uInt32 nCells
, bool bOneCell
);
224 void SetSplitable( bool bSet
) { m_bSplitable
= bSet
; }
225 bool IsSplitable() const { return m_bSplitable
; }
227 const OUString
& GetStyleName() const { return m_aStyleName
; }
228 const OUString
& GetDefaultCellStyleName() const { return m_aDefaultCellStyleName
; }
233 SwXMLTableRow_Impl::SwXMLTableRow_Impl( OUString aStyleName
,
235 const OUString
*pDfltCellStyleName
) :
236 m_aStyleName(std::move( aStyleName
)),
237 m_bSplitable( false )
239 if( pDfltCellStyleName
)
240 m_aDefaultCellStyleName
= *pDfltCellStyleName
;
241 OSL_ENSURE( nCells
<= USHRT_MAX
,
242 "SwXMLTableRow_Impl::SwXMLTableRow_Impl: too many cells" );
243 if( nCells
> USHRT_MAX
)
246 for( sal_uInt32 i
=0U; i
<nCells
; ++i
)
248 m_Cells
.push_back(std::make_unique
<SwXMLTableCell_Impl
>());
252 inline SwXMLTableCell_Impl
*SwXMLTableRow_Impl::GetCell( sal_uInt32 nCol
)
254 OSL_ENSURE( nCol
< USHRT_MAX
,
255 "SwXMLTableRow_Impl::GetCell: column number is too big" );
256 // #i95726# - some fault tolerance
257 OSL_ENSURE( nCol
< m_Cells
.size(),
258 "SwXMLTableRow_Impl::GetCell: column number is out of bound" );
259 return nCol
< m_Cells
.size() ? m_Cells
[nCol
].get() : nullptr;
262 void SwXMLTableRow_Impl::Expand( sal_uInt32 nCells
, bool bOneCell
)
264 OSL_ENSURE( nCells
<= USHRT_MAX
,
265 "SwXMLTableRow_Impl::Expand: too many cells" );
266 if( nCells
> USHRT_MAX
)
269 sal_uInt32 nColSpan
= nCells
- m_Cells
.size();
270 for (size_t i
= m_Cells
.size(); i
< nCells
; ++i
)
272 m_Cells
.push_back(std::make_unique
<SwXMLTableCell_Impl
>(
273 1UL, bOneCell
? nColSpan
: 1UL));
277 OSL_ENSURE( nCells
<= m_Cells
.size(),
278 "SwXMLTableRow_Impl::Expand: wrong number of cells" );
281 inline void SwXMLTableRow_Impl::Set( const OUString
& rStyleName
,
282 const OUString
& rDfltCellStyleName
)
284 m_aStyleName
= rStyleName
;
285 m_aDefaultCellStyleName
= rDfltCellStyleName
;
288 void SwXMLTableRow_Impl::Dispose()
290 for (auto & pCell
: m_Cells
)
298 class SwXMLTableCellContext_Impl
: public SvXMLImportContext
300 OUString m_aStyleName
;
302 OUString m_sSaveParaDefault
;
303 OUString m_StringValue
;
305 SvXMLImportContextRef m_xMyTable
;
309 bool m_bHasStringValue
;
310 bool m_bValueTypeIsString
;
313 sal_uInt32 m_nRowSpan
;
314 sal_uInt32 m_nColSpan
;
315 sal_uInt32 m_nColRepeat
;
317 bool m_bHasTextContent
: 1;
318 bool m_bHasTableContent
: 1;
320 SwXMLTableContext
*GetTable() { return static_cast<SwXMLTableContext
*>(m_xMyTable
.get()); }
322 bool HasContent() const { return m_bHasTextContent
|| m_bHasTableContent
; }
323 inline void InsertContent_();
324 inline void InsertContent();
325 inline void InsertContent( SwXMLTableContext
*pTable
);
329 SwXMLTableCellContext_Impl(
330 SwXMLImport
& rImport
, sal_Int32 nElement
,
331 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
332 SwXMLTableContext
*pTable
);
334 virtual css::uno::Reference
<css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext(
336 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
) override
;
337 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
339 SwXMLImport
& GetSwImport() { return static_cast<SwXMLImport
&>(GetImport()); }
342 /// Handles <table:covered-table-cell>.
343 class SwXMLCoveredTableCellContext
: public SvXMLImportContext
346 SwXMLCoveredTableCellContext(SwXMLImport
& rImport
,
347 const Reference
<xml::sax::XFastAttributeList
>& xAttrList
,
348 SwXMLTableContext
& rTable
);
351 SwXMLCoveredTableCellContext::SwXMLCoveredTableCellContext(
352 SwXMLImport
& rImport
, const Reference
<xml::sax::XFastAttributeList
>& xAttrList
,
353 SwXMLTableContext
& rTable
)
354 : SvXMLImportContext(rImport
)
357 for (auto& rIter
: sax_fastparser::castToFastAttributeList(xAttrList
))
359 switch (rIter
.getToken())
361 case XML_ELEMENT(TABLE
, XML_STYLE_NAME
):
362 aStyleName
= rIter
.toString();
367 if (!aStyleName
.isEmpty())
369 rTable
.InsertCoveredCell(aStyleName
);
374 SwXMLTableCellContext_Impl::SwXMLTableCellContext_Impl(
375 SwXMLImport
& rImport
, sal_Int32
/*nElement*/,
376 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
377 SwXMLTableContext
*pTable
) :
378 SvXMLImportContext( rImport
),
379 m_xMyTable( pTable
),
381 m_bHasValue( false ),
382 m_bHasStringValue(false),
383 m_bValueTypeIsString(false),
388 m_bHasTextContent( false ),
389 m_bHasTableContent( false )
391 m_sSaveParaDefault
= GetImport().GetTextImport()->GetCellParaStyleDefault();
392 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
394 switch( aIter
.getToken() )
396 case XML_ELEMENT(TABLE
, XML_STYLE_NAME
):
397 m_aStyleName
= aIter
.toString();
398 GetImport().GetTextImport()->SetCellParaStyleDefault(m_aStyleName
);
400 case XML_ELEMENT(TABLE
, XML_NUMBER_COLUMNS_SPANNED
):
401 m_nColSpan
= static_cast<sal_uInt32
>(std::max
<sal_Int32
>(1, aIter
.toInt32()));
402 if (m_nColSpan
> 256)
404 SAL_INFO("sw.xml", "ignoring huge table:number-columns-spanned " << m_nColSpan
);
408 case XML_ELEMENT(TABLE
, XML_NUMBER_ROWS_SPANNED
):
409 m_nRowSpan
= static_cast<sal_uInt32
>(std::max
<sal_Int32
>(1, aIter
.toInt32()));
410 if (m_nRowSpan
> 8192 || (m_nRowSpan
> 256 && utl::ConfigManager::IsFuzzing()))
412 SAL_INFO("sw.xml", "ignoring huge table:number-rows-spanned " << m_nRowSpan
);
416 case XML_ELEMENT(TABLE
, XML_NUMBER_COLUMNS_REPEATED
):
417 m_nColRepeat
= static_cast<sal_uInt32
>(std::max
<sal_Int32
>(1, aIter
.toInt32()));
418 if (m_nColRepeat
> 256)
420 SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << m_nColRepeat
);
424 case XML_ELEMENT(TABLE
, XML_FORMULA
):
427 const sal_uInt16 nPrefix2
= GetImport().GetNamespaceMap().
428 GetKeyByAttrValueQName(aIter
.toString(), &sTmp
);
429 m_sFormula
= XML_NAMESPACE_OOOW
== nPrefix2
? sTmp
: aIter
.toString();
432 case XML_ELEMENT(OFFICE
, XML_VALUE
):
434 // Writer wrongly uses DBL_MAX to flag error but fails to
435 // check for it after import, so check that here, tdf#139126.
437 if (::sax::Converter::convertDouble(fTmp
, aIter
.toView()) && fTmp
< DBL_MAX
)
444 case XML_ELEMENT(OFFICE
, XML_TIME_VALUE
):
447 if (::sax::Converter::convertDuration(fTmp
, aIter
.toView()))
454 case XML_ELEMENT(OFFICE
, XML_DATE_VALUE
):
457 if (GetImport().GetMM100UnitConverter().convertDateTime(fTmp
,
465 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
468 if (::sax::Converter::convertBool(bTmp
, aIter
.toView()))
470 m_fValue
= (bTmp
? 1.0 : 0.0);
475 case XML_ELEMENT(TABLE
, XML_PROTECT
): // for backwards compatibility with SRC629 (and before)
476 case XML_ELEMENT(TABLE
, XML_PROTECTED
):
479 if (::sax::Converter::convertBool(bTmp
, aIter
.toView()))
485 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
487 m_StringValue
= aIter
.toString();
488 m_bHasStringValue
= true;
491 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
493 if ("string" == aIter
.toView())
495 m_bValueTypeIsString
= true;
497 // ignore other types - it would be correct to require
498 // matching value-type and $type-value attributes,
499 // but we've been reading those without checking forever.
503 SAL_WARN("sw", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter
.getToken()) << "=" << aIter
.toString());
508 inline void SwXMLTableCellContext_Impl::InsertContent_()
510 SwStartNode
const*const pStartNode( GetTable()->InsertTableSection(nullptr,
511 (m_bHasStringValue
&& m_bValueTypeIsString
&&
512 !m_aStyleName
.isEmpty()) ? & m_aStyleName
: nullptr) );
513 GetTable()->InsertCell( m_aStyleName
, m_nRowSpan
, m_nColSpan
,
515 nullptr, m_bProtect
, &m_sFormula
, m_bHasValue
, m_fValue
,
516 (m_bHasStringValue
&& m_bValueTypeIsString
) ? &m_StringValue
: nullptr);
519 inline void SwXMLTableCellContext_Impl::InsertContent()
521 OSL_ENSURE( !HasContent(), "content already there" );
522 m_bHasTextContent
= true;
526 inline void SwXMLTableCellContext_Impl::InsertContent(
527 SwXMLTableContext
*pTable
)
529 GetTable()->InsertCell( m_aStyleName
, m_nRowSpan
, m_nColSpan
, nullptr, pTable
, m_bProtect
);
530 m_bHasTableContent
= true;
533 css::uno::Reference
<css::xml::sax::XFastContextHandler
> SwXMLTableCellContext_Impl::createFastChildContext(
535 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
)
537 SvXMLImportContext
*pContext
= nullptr;
539 bool bSubTable
= false;
540 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE
) )
542 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
544 if( aIter
.getToken() == XML_ELEMENT(TABLE
, XML_IS_SUB_TABLE
) )
546 if ( IsXMLToken( aIter
, XML_TRUE
) )
550 XMLOFF_WARN_UNKNOWN("sw", aIter
);
559 SwXMLTableContext
*pTableContext
=
560 new SwXMLTableContext( GetSwImport(), GetTable() );
561 pContext
= pTableContext
;
562 if( GetTable()->IsValid() )
563 InsertContent( pTableContext
);
565 GetTable()->SetHasSubTables( true );
570 if( GetTable()->IsValid() && !HasContent() )
572 // fdo#60842: "office:string-value" overrides text content -> no import
573 if (!(m_bValueTypeIsString
&& m_bHasStringValue
))
575 pContext
= GetImport().GetTextImport()->CreateTextChildContext(
576 GetImport(), nElement
, xAttrList
,
584 void SwXMLTableCellContext_Impl::endFastElement(sal_Int32
)
586 if( GetTable()->IsValid() )
588 if( m_bHasTextContent
)
590 GetImport().GetTextImport()->DeleteParagraph();
591 if( m_nColRepeat
> 1 && m_nColSpan
== 1 )
593 // The original text is invalid after deleting the last
595 Reference
< XTextCursor
> xSrcTextCursor
=
596 GetImport().GetTextImport()->GetText()->createTextCursor();
597 xSrcTextCursor
->gotoEnd( true );
599 // Until we have an API for copying we have to use the core.
600 OTextCursorHelper
*pSrcTextCursor
= dynamic_cast<OTextCursorHelper
*>(xSrcTextCursor
.get());
601 assert(pSrcTextCursor
&& "SwXTextCursor missing");
602 SwDoc
*pDoc
= pSrcTextCursor
->GetDoc();
603 const SwPaM
*pSrcPaM
= pSrcTextCursor
->GetPaM();
605 while( m_nColRepeat
> 1 && GetTable()->IsInsertCellPossible() )
609 OTextCursorHelper
*pDstTextCursor
= dynamic_cast<OTextCursorHelper
*>(GetImport().GetTextImport()->GetCursor().get());
610 assert(pDstTextCursor
&& "SwXTextCursor missing");
611 SwPaM
aSrcPaM(*pSrcPaM
->GetMark(), *pSrcPaM
->GetPoint());
612 SwPosition
aDstPos( *pDstTextCursor
->GetPaM()->GetPoint() );
613 pDoc
->getIDocumentContentOperations().CopyRange(aSrcPaM
, aDstPos
, SwCopyFlags::CheckPosInFly
);
619 else if( !m_bHasTableContent
)
622 if( m_nColRepeat
> 1 && m_nColSpan
== 1 )
624 while( m_nColRepeat
> 1 && GetTable()->IsInsertCellPossible() )
632 GetImport().GetTextImport()->SetCellParaStyleDefault(m_sSaveParaDefault
);
637 class SwXMLTableColContext_Impl
: public SvXMLImportContext
639 SvXMLImportContextRef m_xMyTable
;
641 SwXMLTableContext
*GetTable() { return static_cast<SwXMLTableContext
*>(m_xMyTable
.get()); }
645 SwXMLTableColContext_Impl(
646 SwXMLImport
& rImport
,
647 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
648 SwXMLTableContext
*pTable
);
650 SwXMLImport
& GetSwImport() { return static_cast<SwXMLImport
&>(GetImport()); }
655 SwXMLTableColContext_Impl::SwXMLTableColContext_Impl(
656 SwXMLImport
& rImport
,
657 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
658 SwXMLTableContext
*pTable
) :
659 SvXMLImportContext( rImport
),
662 sal_uInt32 nColRep
= 1;
663 OUString aStyleName
, aDfltCellStyleName
;
665 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
667 switch (aIter
.getToken())
669 case XML_ELEMENT(TABLE
, XML_STYLE_NAME
):
670 aStyleName
= aIter
.toString();
672 case XML_ELEMENT(TABLE
, XML_NUMBER_COLUMNS_REPEATED
):
674 nColRep
= static_cast<sal_uInt32
>(std::max
<sal_Int32
>(1, aIter
.toInt32()));
677 SAL_INFO("sw.xml", "ignoring huge table:number-columns-repeated " << nColRep
);
682 case XML_ELEMENT(TABLE
, XML_DEFAULT_CELL_STYLE_NAME
):
683 aDfltCellStyleName
= aIter
.toString();
685 case XML_ELEMENT(XML
, XML_ID
):
687 //FIXME where to put this??? columns do not actually exist in writer...
691 XMLOFF_WARN_UNKNOWN("sw", aIter
);
695 sal_Int32 nWidth
= MINLAY
;
696 bool bRelWidth
= true;
697 if( !aStyleName
.isEmpty() )
699 const SwFormatFrameSize
*pSize
;
700 const SfxItemSet
*pAutoItemSet
= nullptr;
701 if( GetSwImport().FindAutomaticStyle(
702 XmlStyleFamily::TABLE_COLUMN
,
703 aStyleName
, &pAutoItemSet
) &&
705 (pSize
= pAutoItemSet
->GetItemIfSet( RES_FRM_SIZE
, false )) )
707 nWidth
= pSize
->GetWidth();
708 bRelWidth
= SwFrameSize::Variable
== pSize
->GetHeightSizeType();
714 while( nColRep
-- && GetTable()->IsInsertColPossible() )
715 GetTable()->InsertColumn( nWidth
, bRelWidth
, &aDfltCellStyleName
);
721 class SwXMLTableColsContext_Impl
: public SvXMLImportContext
723 SvXMLImportContextRef m_xMyTable
;
725 SwXMLTableContext
*GetTable() { return static_cast<SwXMLTableContext
*>(m_xMyTable
.get()); }
729 SwXMLTableColsContext_Impl(
730 SwXMLImport
& rImport
,
731 SwXMLTableContext
*pTable
);
733 virtual css::uno::Reference
< css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext(
734 sal_Int32 Element
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
> & xAttrList
) override
;
736 SwXMLImport
& GetSwImport() { return static_cast<SwXMLImport
&>(GetImport()); }
741 SwXMLTableColsContext_Impl::SwXMLTableColsContext_Impl(
742 SwXMLImport
& rImport
,
743 SwXMLTableContext
*pTable
) :
744 SvXMLImportContext( rImport
),
749 css::uno::Reference
< css::xml::sax::XFastContextHandler
> SwXMLTableColsContext_Impl::createFastChildContext(
750 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
> & xAttrList
)
752 SvXMLImportContext
*pContext
= nullptr;
754 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_COLUMN
) &&
755 GetTable()->IsInsertColPossible() )
756 pContext
= new SwXMLTableColContext_Impl( GetSwImport(), xAttrList
, GetTable() );
758 XMLOFF_WARN_UNKNOWN_ELEMENT("sw", nElement
);
765 class SwXMLTableRowContext_Impl
: public SvXMLImportContext
767 SvXMLImportContextRef m_xMyTable
;
769 sal_uInt32 m_nRowRepeat
;
771 SwXMLTableContext
*GetTable() { return static_cast<SwXMLTableContext
*>(m_xMyTable
.get()); }
775 SwXMLTableRowContext_Impl(
776 SwXMLImport
& rImport
, sal_Int32 nElement
,
777 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
778 SwXMLTableContext
*pTable
, bool bInHead
=false );
780 virtual css::uno::Reference
<css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext( sal_Int32 nElement
,
781 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
) override
;
783 virtual void SAL_CALL
endFastElement(sal_Int32 nElement
) override
;
785 SwXMLImport
& GetSwImport() { return static_cast<SwXMLImport
&>(GetImport()); }
790 SwXMLTableRowContext_Impl::SwXMLTableRowContext_Impl( SwXMLImport
& rImport
,
791 sal_Int32
/*nElement*/,
792 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
,
793 SwXMLTableContext
*pTable
,
795 SvXMLImportContext( rImport
),
796 m_xMyTable( pTable
),
799 OUString aStyleName
, aDfltCellStyleName
;
801 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
803 switch(aIter
.getToken())
805 case XML_ELEMENT(TABLE
, XML_STYLE_NAME
):
806 aStyleName
= aIter
.toString();
808 case XML_ELEMENT(STYLE
, XML_NUMBER_ROWS_REPEATED
):
810 m_nRowRepeat
= static_cast<sal_uInt32
>(std::max
<sal_Int32
>(1, aIter
.toInt32()));
811 if (m_nRowRepeat
> 8192 || (m_nRowRepeat
> 256 && utl::ConfigManager::IsFuzzing()))
813 SAL_INFO("sw.xml", "ignoring huge table:number-rows-repeated " << m_nRowRepeat
);
818 case XML_ELEMENT(STYLE
, XML_DEFAULT_CELL_STYLE_NAME
):
819 aDfltCellStyleName
= aIter
.toString();
821 case XML_ELEMENT(XML
, XML_ID
):
824 XMLOFF_WARN_UNKNOWN("sw", aIter
);
827 if( GetTable()->IsValid() )
828 GetTable()->InsertRow( aStyleName
, aDfltCellStyleName
, bInHead
);
831 void SwXMLTableRowContext_Impl::endFastElement(sal_Int32
)
833 if( GetTable()->IsValid() )
835 GetTable()->FinishRow();
837 if( m_nRowRepeat
> 1 )
838 GetTable()->InsertRepRows( m_nRowRepeat
);
842 css::uno::Reference
<css::xml::sax::XFastContextHandler
> SwXMLTableRowContext_Impl::createFastChildContext(
844 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
)
846 SvXMLImportContext
*pContext
= nullptr;
848 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_CELL
) ||
849 nElement
== XML_ELEMENT(LO_EXT
, XML_TABLE_CELL
) )
851 if( !GetTable()->IsValid() || GetTable()->IsInsertCellPossible() )
852 pContext
= new SwXMLTableCellContext_Impl( GetSwImport(), nElement
,
856 else if( nElement
== XML_ELEMENT(TABLE
, XML_COVERED_TABLE_CELL
) ||
857 nElement
== XML_ELEMENT(LO_EXT
, XML_COVERED_TABLE_CELL
) )
859 if (GetTable()->IsValid() && GetTable()->IsInsertCoveredCellPossible())
861 pContext
= new SwXMLCoveredTableCellContext(GetSwImport(), xAttrList
, *GetTable());
865 pContext
= new SvXMLImportContext(GetImport());
869 SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
876 class SwXMLTableRowsContext_Impl
: public SvXMLImportContext
878 SvXMLImportContextRef m_xMyTable
;
882 SwXMLTableContext
*GetTable() { return static_cast<SwXMLTableContext
*>(m_xMyTable
.get()); }
886 SwXMLTableRowsContext_Impl( SwXMLImport
& rImport
,
887 SwXMLTableContext
*pTable
,
890 virtual css::uno::Reference
<css::xml::sax::XFastContextHandler
> SAL_CALL
createFastChildContext(
892 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
) override
;
894 SwXMLImport
& GetSwImport() { return static_cast<SwXMLImport
&>(GetImport()); }
899 SwXMLTableRowsContext_Impl::SwXMLTableRowsContext_Impl( SwXMLImport
& rImport
,
900 SwXMLTableContext
*pTable
,
902 SvXMLImportContext( rImport
),
903 m_xMyTable( pTable
),
908 css::uno::Reference
<css::xml::sax::XFastContextHandler
> SwXMLTableRowsContext_Impl::createFastChildContext(
910 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
)
912 if( nElement
== XML_ELEMENT(TABLE
, XML_TABLE_ROW
) &&
913 GetTable()->IsInsertRowPossible() )
914 return new SwXMLTableRowContext_Impl( GetSwImport(), nElement
,
918 SAL_WARN("sw", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
922 class SwXMLDDETableContext_Impl
: public SvXMLImportContext
924 OUString m_sConnectionName
;
925 OUString m_sDDEApplication
;
927 OUString m_sDDETopic
;
928 bool m_bIsAutomaticUpdate
;
933 SwXMLDDETableContext_Impl(SwXMLImport
& rImport
);
935 virtual void SAL_CALL
startFastElement(
937 const Reference
<xml::sax::XFastAttributeList
> & xAttrList
) override
;
939 OUString
& GetConnectionName() { return m_sConnectionName
; }
940 OUString
& GetDDEApplication() { return m_sDDEApplication
; }
941 OUString
& GetDDEItem() { return m_sDDEItem
; }
942 OUString
& GetDDETopic() { return m_sDDETopic
; }
943 bool GetIsAutomaticUpdate() const { return m_bIsAutomaticUpdate
; }
947 SwXMLDDETableContext_Impl::SwXMLDDETableContext_Impl(SwXMLImport
& rImport
) :
948 SvXMLImportContext(rImport
),
949 m_bIsAutomaticUpdate(false)
953 void SwXMLDDETableContext_Impl::startFastElement(
954 sal_Int32
/*nElement*/,
955 const Reference
<xml::sax::XFastAttributeList
> & xAttrList
)
957 for( auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
) )
959 switch (aIter
.getToken())
961 case XML_ELEMENT(OFFICE
, XML_DDE_APPLICATION
):
962 m_sDDEApplication
= aIter
.toString();
964 case XML_ELEMENT(OFFICE
, XML_DDE_TOPIC
):
965 m_sDDETopic
= aIter
.toString();
967 case XML_ELEMENT(OFFICE
, XML_DDE_ITEM
):
968 m_sDDEItem
= aIter
.toString();
970 case XML_ELEMENT(OFFICE
, XML_NAME
):
971 m_sConnectionName
= aIter
.toString();
973 case XML_ELEMENT(OFFICE
, XML_AUTOMATIC_UPDATE
):
976 if (::sax::Converter::convertBool(bTmp
, aIter
.toView()))
978 m_bIsAutomaticUpdate
= bTmp
;
983 XMLOFF_WARN_UNKNOWN("sw", aIter
);
985 // else: unknown attribute namespace
989 // generate a new name for DDE field type (called by lcl_GetDDEFieldType below)
990 static OUString
lcl_GenerateFieldTypeName(const OUString
& sPrefix
, SwTableNode
* pTableNode
)
992 const OUString
sPrefixStr(sPrefix
.isEmpty() ? OUString("_") : sPrefix
);
994 // increase count until we find a name that is not yet taken
996 sal_Int32 nCount
= 0;
999 // this is crazy, but just in case all names are taken: exit gracefully
1000 if (nCount
== SAL_MAX_INT32
)
1004 sName
= sPrefixStr
+ OUString::number(nCount
);
1006 while (nullptr != pTableNode
->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde
, sName
, false));
1011 // set table properties
1012 static SwDDEFieldType
* lcl_GetDDEFieldType(SwXMLDDETableContext_Impl
* pContext
,
1013 SwTableNode
* pTableNode
)
1015 // make command string
1016 const OUString
sCommand(pContext
->GetDDEApplication()
1017 + OUStringChar(sfx2::cTokenSeparator
)
1018 + pContext
->GetDDEItem()
1019 + OUStringChar(sfx2::cTokenSeparator
)
1020 + pContext
->GetDDETopic());
1022 const SfxLinkUpdateMode nType
= pContext
->GetIsAutomaticUpdate()
1023 ? SfxLinkUpdateMode::ALWAYS
1024 : SfxLinkUpdateMode::ONCALL
;
1026 OUString
sName(pContext
->GetConnectionName());
1028 // field type to be returned
1029 SwDDEFieldType
* pType
= nullptr;
1032 if (sName
.isEmpty())
1034 sName
= lcl_GenerateFieldTypeName(pContext
->GetDDEApplication(),
1039 // check for existing DDE field type with the same name
1040 SwDDEFieldType
* pOldType
= static_cast<SwDDEFieldType
*>(pTableNode
->GetDoc().getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde
, sName
, false));
1041 if (nullptr != pOldType
)
1043 // same values -> return old type
1044 if ( (pOldType
->GetCmd() == sCommand
) &&
1045 (pOldType
->GetType() == nType
) )
1047 // same name, same values -> return old type!
1052 // same name, different values -> think of new name
1053 sName
= lcl_GenerateFieldTypeName(pContext
->GetDDEApplication(),
1057 // no old type -> create new one
1060 // create new field type (unless we already have one)
1061 if (nullptr == pType
)
1063 // create new field type and return
1064 SwDDEFieldType
aDDEFieldType(sName
, sCommand
, nType
);
1065 pType
= static_cast<SwDDEFieldType
*>(pTableNode
->
1066 GetDoc().getIDocumentFieldsAccess().InsertFieldType(aDDEFieldType
));
1069 OSL_ENSURE(nullptr != pType
, "We really want a SwDDEFieldType here!");
1080 TableBoxIndex( OUString aName
, sal_Int32 nWidth
,
1082 msName(std::move( aName
)),
1084 mbProtected( bProtected
)
1087 bool operator== ( const TableBoxIndex
& rArg
) const
1089 return (rArg
.mnWidth
== mnWidth
) &&
1090 (rArg
.mbProtected
== mbProtected
) &&
1091 (rArg
.msName
== msName
);
1095 class TableBoxIndexHasher
1098 size_t operator() (const TableBoxIndex
& rArg
) const
1100 return rArg
.msName
.hashCode() + rArg
.mnWidth
+ (rArg
.mbProtected
? 1 : 0);
1104 const SwXMLTableCell_Impl
*SwXMLTableContext::GetCell( sal_uInt32 nRow
,
1105 sal_uInt32 nCol
) const
1107 return (*m_pRows
)[nRow
]->GetCell( nCol
);
1110 SwXMLTableCell_Impl
*SwXMLTableContext::GetCell( sal_uInt32 nRow
,
1113 return (*m_pRows
)[nRow
]->GetCell( nCol
);
1117 SwXMLTableContext::SwXMLTableContext( SwXMLImport
& rImport
,
1118 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
) :
1119 XMLTextTableContext( rImport
),
1120 m_pRows( new SwXMLTableRows_Impl
),
1121 m_pTableNode( nullptr ),
1123 m_bOwnsBox1( false ),
1124 m_pSttNd1( nullptr ),
1125 m_pBoxFormat( nullptr ),
1126 m_pLineFormat( nullptr ),
1127 m_bFirstSection( true ),
1128 m_bRelWidth( true ),
1129 m_bHasSubTables( false ),
1133 m_nNonMergedCurCol( 0 ),
1139 // this method will modify the document directly -> lock SolarMutex
1140 SolarMutexGuard aGuard
;
1142 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
1144 const OUString sValue
= aIter
.toString();
1145 switch(aIter
.getToken())
1147 case XML_ELEMENT(TABLE
, XML_STYLE_NAME
):
1148 m_aStyleName
= sValue
;
1150 case XML_ELEMENT(TABLE
, XML_NAME
):
1153 case XML_ELEMENT(TABLE
, XML_DEFAULT_CELL_STYLE_NAME
):
1154 m_aDfltCellStyleName
= sValue
;
1156 case XML_ELEMENT(TABLE
, XML_TEMPLATE_NAME
):
1157 m_aTemplateName
= sValue
;
1159 case XML_ELEMENT(XML
, XML_ID
):
1163 XMLOFF_WARN_UNKNOWN("sw", aIter
);
1167 SwDoc
*pDoc
= GetSwImport().getDoc();
1169 OUString sTableName
;
1170 if( !aName
.isEmpty() )
1172 const SwTableFormat
*pTableFormat
= pDoc
->FindTableFormatByName( aName
);
1176 if( sTableName
.isEmpty() )
1178 // Optimization: use import's own map to create unique names, because
1179 // SwDoc::GetUniqueTableName scans all the already present tables,
1180 // builds a bitset using rather complex rules, and that has quadratic
1181 // complexity. Try once, then fallback to SwDoc::GetUniqueTableName
1182 auto& tableNameMap
= rImport
.GetTableNameMap();
1183 sal_Int32 nextIx
= ++tableNameMap
[aName
];
1184 OUString test
= aName
.isEmpty()
1185 ? OUString(rImport
.GetDefTableName() + OUString::number(nextIx
))
1186 : OUString(aName
+ "_" + OUString::number(nextIx
));
1187 if (const SwTableFormat
* pExisting
= pDoc
->FindTableFormatByName(test
); !pExisting
)
1190 sTableName
= pDoc
->GetUniqueTableName();
1191 GetImport().GetTextImport()
1192 ->GetRenameMap().Add( XML_TEXT_RENAME_TYPE_TABLE
, aName
, sTableName
);
1195 Reference
< XTextTable
> xTable
;
1196 SwXTextTable
*pXTable
= nullptr;
1197 Reference
<XMultiServiceFactory
> xFactory( GetImport().GetModel(),
1199 OSL_ENSURE( xFactory
.is(), "factory missing" );
1202 Reference
<XInterface
> xIfc
= xFactory
->createInstance( "com.sun.star.text.TextTable" );
1203 OSL_ENSURE( xIfc
.is(), "Couldn't create a table" );
1206 xTable
.set( xIfc
, UNO_QUERY
);
1211 xTable
->initialize( 1, 1 );
1212 if (auto xPropSet
= xTable
.query
<css::beans::XPropertySet
>())
1213 xPropSet
->setPropertyValue(UNO_NAME_TABLE_NAME
, css::uno::Any(sTableName
));
1217 m_xTextContent
= xTable
;
1218 GetImport().GetTextImport()->InsertTextContent( m_xTextContent
);
1220 catch( IllegalArgumentException
& )
1229 // xml:id for RDF metadata
1230 GetImport().SetXmlId(xTable
, sXmlId
);
1232 pXTable
= dynamic_cast<SwXTextTable
*>(xTable
.get());
1234 Reference
< XCellRange
> xCellRange( xTable
, UNO_QUERY
);
1235 Reference
< XCell
> xCell
= xCellRange
->getCellByPosition( 0, 0 );
1236 Reference
< XText
> xText( xCell
, UNO_QUERY
);
1237 m_xOldCursor
= GetImport().GetTextImport()->GetCursor();
1238 GetImport().GetTextImport()->SetCursor( xText
->createTextCursor() );
1240 // take care of open redlines for tables
1241 GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
1246 SwFrameFormat
*const pTableFrameFormat
= pXTable
->GetFrameFormat();
1247 OSL_ENSURE( pTableFrameFormat
, "table format missing" );
1248 SwTable
*pTable
= SwTable::FindTable( pTableFrameFormat
);
1249 OSL_ENSURE( pTable
, "table missing" );
1250 m_pTableNode
= pTable
->GetTableNode();
1251 OSL_ENSURE( m_pTableNode
, "table node missing" );
1253 SwTableLine
*pLine1
= m_pTableNode
->GetTable().GetTabLines()[0U];
1254 m_pBox1
= pLine1
->GetTabBoxes()[0U];
1255 m_pSttNd1
= m_pBox1
->GetSttNd();
1258 SwXMLTableContext::SwXMLTableContext( SwXMLImport
& rImport
,
1259 SwXMLTableContext
*pTable
) :
1260 XMLTextTableContext( rImport
),
1261 m_pRows( new SwXMLTableRows_Impl
),
1262 m_pTableNode( pTable
->m_pTableNode
),
1264 m_bOwnsBox1( false ),
1265 m_pSttNd1( nullptr ),
1266 m_pBoxFormat( nullptr ),
1267 m_pLineFormat( nullptr ),
1268 m_xParentTable( pTable
),
1269 m_bFirstSection( false ),
1270 m_bRelWidth( true ),
1271 m_bHasSubTables( false ),
1275 m_nNonMergedCurCol( 0 ),
1280 SwXMLTableContext::~SwXMLTableContext()
1284 m_xColumnDefaultCellStyleNames
.reset();
1285 m_pSharedBoxFormats
.reset();
1288 // close redlines on table end nodes
1289 GetImport().GetTextImport()->RedlineAdjustStartNodeCursor();
1292 css::uno::Reference
<css::xml::sax::XFastContextHandler
> SwXMLTableContext::createFastChildContext(
1294 const Reference
< xml::sax::XFastAttributeList
> & xAttrList
)
1296 bool bHeader
= false;
1299 case XML_ELEMENT(TABLE
, XML_TABLE_ROW
):
1300 case XML_ELEMENT(LO_EXT
, XML_TABLE_ROW
):
1301 if( IsInsertRowPossible() )
1302 return new SwXMLTableRowContext_Impl( GetSwImport(), nElement
, xAttrList
, this );
1304 case XML_ELEMENT(TABLE
, XML_TABLE_HEADER_ROWS
):
1307 case XML_ELEMENT(TABLE
, XML_TABLE_ROWS
):
1308 return new SwXMLTableRowsContext_Impl( GetSwImport(), this, bHeader
);
1309 case XML_ELEMENT(TABLE
, XML_TABLE_HEADER_COLUMNS
):
1310 case XML_ELEMENT(TABLE
, XML_TABLE_COLUMNS
):
1311 // There are slight differences between <table:table-columns> and
1312 // <table:table-columns-groups>. However, none of these are
1313 // supported in Writer (they are Calc-only features), so we
1314 // support column groups by simply using the <table:table-columns>
1315 // token for column groups, too.
1316 case XML_ELEMENT(TABLE
, XML_TABLE_COLUMN_GROUP
):
1318 return new SwXMLTableColsContext_Impl( GetSwImport(), this );
1320 case XML_ELEMENT(TABLE
, XML_TABLE_COLUMN
):
1321 case XML_ELEMENT(LO_EXT
, XML_TABLE_COLUMN
):
1322 if( IsValid() && IsInsertColPossible() )
1323 return new SwXMLTableColContext_Impl( GetSwImport(), xAttrList
,
1326 case XML_ELEMENT(OFFICE
, XML_DDE_SOURCE
):
1327 // save context for later processing (discard old context, if approp.)
1330 m_xDDESource
.set(new SwXMLDDETableContext_Impl( GetSwImport() ));
1331 return m_xDDESource
;
1338 void SwXMLTableContext::InsertColumn( sal_Int32 nWidth2
, bool bRelWidth2
,
1339 const OUString
*pDfltCellStyleName
)
1341 OSL_ENSURE( m_nCurCol
< USHRT_MAX
,
1342 "SwXMLTableContext::InsertColumn: no space left" );
1343 if( m_nCurCol
>= USHRT_MAX
)
1346 if( nWidth2
< MINLAY
)
1348 else if( nWidth2
> MAX_WIDTH
)
1349 nWidth2
= MAX_WIDTH
;
1350 m_aColumnWidths
.emplace_back(nWidth2
, bRelWidth2
);
1351 if( !((pDfltCellStyleName
&& !pDfltCellStyleName
->isEmpty()) ||
1352 m_xColumnDefaultCellStyleNames
) )
1355 if( !m_xColumnDefaultCellStyleNames
)
1357 m_xColumnDefaultCellStyleNames
.emplace();
1358 sal_uLong nCount
= m_aColumnWidths
.size() - 1;
1360 m_xColumnDefaultCellStyleNames
->push_back(OUString());
1363 if(pDfltCellStyleName
)
1364 m_xColumnDefaultCellStyleNames
->push_back(*pDfltCellStyleName
);
1366 m_xColumnDefaultCellStyleNames
->push_back(OUString());
1369 sal_Int32
SwXMLTableContext::GetColumnWidth( sal_uInt32 nCol
,
1370 sal_uInt32 nColSpan
) const
1372 sal_uInt32 nLast
= nCol
+nColSpan
;
1373 if( nLast
> m_aColumnWidths
.size() )
1374 nLast
= m_aColumnWidths
.size();
1376 sal_Int32 nWidth2
= 0;
1377 for( sal_uInt32 i
=nCol
; i
< nLast
; ++i
)
1378 nWidth2
+= m_aColumnWidths
[i
].width
;
1383 OUString
SwXMLTableContext::GetColumnDefaultCellStyleName( sal_uInt32 nCol
) const
1385 if( m_xColumnDefaultCellStyleNames
&& nCol
< m_xColumnDefaultCellStyleNames
->size())
1386 return (*m_xColumnDefaultCellStyleNames
)[static_cast<size_t>(nCol
)];
1391 void SwXMLTableContext::InsertCell( const OUString
& rStyleName
,
1392 sal_uInt32 nRowSpan
, sal_uInt32 nColSpan
,
1393 const SwStartNode
*pStartNode
,
1394 SwXMLTableContext
*pTable
,
1396 const OUString
* pFormula
,
1399 OUString
const*const pStringValue
)
1401 OSL_ENSURE( m_nCurCol
< GetColumnCount(),
1402 "SwXMLTableContext::InsertCell: row is full" );
1403 OSL_ENSURE( m_nCurRow
< USHRT_MAX
,
1404 "SwXMLTableContext::InsertCell: table is full" );
1405 if( m_nCurCol
>= USHRT_MAX
|| m_nCurRow
> USHRT_MAX
)
1408 OSL_ENSURE( nRowSpan
>=1, "SwXMLTableContext::InsertCell: row span is 0" );
1411 OSL_ENSURE( nColSpan
>=1, "SwXMLTableContext::InsertCell: col span is 0" );
1415 // Until it is possible to add columns here, fix the column span.
1416 sal_uInt32 nColsReq
= m_nCurCol
+ nColSpan
;
1417 if( nColsReq
> GetColumnCount() )
1419 nColSpan
= GetColumnCount() - m_nCurCol
;
1420 nColsReq
= GetColumnCount();
1423 // Check whether there are cells from a previous line already that reach
1424 // into the current row.
1425 if( m_nCurRow
> 0 && nColSpan
> 1 )
1427 SwXMLTableRow_Impl
*pCurRow
= (*m_pRows
)[m_nCurRow
].get();
1428 sal_uInt32 nLastCol
= GetColumnCount() < nColsReq
? GetColumnCount()
1430 for( sal_uInt32 i
=m_nCurCol
+1; i
<nLastCol
; ++i
)
1432 if( pCurRow
->GetCell(i
)->IsUsed() )
1434 // If this cell is used, the column span is truncated
1435 nColSpan
= i
- m_nCurCol
;
1442 sal_uInt32 nRowsReq
= m_nCurRow
+ nRowSpan
;
1443 if( nRowsReq
> USHRT_MAX
)
1445 nRowSpan
= USHRT_MAX
- m_nCurRow
;
1446 nRowsReq
= USHRT_MAX
;
1449 // Add columns (if # required columns greater than # columns):
1450 // This should never happen, since we require column definitions!
1451 if ( nColsReq
> GetColumnCount() )
1453 for( sal_uInt32 i
=GetColumnCount(); i
<nColsReq
; ++i
)
1455 m_aColumnWidths
.emplace_back(MINLAY
, true );
1457 // adjust columns in *all* rows, if columns must be inserted
1458 for (size_t i
= 0; i
< m_pRows
->size(); ++i
)
1459 (*m_pRows
)[i
]->Expand( nColsReq
, i
<m_nCurRow
);
1463 if (m_pRows
->size() < nRowsReq
)
1465 for (size_t i
= m_pRows
->size(); i
< nRowsReq
; ++i
)
1466 m_pRows
->push_back(std::make_unique
<SwXMLTableRow_Impl
>(
1467 "", GetColumnCount()));
1470 OUString
sStyleName( rStyleName
);
1471 if( sStyleName
.isEmpty() )
1473 sStyleName
= (*m_pRows
)[m_nCurRow
]->GetDefaultCellStyleName();
1474 if( sStyleName
.isEmpty() && m_xColumnDefaultCellStyleNames
)
1476 sStyleName
= GetColumnDefaultCellStyleName( m_nCurCol
);
1477 if( sStyleName
.isEmpty() )
1478 sStyleName
= m_aDfltCellStyleName
;
1483 for( sal_uInt32 i
=nColSpan
; i
>0; --i
)
1485 for( sal_uInt32 j
=nRowSpan
; j
>0; --j
)
1487 const bool bCovered
= i
!= nColSpan
|| j
!= nRowSpan
;
1488 SwXMLTableCell_Impl
*pCell
= GetCell( nRowsReq
-j
, nColsReq
-i
);
1490 throw css::lang::IndexOutOfBoundsException();
1491 pCell
->Set( sStyleName
, j
, i
, pStartNode
,
1492 pTable
, bProtect
, pFormula
, bHasValue
, bCovered
, fValue
,
1497 // Set current col to the next (free) column
1498 m_nCurCol
= nColsReq
;
1499 m_nNonMergedCurCol
= nColsReq
;
1500 while( m_nCurCol
<GetColumnCount() && GetCell(m_nCurRow
,m_nCurCol
)->IsUsed() )
1504 void SwXMLTableContext::InsertCoveredCell(const OUString
& rStyleName
)
1506 const IDocumentSettingAccess
& rIDSA
= GetSwImport().getDoc()->getIDocumentSettingAccess();
1507 bool bWordTableCell
= rIDSA
.get(DocumentSettingId::TABLE_ROW_KEEP
);
1508 if (!bWordTableCell
)
1510 // Compatibility flag not active, ignore formatting of covered cells.
1514 SwXMLTableCell_Impl
* pCell
= GetCell(m_nCurRow
, m_nNonMergedCurCol
);
1515 ++m_nNonMergedCurCol
;
1521 pCell
->SetStyleName(rStyleName
);
1524 void SwXMLTableContext::InsertRow( const OUString
& rStyleName
,
1525 const OUString
& rDfltCellStyleName
,
1528 OSL_ENSURE( m_nCurRow
< USHRT_MAX
,
1529 "SwXMLTableContext::InsertRow: no space left" );
1530 if( m_nCurRow
>= USHRT_MAX
)
1533 // Make sure there is at least one column.
1534 if( 0==m_nCurRow
&& 0 == GetColumnCount() )
1535 InsertColumn( USHRT_MAX
, true );
1537 if (m_nCurRow
< m_pRows
->size())
1539 // The current row has already been inserted because of a row span
1540 // of a previous row.
1541 (*m_pRows
)[m_nCurRow
]->Set(
1542 rStyleName
, rDfltCellStyleName
);
1547 m_pRows
->push_back(std::make_unique
<SwXMLTableRow_Impl
>(
1548 rStyleName
, GetColumnCount(),
1549 &rDfltCellStyleName
));
1552 // We start at the first column ...
1554 m_nNonMergedCurCol
= 0;
1556 // ... but this cell may be occupied already.
1557 while( m_nCurCol
<GetColumnCount() && GetCell(m_nCurRow
,m_nCurCol
)->IsUsed() )
1560 if( bInHead
&& m_nHeaderRows
== m_nCurRow
)
1564 void SwXMLTableContext::InsertRepRows( sal_uInt32 nCount
)
1566 const SwXMLTableRow_Impl
*pSrcRow
= (*m_pRows
)[m_nCurRow
-1].get();
1567 while( nCount
> 1 && IsInsertRowPossible() )
1569 InsertRow( pSrcRow
->GetStyleName(), pSrcRow
->GetDefaultCellStyleName(),
1571 while( m_nCurCol
< GetColumnCount() )
1573 if( !GetCell(m_nCurRow
,m_nCurCol
)->IsUsed() )
1575 const SwXMLTableCell_Impl
*pSrcCell
=
1576 GetCell( m_nCurRow
-1, m_nCurCol
);
1577 InsertCell( pSrcCell
->GetStyleName(), 1U,
1578 pSrcCell
->GetColSpan(),
1579 InsertTableSection(),
1580 nullptr, pSrcCell
->IsProtected(),
1581 &pSrcCell
->GetFormula(),
1582 pSrcCell
->HasValue(), pSrcCell
->GetValue(),
1583 pSrcCell
->GetStringValue() );
1591 void SwXMLTableContext::FinishRow()
1593 // Insert an empty cell at the end of the line if the row is not complete
1594 if( m_nCurCol
< GetColumnCount() )
1596 InsertCell( "", 1U, GetColumnCount() - m_nCurCol
,
1597 InsertTableSection() );
1600 // Move to the next row.
1604 const SwStartNode
*SwXMLTableContext::GetPrevStartNode( sal_uInt32 nRow
,
1605 sal_uInt32 nCol
) const
1607 const SwXMLTableCell_Impl
*pPrevCell
= nullptr;
1608 if( GetColumnCount() == nCol
)
1610 // The last cell is the right one here.
1611 pPrevCell
= GetCell( m_pRows
->size() - 1U, GetColumnCount() - 1 );
1615 // The previous cell in this row.
1616 pPrevCell
= GetCell( nRow
, nCol
-1 );
1620 // The last cell from the previous row.
1621 pPrevCell
= GetCell( nRow
-1, GetColumnCount()-1 );
1624 const SwStartNode
*pSttNd
= nullptr;
1627 if( pPrevCell
->GetStartNode() )
1628 pSttNd
= pPrevCell
->GetStartNode();
1629 // #i95726# - Some fault tolerance
1631 else if ( pPrevCell
->GetSubTable() )
1632 pSttNd
= pPrevCell
->GetSubTable()->GetLastStartNode();
1634 OSL_ENSURE( pSttNd
!= nullptr,
1641 void SwXMLTableContext::FixRowSpan( sal_uInt32 nRow
, sal_uInt32 nCol
,
1642 sal_uInt32 nColSpan
)
1644 sal_uInt32 nLastCol
= nCol
+ nColSpan
;
1645 for( sal_uInt32 i
= nCol
; i
< nLastCol
; i
++ )
1647 sal_uInt32 j
= nRow
;
1648 sal_uInt32 nRowSpan
= 1;
1649 SwXMLTableCell_Impl
*pCell
= GetCell( j
, i
);
1650 while( pCell
&& pCell
->GetRowSpan() > 1 )
1652 pCell
->SetRowSpan( nRowSpan
++ );
1653 pCell
= j
> 0 ? GetCell( --j
, i
) : nullptr;
1658 void SwXMLTableContext::ReplaceWithEmptyCell( sal_uInt32 nRow
, sal_uInt32 nCol
, bool bRows
)
1660 const SwStartNode
*pPrevSttNd
= GetPrevStartNode( nRow
, nCol
);
1661 const SwStartNode
*pSttNd
= InsertTableSection( pPrevSttNd
);
1663 const SwXMLTableCell_Impl
*pCell
= GetCell( nRow
, nCol
);
1664 sal_uInt32 nLastRow
= bRows
? nRow
+ pCell
->GetRowSpan() : nRow
+ 1;
1665 sal_uInt32 nLastCol
= nCol
+ pCell
->GetColSpan();
1667 for( sal_uInt32 i
=nRow
; i
<nLastRow
; i
++ )
1669 SwXMLTableRow_Impl
*pRow
= (*m_pRows
)[i
].get();
1670 for( sal_uInt32 j
=nCol
; j
<nLastCol
; j
++ )
1671 pRow
->GetCell( j
)->SetStartNode( pSttNd
);
1676 SwTableBox
*SwXMLTableContext::NewTableBox( const SwStartNode
*pStNd
,
1677 SwTableLine
*pUpper
)
1679 // The topmost table is the only table that maintains the two members
1680 // pBox1 and bFirstSection.
1681 if( m_xParentTable
.is() )
1682 return static_cast<SwXMLTableContext
*>(m_xParentTable
.get())->NewTableBox( pStNd
,
1688 m_pBox1
->GetSttNd() == pStNd
)
1690 // if the StartNode is equal to the StartNode of the initially
1691 // created box, we use this box
1693 pBox
->SetUpper( pUpper
);
1695 m_bOwnsBox1
= false;
1698 pBox
= new SwTableBox( m_pBoxFormat
, *pStNd
, pUpper
);
1703 SwTableBoxFormat
* SwXMLTableContext::GetSharedBoxFormat(
1705 const OUString
& rStyleName
,
1706 sal_Int32 nColumnWidth
,
1710 bool* pModifyLocked
)
1712 if ( m_pSharedBoxFormats
== nullptr )
1713 m_pSharedBoxFormats
.reset(new map_BoxFormat
);
1715 SwTableBoxFormat
* pBoxFormat2
;
1717 TableBoxIndex
aKey( rStyleName
, nColumnWidth
, bProtected
);
1718 map_BoxFormat::iterator aIter
= m_pSharedBoxFormats
->find( aKey
);
1719 if ( aIter
== m_pSharedBoxFormats
->end() )
1721 // unknown format so far -> construct a new one
1723 // get the old format, and reset all attributes
1724 // (but preserve FillOrder)
1725 pBoxFormat2
= static_cast<SwTableBoxFormat
*>(pBox
->ClaimFrameFormat());
1726 SwFormatFillOrder
aFillOrder( pBoxFormat2
->GetFillOrder() );
1727 pBoxFormat2
->ResetAllFormatAttr(); // #i73790# - method renamed
1728 pBoxFormat2
->SetFormatAttr( aFillOrder
);
1729 bNew
= true; // it's a new format now
1731 // share this format, if allowed
1733 (*m_pSharedBoxFormats
)[ aKey
] = pBoxFormat2
;
1737 // set the shared format
1738 pBoxFormat2
= aIter
->second
;
1739 pBox
->ChgFrameFormat( pBoxFormat2
, /*bNeedToReregister*/false );
1740 bNew
= false; // copied from an existing format
1742 // claim it, if we are not allowed to share
1744 pBoxFormat2
= static_cast<SwTableBoxFormat
*>(pBox
->ClaimFrameFormat());
1747 // lock format (if so desired)
1748 if ( pModifyLocked
!= nullptr )
1750 (*pModifyLocked
) = pBoxFormat2
->IsModifyLocked();
1751 pBoxFormat2
->LockModify();
1757 SwTableBox
*SwXMLTableContext::MakeTableBox( SwTableLine
*pUpper
,
1759 sal_uInt32 nLeftCol
,
1760 sal_uInt32 nBottomRow
,
1761 sal_uInt32 nRightCol
)
1763 //FIXME: here would be a great place to handle XmlId for cell
1764 SwTableBox
*pBox
= new SwTableBox( m_pBoxFormat
, 0, pUpper
);
1766 sal_uInt32 nColSpan
= nRightCol
- nLeftCol
;
1767 sal_Int32 nColWidth
= GetColumnWidth( nLeftCol
, nColSpan
);
1769 // TODO: Share formats!
1770 SwFrameFormat
*pFrameFormat
= pBox
->ClaimFrameFormat();
1771 SwFormatFillOrder
aFillOrder( pFrameFormat
->GetFillOrder() );
1772 pFrameFormat
->ResetAllFormatAttr(); // #i73790# - method renamed
1773 pFrameFormat
->SetFormatAttr( aFillOrder
);
1775 pFrameFormat
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nColWidth
) );
1777 SwTableLines
& rLines
= pBox
->GetTabLines();
1778 bool bSplitted
= false;
1782 sal_uInt32 nStartRow
= nTopRow
;
1785 for( i
= nTopRow
; i
< nBottomRow
; i
++ )
1787 // Could the table be split behind the current row?
1789 SwXMLTableRow_Impl
*pRow
= (*m_pRows
)[i
].get();
1790 for( sal_uInt32 j
=nLeftCol
; j
<nRightCol
; j
++ )
1792 bSplit
= ( 1 == pRow
->GetCell(j
)->GetRowSpan() );
1796 if( bSplit
&& (nStartRow
>nTopRow
|| i
+1<nBottomRow
) )
1798 SwTableLine
*pLine
=
1799 MakeTableLine( pBox
, nStartRow
, nLeftCol
, i
+1,
1802 rLines
.push_back( pLine
);
1810 // No splitting was possible. That for, we have to force it.
1813 nStartRow
= nTopRow
;
1814 while( nStartRow
< nBottomRow
)
1816 sal_uInt32 nMaxRowSpan
= 0;
1817 SwXMLTableRow_Impl
*pStartRow
= (*m_pRows
)[nStartRow
].get();
1818 const SwXMLTableCell_Impl
*pCell
;
1819 for( i
=nLeftCol
; i
<nRightCol
; i
++ )
1821 pCell
= pStartRow
->GetCell(i
);
1822 if( pCell
->GetRowSpan() > nMaxRowSpan
)
1823 nMaxRowSpan
= pCell
->GetRowSpan();
1826 nStartRow
+= nMaxRowSpan
;
1827 if( nStartRow
<nBottomRow
)
1829 SwXMLTableRow_Impl
*pPrevRow
= (*m_pRows
)[nStartRow
- 1U].get();
1831 while( i
< nRightCol
)
1833 if( pPrevRow
->GetCell(i
)->GetRowSpan() > 1 )
1835 const SwXMLTableCell_Impl
*pCell2
=
1836 GetCell( nStartRow
, i
);
1837 const sal_uInt32 nColSpan2
= pCell2
->GetColSpan();
1838 FixRowSpan( nStartRow
-1, i
, nColSpan2
);
1839 ReplaceWithEmptyCell( nStartRow
, i
, true );
1849 // and now start over again...
1856 SwTableBox
*SwXMLTableContext::MakeTableBox(
1857 SwTableLine
*pUpper
, const SwXMLTableCell_Impl
*pCell
,
1858 sal_uInt32 nLeftCol
, sal_uInt32 nRightCol
)
1860 //FIXME: here would be a great place to handle XmlId for cell
1862 sal_uInt32 nColSpan
= nRightCol
- nLeftCol
;
1863 sal_Int32 nColWidth
= GetColumnWidth( nLeftCol
, nColSpan
);
1865 if( pCell
->GetStartNode() )
1867 pBox
= NewTableBox( pCell
->GetStartNode(), pUpper
);
1871 // and it is a table: therefore we build a new box and
1872 // put the rows of the table into the rows of the box
1873 pBox
= new SwTableBox( m_pBoxFormat
, 0, pUpper
);
1874 pCell
->GetSubTable()->MakeTable( pBox
, nColWidth
);
1878 const OUString sStyleName
= pCell
->GetStyleName();
1881 SwTableBoxFormat
*pBoxFormat2
= GetSharedBoxFormat(
1882 pBox
, sStyleName
, nColWidth
, pCell
->IsProtected(),
1883 pCell
->GetStartNode() && pCell
->GetFormula().isEmpty() &&
1884 ! pCell
->HasValue(),
1885 bNew
, &bModifyLocked
);
1887 // if a new format was created, then we need to set the style
1891 const SfxItemSet
*pAutoItemSet
= nullptr;
1892 if( pCell
->GetStartNode() && !sStyleName
.isEmpty() &&
1893 GetSwImport().FindAutomaticStyle(
1894 XmlStyleFamily::TABLE_CELL
, sStyleName
, &pAutoItemSet
) )
1897 pBoxFormat2
->SetFormatAttr( *pAutoItemSet
);
1901 if( pCell
->GetStartNode() )
1903 if (pCell
->HasStringValue())
1905 SwNodeIndex
const aNodeIndex(*(pCell
->GetStartNode()), 1);
1906 SwTextNode
*const pTextNode(aNodeIndex
.GetNode().GetTextNode());
1907 SAL_WARN_IF(!pTextNode
, "sw", "Should have a text node in cell?");
1910 SAL_WARN_IF(!pTextNode
->GetText().isEmpty(), "sw",
1912 pTextNode
->InsertText(*pCell
->GetStringValue(),
1913 SwContentIndex(pTextNode
, 0));
1917 // try to rescue broken documents with a certain pattern
1918 // if: 1) the cell has a default number format (number 0)
1919 // 2) the call has no formula
1920 // 3) the value is 0.0
1921 // 4) the text doesn't look anything like 0.0
1922 // [read: length > 10, or length smaller 10 and no 0 in it]
1923 // then make it a text cell!
1924 bool bSuppressNumericContent
= false;
1925 if( pCell
->HasValue() && (pCell
->GetValue() == 0.0) &&
1926 pCell
->GetFormula().isEmpty() &&
1927 !sStyleName
.isEmpty() )
1929 // default num format?
1930 if( const SwTableBoxNumFormat
* pNumFormat
= pBoxFormat2
->GetItemIfSet( RES_BOXATR_FORMAT
, false ) )
1932 if (pNumFormat
&& (pNumFormat
->GetValue() % SV_COUNTRY_LANGUAGE_OFFSET
) == 0)
1934 // only one text node?
1935 SwNodeIndex
aNodeIndex( *(pCell
->GetStartNode()), 1 );
1936 if( ( aNodeIndex
.GetNode().EndOfSectionIndex() -
1937 aNodeIndex
.GetNode().StartOfSectionIndex() ) == SwNodeOffset(2) )
1939 SwTextNode
* pTextNode
= aNodeIndex
.GetNode().GetTextNode();
1940 if( pTextNode
!= nullptr )
1942 // check text: does it look like some form of 0.0?
1943 const OUString
& rText
= pTextNode
->GetText();
1944 if( ( rText
.getLength() > 10 ) ||
1945 ( rText
.indexOf( '0' ) == -1 ) )
1947 bSuppressNumericContent
= true;
1952 bSuppressNumericContent
= true; // several nodes
1957 if( bSuppressNumericContent
)
1959 // suppress numeric content? Then reset number format!
1960 pBoxFormat2
->ResetFormatAttr( RES_BOXATR_FORMULA
);
1961 pBoxFormat2
->ResetFormatAttr( RES_BOXATR_FORMAT
);
1962 pBoxFormat2
->ResetFormatAttr( RES_BOXATR_VALUE
);
1966 // the normal case: set formula and value (if available)
1968 const OUString
& rFormula
= pCell
->GetFormula();
1969 if (!rFormula
.isEmpty())
1971 // formula cell: insert formula if valid
1972 SwTableBoxFormula
aFormulaItem( rFormula
);
1973 pBoxFormat2
->SetFormatAttr( aFormulaItem
);
1975 else if (!pCell
->HasValue() && pCell
->HasStringValue())
1977 // Check for another inconsistency:
1978 // No value but a non-textual format, i.e. a number format
1979 // Solution: the number format will be removed,
1980 // the cell gets the default text format.
1981 if( const SwTableBoxNumFormat
* pNumFormat
= m_pBoxFormat
->GetItemIfSet( RES_BOXATR_FORMAT
, false ) )
1983 const SwDoc
* pDoc
= m_pBoxFormat
->GetDoc();
1984 const SvNumberFormatter
* pNumberFormatter
= pDoc
?
1985 pDoc
->GetNumberFormatter() : nullptr;
1986 if( pNumFormat
!= nullptr && pNumberFormatter
&&
1987 !pNumberFormatter
->GetEntry( pNumFormat
->GetValue() )->IsTextFormat() )
1988 m_pBoxFormat
->ResetFormatAttr( RES_BOXATR_FORMAT
);
1991 // always insert value, even if default
1992 if( pCell
->HasValue() )
1994 SwTableBoxValue
aValueItem( pCell
->GetValue() );
1995 pBoxFormat2
->SetFormatAttr( aValueItem
);
1999 // update cell content depend on the default language
2000 pBox
->ActualiseValueBox();
2003 // table cell protection
2004 if( pCell
->IsProtected() )
2006 SvxProtectItem
aProtectItem( RES_PROTECT
);
2007 aProtectItem
.SetContentProtect( true );
2008 pBoxFormat2
->SetFormatAttr( aProtectItem
);
2011 // restore old modify-lock state
2012 if (! bModifyLocked
)
2013 pBoxFormat2
->UnlockModify();
2015 pBoxFormat2
->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable
, nColWidth
) );
2020 SwTableLine
*SwXMLTableContext::MakeTableLine( SwTableBox
*pUpper
,
2022 sal_uInt32 nLeftCol
,
2023 sal_uInt32 nBottomRow
,
2024 sal_uInt32 nRightCol
)
2026 //FIXME: here would be a great place to handle XmlId for row
2028 if( !pUpper
&& 0UL==nTopRow
)
2030 pLine
= m_pTableNode
->GetTable().GetTabLines()[0U];
2034 pLine
= new SwTableLine( m_pLineFormat
, 0, pUpper
);
2037 // TODO: Share formats!
2038 SwFrameFormat
*pFrameFormat
= pLine
->ClaimFrameFormat();
2039 SwFormatFillOrder
aFillOrder( pFrameFormat
->GetFillOrder() );
2040 pFrameFormat
->ResetAllFormatAttr(); // #i73790# - method renamed
2041 pFrameFormat
->SetFormatAttr( aFillOrder
);
2043 const SfxItemSet
*pAutoItemSet
= nullptr;
2044 const OUString
& rStyleName
= (*m_pRows
)[nTopRow
]->GetStyleName();
2045 if( 1 == (nBottomRow
- nTopRow
) &&
2046 !rStyleName
.isEmpty() &&
2047 GetSwImport().FindAutomaticStyle(
2048 XmlStyleFamily::TABLE_ROW
, rStyleName
, &pAutoItemSet
) )
2051 pFrameFormat
->SetFormatAttr( *pAutoItemSet
);
2054 SwTableBoxes
& rBoxes
= pLine
->GetTabBoxes();
2056 sal_uInt32 nStartCol
= nLeftCol
;
2057 while( nStartCol
< nRightCol
)
2059 for( sal_uInt32 nRow
=nTopRow
; nRow
<nBottomRow
; nRow
++ )
2060 (*m_pRows
)[nRow
]->SetSplitable( true );
2062 sal_uInt32 nCol
= nStartCol
;
2063 sal_uInt32 nSplitCol
= nRightCol
;
2064 bool bSplitted
= false;
2067 OSL_ENSURE( nCol
< nRightCol
, "Ran too far" );
2069 // Can be split after current HTML table column?
2070 // If yes, can the created region still be split to
2071 // rows if the next column is added to it?
2073 bool bHoriSplitMayContinue
= false;
2074 bool bHoriSplitPossible
= false;
2076 if ( m_bHasSubTables
)
2078 // Convert row spans if the table has subtables:
2079 for( sal_uInt32 nRow
=nTopRow
; nRow
<nBottomRow
; nRow
++ )
2081 SwXMLTableCell_Impl
*pCell
= GetCell(nRow
,nCol
);
2082 // Could the table fragment be split horizontally behind
2083 // the current line?
2084 bool bHoriSplit
= (*m_pRows
)[nRow
]->IsSplitable() &&
2085 nRow
+1 < nBottomRow
&&
2086 1 == pCell
->GetRowSpan();
2087 (*m_pRows
)[nRow
]->SetSplitable( bHoriSplit
);
2089 // Could the table fragment be split vertically behind the
2090 // current column (uptp the current line?
2091 bSplit
&= ( 1 == pCell
->GetColSpan() );
2094 bHoriSplitPossible
|= bHoriSplit
;
2096 // Could the current table fragment be split
2097 // horizontally behind the next column, too?
2098 bHoriSplit
&= (nCol
+1 < nRightCol
&&
2099 1 == GetCell(nRow
,nCol
+1)->GetRowSpan());
2100 bHoriSplitMayContinue
|= bHoriSplit
;
2106 // No subtables: we use the new table model.
2107 SwXMLTableCell_Impl
*pCell
= GetCell(nTopRow
,nCol
);
2109 // #i95726# - some fault tolerance
2110 if ( pCell
== nullptr )
2112 OSL_FAIL( "table seems to be corrupt." );
2116 // Could the table fragment be split vertically behind the
2117 // current column (uptp the current line?
2118 bSplit
= 1 == pCell
->GetColSpan();
2121 #if OSL_DEBUG_LEVEL > 0
2122 if( nCol
== nRightCol
-1 )
2124 OSL_ENSURE( bSplit
, "Split-Flag wrong" );
2125 if ( m_bHasSubTables
)
2127 OSL_ENSURE( !bHoriSplitMayContinue
,
2128 "HoriSplitMayContinue-Flag wrong" );
2129 SwXMLTableCell_Impl
*pTmpCell
= GetCell( nTopRow
, nStartCol
);
2130 OSL_ENSURE( pTmpCell
->GetRowSpan() != (nBottomRow
-nTopRow
) ||
2131 !bHoriSplitPossible
, "HoriSplitPossible-Flag wrong" );
2136 OSL_ENSURE( !m_bHasSubTables
|| !bHoriSplitMayContinue
|| bHoriSplitPossible
,
2137 "bHoriSplitMayContinue, but not bHoriSplitPossible" );
2141 SwTableBox
* pBox
= nullptr;
2142 SwXMLTableCell_Impl
*pCell
= GetCell( nTopRow
, nStartCol
);
2143 // #i95726# - some fault tolerance
2144 if( ( !m_bHasSubTables
|| ( pCell
->GetRowSpan() == (nBottomRow
-nTopRow
) ) ) &&
2145 pCell
->GetColSpan() == (nCol
+1-nStartCol
) &&
2146 ( pCell
->GetStartNode() || pCell
->GetSubTable() ) )
2148 // insert new empty cell for covered cells:
2149 sal_Int32 nBoxRowSpan
= 1;
2150 if ( !m_bHasSubTables
)
2152 nBoxRowSpan
= pCell
->GetRowSpan();
2153 if ( pCell
->IsCovered() )
2155 nBoxRowSpan
= -1 * nBoxRowSpan
;
2156 ReplaceWithEmptyCell( nTopRow
, nStartCol
, false );
2160 // The remaining box neither contains lines nor rows (i.e.
2162 nSplitCol
= nCol
+ 1;
2164 pBox
= MakeTableBox( pLine
, pCell
, nStartCol
, nSplitCol
);
2166 if ( 1 != nBoxRowSpan
)
2167 pBox
->setRowSpan( nBoxRowSpan
);
2171 else if( m_bHasSubTables
&& bHoriSplitPossible
&& bHoriSplitMayContinue
)
2173 // The table fragment could be split behind the current
2174 // column, and the remaining fragment could be divided
2175 // into lines. Anyway, it could be that this applies to
2176 // the next column, too. That for, we check the next
2177 // column but remember the current one as a good place to
2179 nSplitCol
= nCol
+ 1;
2181 else if ( m_bHasSubTables
)
2183 // If the table resulting table fragment could be divided
2184 // into lines if splitting behind the current column, but
2185 // this doesn't apply for thr next column, we split begind
2186 // the current column. This applies for the last column,
2188 // If the resulting box cannot be split into rows,
2189 // the split at the last split position we remembered.
2190 if( bHoriSplitPossible
|| nSplitCol
> nCol
+1 )
2192 OSL_ENSURE( !bHoriSplitMayContinue
,
2193 "bHoriSplitMayContinue==true" );
2194 OSL_ENSURE( bHoriSplitPossible
|| nSplitCol
== nRightCol
,
2195 "bHoriSplitPossible flag should be set" );
2197 nSplitCol
= nCol
+ 1;
2200 pBox
= MakeTableBox( pLine
, nTopRow
, nStartCol
,
2201 nBottomRow
, nSplitCol
);
2205 OSL_ENSURE( m_bHasSubTables
|| pBox
, "Colspan trouble" );
2208 rBoxes
.push_back( pBox
);
2212 nStartCol
= nSplitCol
;
2218 void SwXMLTableContext::MakeTable_( SwTableBox
*pBox
)
2220 // fix column widths
2221 std::vector
<ColumnWidthInfo
>::iterator colIter
;
2222 sal_uInt32 nCols
= GetColumnCount();
2224 // If there are empty rows (because of some row span of previous rows)
2225 // the have to be deleted. The previous rows have to be truncated.
2227 if (m_pRows
->size() > m_nCurRow
)
2229 SwXMLTableRow_Impl
*pPrevRow
= (*m_pRows
)[m_nCurRow
- 1U].get();
2230 const SwXMLTableCell_Impl
*pCell
;
2231 for( size_t i
= 0; i
< m_aColumnWidths
.size(); ++i
)
2233 pCell
= pPrevRow
->GetCell(i
);
2234 if( pCell
->GetRowSpan() > 1 )
2236 FixRowSpan( m_nCurRow
-1, i
, 1UL );
2239 for (size_t i
= m_pRows
->size() - 1; i
>= m_nCurRow
; --i
)
2240 m_pRows
->pop_back();
2243 if (m_pRows
->empty())
2245 InsertCell( "", 1U, nCols
, InsertTableSection() );
2248 // TODO: Do we have to keep both values, the relative and the absolute
2250 sal_Int32 nAbsWidth
= 0;
2251 sal_Int32 nMinAbsColWidth
= 0;
2252 sal_Int32 nRelWidth
= 0;
2253 sal_Int32 nMinRelColWidth
= 0;
2254 sal_uInt32 nRelCols
= 0;
2255 for( const auto& rCol
: m_aColumnWidths
)
2257 if( rCol
.isRelative
)
2259 nRelWidth
+= rCol
.width
;
2260 if( 0 == nMinRelColWidth
|| rCol
.width
< nMinRelColWidth
)
2261 nMinRelColWidth
= rCol
.width
;
2266 nAbsWidth
+= rCol
.width
;
2267 if( 0 == nMinAbsColWidth
|| rCol
.width
< nMinAbsColWidth
)
2268 nMinAbsColWidth
= rCol
.width
;
2271 sal_uInt32 nAbsCols
= nCols
- nRelCols
;
2275 // If there a columns that have an absolute width, we have to
2276 // calculate a relative one for them.
2279 // All column that have absolute widths get relative widths;
2280 // these widths relate to each over like the original absolute
2281 // widths. The smallest column gets a width that has the same
2282 // value as the smallest column that has a relative width
2284 if( 0 == nMinRelColWidth
)
2285 nMinRelColWidth
= nMinAbsColWidth
;
2287 for( auto& rCol
: m_aColumnWidths
)
2289 if( !rCol
.isRelative
)
2291 if (nMinAbsColWidth
== 0)
2292 throw o3tl::divide_by_zero();
2294 if (o3tl::checked_multiply
<sal_Int32
>(rCol
.width
, nMinRelColWidth
, nVal
))
2295 throw std::overflow_error("overflow in multiply");
2296 sal_Int32 nRelCol
= nVal
/ nMinAbsColWidth
;
2297 rCol
.width
= nRelCol
;
2298 rCol
.isRelative
= true;
2299 nRelWidth
+= nRelCol
;
2309 // This happens only for percentage values for the table itself.
2310 // In this case, the columns get the correct width even if
2311 // the sum of the relative widths is smaller than the available
2312 // width in TWIP. Therefore, we can use the relative width.
2313 m_nWidth
= std::min(nRelWidth
, MAX_WIDTH
);
2315 if( nRelWidth
!= m_nWidth
&& nRelWidth
&& nCols
)
2317 double n
= static_cast<double>(m_nWidth
) / static_cast<double>(nRelWidth
);
2319 for( colIter
= m_aColumnWidths
.begin(); colIter
< m_aColumnWidths
.end() - 1; ++colIter
)
2321 sal_Int32 nW
= static_cast<sal_Int32
>( colIter
->width
* n
);
2322 colIter
->width
= o3tl::narrowing
<sal_uInt16
>(nW
);
2325 m_aColumnWidths
.back().width
= (m_nWidth
-nRelWidth
);
2330 // If there are columns that have relative widths, we have to
2331 // calculate an absolute widths for them.
2334 // The absolute space that is available for all columns with a
2336 sal_Int32 nAbsForRelWidth
=
2337 m_nWidth
> nAbsWidth
? m_nWidth
- nAbsWidth
: sal_Int32(0L);
2339 // The relative width that has to be distributed in addition to
2340 // equally widthed columns.
2341 sal_Int32 nExtraRel
= nRelWidth
- (nRelCols
* nMinRelColWidth
);
2343 // The absolute space that may be distributed in addition to
2344 // minimum widthed columns.
2345 sal_Int32 nMinAbs
= nRelCols
* MINLAY
;
2346 sal_Int32 nExtraAbs
=
2347 nAbsForRelWidth
> nMinAbs
? nAbsForRelWidth
- nMinAbs
: sal_Int32(0L);
2349 bool bMin
= false; // Do all columns get the minimum width?
2350 bool bMinExtra
= false; // Do all columns get the minimum width plus
2351 // some extra space?
2353 if( nAbsForRelWidth
<= nMinAbs
)
2355 // If there is not enough space left for all columns to
2356 // get the minimum width, they get the minimum width, anyway.
2357 nAbsForRelWidth
= nMinAbs
;
2360 else if( nAbsForRelWidth
<= (nRelWidth
* MINLAY
) /
2363 // If there is enough space for all columns to get the
2364 // minimum width, but not to get a width that takes the
2365 // relative width into account, each column gets the minimum
2366 // width plus some extra space that is based on the additional
2367 // space that is available.
2370 // Otherwise, if there is enough space for every column, every
2371 // column gets this space.
2373 for( auto& rCol
: m_aColumnWidths
)
2375 if( rCol
.isRelative
)
2380 // The last column that has a relative width gets
2381 // all absolute space that is left.
2382 nAbsCol
= nAbsForRelWidth
;
2390 else if( bMinExtra
)
2392 sal_Int32 nExtraRelCol
= rCol
.width
- nMinRelColWidth
;
2393 nAbsCol
= MINLAY
+ (nExtraRelCol
* nExtraAbs
) /
2398 nAbsCol
= ( rCol
.width
* nAbsForRelWidth
) / nRelWidth
;
2401 rCol
.width
= nAbsCol
;
2402 rCol
.isRelative
= false;
2403 nAbsForRelWidth
-= nAbsCol
;
2404 nAbsWidth
+= nAbsCol
;
2412 if( nCols
&& nAbsWidth
)
2414 if( nAbsWidth
< m_nWidth
)
2416 // If the table's width is larger than the sum of the absolute
2417 // column widths, every column get some extra width.
2418 sal_Int32 nExtraAbs
= m_nWidth
- nAbsWidth
;
2419 sal_Int32 nAbsLastCol
= m_aColumnWidths
.back().width
+ nExtraAbs
;
2420 for( colIter
= m_aColumnWidths
.begin(); colIter
< m_aColumnWidths
.end()-1; ++colIter
)
2422 sal_Int32 nAbsCol
= colIter
->width
;
2423 sal_Int32 nExtraAbsCol
= (nAbsCol
* nExtraAbs
) /
2425 nAbsCol
+= nExtraAbsCol
;
2426 colIter
->width
= nAbsCol
;
2427 nAbsLastCol
-= nExtraAbsCol
;
2429 m_aColumnWidths
.back().width
= nAbsLastCol
;
2431 else if( nAbsWidth
> m_nWidth
)
2433 // If the table's width is smaller than the sum of the absolute
2434 // column widths, every column needs to shrink.
2435 // Every column gets the minimum width plus some extra width.
2436 sal_Int32 nExtraAbs
= m_nWidth
- (nCols
* MINLAY
);
2437 sal_Int32 nAbsLastCol
= MINLAY
+ nExtraAbs
;
2438 for( colIter
= m_aColumnWidths
.begin(); colIter
< m_aColumnWidths
.end()-1; ++colIter
)
2440 sal_Int32 nAbsCol
= colIter
->width
;
2441 sal_Int32 nExtraAbsCol
= (nAbsCol
* nExtraAbs
) /
2443 nAbsCol
= MINLAY
+ nExtraAbsCol
;
2444 colIter
->width
= nAbsCol
;
2445 nAbsLastCol
-= nExtraAbsCol
;
2447 m_aColumnWidths
.back().width
= nAbsLastCol
;
2452 SwTableLines
& rLines
=
2453 pBox
? pBox
->GetTabLines()
2454 : m_pTableNode
->GetTable().GetTabLines();
2456 sal_uInt32 nStartRow
= 0;
2457 sal_uInt32 nRows
= m_pRows
->size();
2458 for(sal_uInt32 i
=0; i
<nRows
; ++i
)
2460 // Could we split the table behind the current line?
2462 if ( m_bHasSubTables
)
2464 SwXMLTableRow_Impl
*pRow
= (*m_pRows
)[i
].get();
2465 for( sal_uInt32 j
=0; j
<nCols
; j
++ )
2467 bSplit
= ( 1 == pRow
->GetCell(j
)->GetRowSpan() );
2475 SwTableLine
*pLine
=
2476 MakeTableLine( pBox
, nStartRow
, 0UL, i
+1, nCols
);
2477 if( pBox
|| nStartRow
>0 )
2478 rLines
.push_back( pLine
);
2484 void SwXMLTableContext::MakeTable()
2486 // this method will modify the document directly -> lock SolarMutex
2487 // This will call all other MakeTable*(..) methods, so
2488 // those don't need to be locked separately.
2489 SolarMutexGuard aGuard
;
2491 // #i97274# handle invalid tables
2492 if (!m_pRows
|| m_pRows
->empty() || !GetColumnCount())
2494 OSL_FAIL("invalid table: no cells; deleting...");
2495 m_pTableNode
->GetDoc().getIDocumentContentOperations().DeleteSection( m_pTableNode
);
2496 m_pTableNode
= nullptr;
2498 m_bOwnsBox1
= false;
2499 m_pSttNd1
= nullptr;
2503 SwXMLImport
& rSwImport
= GetSwImport();
2505 SwFrameFormat
*pFrameFormat
= m_pTableNode
->GetTable().GetFrameFormat();
2507 sal_Int16 eHoriOrient
= text::HoriOrientation::FULL
;
2508 bool bSetHoriOrient
= false;
2510 sal_uInt8 nPercentWidth
= 0U;
2512 OUString sStyleName
;
2513 SwStyleNameMapper::FillUIName( m_aTemplateName
, sStyleName
, SwGetPoolIdFromName::TabStyle
);
2514 m_pTableNode
->GetTable().SetTableStyleName( sStyleName
);
2515 m_pTableNode
->GetTable().SetRowsToRepeat( m_nHeaderRows
);
2516 m_pTableNode
->GetTable().SetTableModel( !m_bHasSubTables
);
2518 const SfxItemSet
*pAutoItemSet
= nullptr;
2519 if( !m_aStyleName
.isEmpty() &&
2520 rSwImport
.FindAutomaticStyle(
2521 XmlStyleFamily::TABLE_TABLE
, m_aStyleName
, &pAutoItemSet
) &&
2524 const SvxLRSpaceItem
*pLRSpace
=
2525 pAutoItemSet
->GetItemIfSet( RES_LR_SPACE
, false );
2527 if( const SwFormatHoriOrient
* pItem
= pAutoItemSet
->GetItemIfSet( RES_HORI_ORIENT
, false ) )
2529 eHoriOrient
= pItem
->GetHoriOrient();
2530 switch( eHoriOrient
)
2532 case text::HoriOrientation::FULL
:
2535 eHoriOrient
= text::HoriOrientation::NONE
;
2536 bSetHoriOrient
= true;
2539 case text::HoriOrientation::LEFT
:
2542 eHoriOrient
= text::HoriOrientation::LEFT_AND_WIDTH
;
2543 bSetHoriOrient
= true;
2552 bSetHoriOrient
= true;
2555 const SwFormatFrameSize
*pSize
=
2556 pAutoItemSet
->GetItemIfSet( RES_FRM_SIZE
, false );
2558 switch( eHoriOrient
)
2560 case text::HoriOrientation::FULL
:
2561 case text::HoriOrientation::NONE
:
2562 // For text::HoriOrientation::NONE we would prefer to use the sum
2563 // of the relative column widths as reference width.
2564 // Unfortunately this works only if this sum interpreted as
2565 // twip value is larger than the space that is available.
2566 // We don't know that space, so we have to use MAX_WIDTH, too.
2567 // Even if a size is specified, it will be ignored!
2568 m_nWidth
= MAX_WIDTH
;
2573 if( pSize
->GetWidthPercent() )
2575 // The width will be set in MakeTable_
2576 nPercentWidth
= pSize
->GetWidthPercent();
2580 m_nWidth
= pSize
->GetWidth();
2581 sal_Int32
const min
= static_cast<sal_Int32
>(
2582 std::min
<sal_uInt32
>(GetColumnCount() * MINLAY
, MAX_WIDTH
));
2583 if( m_nWidth
< min
)
2587 else if( m_nWidth
> MAX_WIDTH
)
2589 m_nWidth
= MAX_WIDTH
;
2591 m_bRelWidth
= false;
2596 eHoriOrient
= text::HoriOrientation::LEFT_AND_WIDTH
== eHoriOrient
2597 ? text::HoriOrientation::NONE
: text::HoriOrientation::FULL
;
2598 bSetHoriOrient
= true;
2599 m_nWidth
= MAX_WIDTH
;
2604 pFrameFormat
->SetFormatAttr( *pAutoItemSet
);
2608 bSetHoriOrient
= true;
2609 m_nWidth
= MAX_WIDTH
;
2612 SwTableLine
*pLine1
= m_pTableNode
->GetTable().GetTabLines()[0U];
2613 assert(m_pBox1
== pLine1
->GetTabBoxes()[0] && !m_bOwnsBox1
&& "Why is box 1 change?");
2614 m_pBox1
->m_pStartNode
= m_pSttNd1
;
2615 pLine1
->GetTabBoxes().erase( pLine1
->GetTabBoxes().begin() );
2618 m_pLineFormat
= static_cast<SwTableLineFormat
*>(pLine1
->GetFrameFormat());
2619 m_pBoxFormat
= static_cast<SwTableBoxFormat
*>(m_pBox1
->GetFrameFormat());
2623 if( bSetHoriOrient
)
2624 pFrameFormat
->SetFormatAttr( SwFormatHoriOrient( 0, eHoriOrient
) );
2626 // This must be after the call to MakeTable_, because nWidth might be
2628 pFrameFormat
->LockModify();
2629 SwFormatFrameSize
aSize( SwFrameSize::Variable
, m_nWidth
);
2630 aSize
.SetWidthPercent( nPercentWidth
);
2631 pFrameFormat
->SetFormatAttr( aSize
);
2632 pFrameFormat
->UnlockModify();
2634 for (std::unique_ptr
<SwXMLTableRow_Impl
> & rRow
: *m_pRows
)
2637 // now that table is complete, change into DDE table (if appropriate)
2638 if (m_xDDESource
.is())
2640 // change existing table into DDE table:
2641 // 1) Get DDE field type (get data from dde-source context),
2642 SwDDEFieldType
* pFieldType
= lcl_GetDDEFieldType( m_xDDESource
.get(),
2645 // 2) release the DDE source context,
2646 m_xDDESource
.set(nullptr);
2648 // 3) create new DDE table, and
2649 std::unique_ptr
<SwDDETable
> pDDETable( new SwDDETable( m_pTableNode
->GetTable(),
2650 pFieldType
, false ) );
2652 // 4) set new (DDE)table at node.
2653 m_pTableNode
->SetNewTable(std::move(pDDETable
), false);
2656 // ??? this is always false: root frame is only created in SwViewShell::Init
2657 if( m_pTableNode
->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() )
2659 m_pTableNode
->DelFrames();
2660 m_pTableNode
->MakeOwnFrames();
2664 void SwXMLTableContext::MakeTable( SwTableBox
*pBox
, sal_Int32 nW
)
2666 //FIXME: here would be a great place to handle XmlId for subtable
2667 m_pLineFormat
= GetParentTable()->m_pLineFormat
;
2668 m_pBoxFormat
= GetParentTable()->m_pBoxFormat
;
2670 m_bRelWidth
= GetParentTable()->m_bRelWidth
;
2674 for (std::unique_ptr
<SwXMLTableRow_Impl
> & rpRow
: *m_pRows
)
2676 // i#113600, to break the cyclic reference to SwXMLTableContext object
2681 const SwStartNode
*SwXMLTableContext::InsertTableSection(
2682 const SwStartNode
*const pPrevSttNd
,
2683 OUString
const*const pStringValueStyleName
)
2685 // The topmost table is the only table that maintains the two members
2686 // pBox1 and bFirstSection.
2687 if( m_xParentTable
.is() )
2688 return static_cast<SwXMLTableContext
*>(m_xParentTable
.get())
2689 ->InsertTableSection(pPrevSttNd
, pStringValueStyleName
);
2691 const SwStartNode
*pStNd
;
2693 if( m_bFirstSection
)
2695 Reference
<XInterface
> xCursorTunnel( GetImport().GetTextImport()->GetCursor(),
2697 OSL_ENSURE( xCursorTunnel
.is(), "missing XUnoTunnel for Cursor" );
2698 OTextCursorHelper
*pTextCursor
= dynamic_cast<OTextCursorHelper
*>(xCursorTunnel
.get());
2699 assert(pTextCursor
&& "SwXTextCursor missing");
2701 // The Cursor already is in the first section
2702 pStNd
= pTextCursor
->GetPaM()->GetPointNode().FindTableBoxStartNode();
2703 m_bFirstSection
= false;
2704 GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
2705 GetImport().GetTextImport()->GetCursor(), "Standard", true );
2709 SwDoc
* pDoc
= GetSwImport().getDoc();
2710 const SwEndNode
*pEndNd
= pPrevSttNd
? pPrevSttNd
->EndOfSectionNode()
2711 : m_pTableNode
->EndOfSectionNode();
2712 // #i78921# - make code robust
2713 OSL_ENSURE( pDoc
, "<SwXMLTableContext::InsertTableSection(..)> - no <pDoc> at <SwXTextCursor> instance - <SwXTextCurosr> doesn't seem to be registered at a <SwUnoCursor> instance." );
2716 pDoc
= &const_cast<SwDoc
&>(pEndNd
->GetDoc());
2718 SwNodeOffset
nOffset(pPrevSttNd
? 1 : 0);
2719 SwNodeIndex
aIdx( *pEndNd
, nOffset
);
2720 SwTextFormatColl
*pColl
=
2721 pDoc
->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
, false );
2722 pStNd
= pDoc
->GetNodes().MakeTextSection( aIdx
.GetNode(), SwTableBoxStartNode
,
2724 // Consider the case that a table is defined without a row.
2725 if( !pPrevSttNd
&& m_pBox1
!= nullptr )
2727 m_pBox1
->m_pStartNode
= pStNd
;
2728 SwContentNode
*pCNd
= pDoc
->GetNodes()[ pStNd
->GetIndex() + 1 ]
2730 SwFrameFormat
*const pTableFormat
= m_pTableNode
->GetTable().GetFrameFormat();
2731 rtl::Reference
<SwXCell
> xParent
= SwXCell::CreateXCell( pTableFormat
, m_pBox1
);
2732 DBG_TESTSOLARMUTEX();
2733 SwPaM
aPam(*pCNd
, *pCNd
);
2734 rtl::Reference
<SwXTextCursor
> xTextCursor
=
2735 new SwXTextCursor(*pDoc
, xParent
, CursorType::TableText
,
2736 *aPam
.GetPoint(), aPam
.GetMark());
2737 GetImport().GetTextImport()->SetCursor( static_cast<XWordCursor
*>(xTextCursor
.get()) );
2741 if (pStringValueStyleName
)
2742 { // fdo#62147: apply style to paragraph on string-value cell
2743 GetImport().GetTextImport()->SetStyleAndAttrs( GetImport(),
2744 GetImport().GetTextImport()->GetCursor(), *pStringValueStyleName
,
2745 true, false, -1, false); // parameters same as sCellParaStyleName
2751 void SwXMLTableContext::endFastElement(sal_Int32
)
2753 if( IsValid() && !m_xParentTable
.is() )
2756 GetImport().GetTextImport()->SetCursor( m_xOldCursor
);
2760 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */