tdf#150927: properly handle nesting in tables
[LibreOffice.git] / sw / source / filter / xml / xmltble.cxx
blob2bb7a564a48a770d913f25ad16585e91bb6955cf
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 <sal/config.h>
22 #include <string_view>
24 #include <com/sun/star/text/XTextTable.hpp>
25 #include <com/sun/star/text/XTextSection.hpp>
27 #include <hintids.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <xmloff/xmlnamespace.hxx>
30 #include <xmloff/xmltoken.hxx>
31 #include <xmloff/xmluconv.hxx>
32 #include <xmloff/numehelp.hxx>
33 #include <editeng/brushitem.hxx>
34 #include <editeng/boxitem.hxx>
35 #include <editeng/prntitem.hxx>
36 #include <editeng/xmlcnitm.hxx>
37 #include <fmtrowsplt.hxx>
38 #include <editeng/frmdiritem.hxx>
39 #include <swtable.hxx>
40 #include <doc.hxx>
41 #include <frmfmt.hxx>
42 #include <wrtswtbl.hxx>
43 #include <fmtfsize.hxx>
44 #include <fmtornt.hxx>
45 #include <cellatr.hxx>
46 #include <ddefld.hxx>
47 #include <swddetbl.hxx>
48 #include <xmloff/namespacemap.hxx>
49 #include <sfx2/linkmgr.hxx>
50 #include <unotbl.hxx>
51 #include "xmltexte.hxx"
52 #include "xmlexp.hxx"
53 #include <o3tl/any.hxx>
54 #include <o3tl/sorted_vector.hxx>
55 #include <textboxhelper.hxx>
56 #include <SwStyleNameMapper.hxx>
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::text;
61 using namespace ::com::sun::star::beans;
62 using namespace ::com::sun::star::lang;
63 using namespace ::com::sun::star::container;
64 using namespace ::xmloff::token;
65 using table::XCell;
66 using std::vector;
67 using std::advance;
70 class SwXMLTableColumn_Impl : public SwWriteTableCol
72 OUString m_sStyleName;
73 sal_uInt32 m_nRelWidth;
75 public:
77 explicit SwXMLTableColumn_Impl(sal_uInt32 nPosition)
78 : SwWriteTableCol(nPosition)
79 , m_nRelWidth(0)
80 {};
82 void SetStyleName( const OUString& rName ) { m_sStyleName = rName; }
83 const OUString& GetStyleName() const { return m_sStyleName; }
85 void SetRelWidth( sal_uInt32 nSet ) { m_nRelWidth = nSet; }
86 sal_uInt32 GetRelWidth() const { return m_nRelWidth; }
89 namespace {
91 struct SwXMLTableColumnCmpWidth_Impl
93 bool operator()( SwXMLTableColumn_Impl* const& lhs, SwXMLTableColumn_Impl* const& rhs ) const
95 sal_Int32 n = static_cast<sal_Int32>(lhs->GetWidthOpt()) - static_cast<sal_Int32>(rhs->GetWidthOpt());
96 if( !n )
97 n = static_cast<sal_Int32>(lhs->GetRelWidth()) - static_cast<sal_Int32>(rhs->GetRelWidth());
98 return n < 0;
102 class SwXMLTableColumns_Impl : public o3tl::sorted_vector<std::unique_ptr<SwXMLTableColumn_Impl>, o3tl::less_uniqueptr_to<SwXMLTableColumn_Impl> > {
107 class SwXMLTableColumnsSortByWidth_Impl : public o3tl::sorted_vector<SwXMLTableColumn_Impl*, SwXMLTableColumnCmpWidth_Impl> {};
109 class SwXMLTableLines_Impl
111 SwXMLTableColumns_Impl m_aCols;
112 const SwTableLines *m_pLines;
113 sal_uInt32 m_nWidth;
115 public:
117 explicit SwXMLTableLines_Impl( const SwTableLines& rLines );
119 sal_uInt32 GetWidth() const { return m_nWidth; }
120 const SwTableLines *GetLines() const { return m_pLines; }
122 const SwXMLTableColumns_Impl& GetColumns() const { return m_aCols; }
125 SwXMLTableLines_Impl::SwXMLTableLines_Impl( const SwTableLines& rLines ) :
126 m_pLines( &rLines ),
127 m_nWidth( 0 )
129 #if OSL_DEBUG_LEVEL > 0
130 sal_uInt32 nEndCPos = 0U;
131 #endif
132 const size_t nLines = rLines.size();
133 for( size_t nLine=0U; nLine<nLines; ++nLine )
135 const SwTableLine *pLine = rLines[nLine];
136 const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
137 const size_t nBoxes = rBoxes.size();
139 sal_uInt32 nCPos = 0U;
140 for( size_t nBox=0U; nBox<nBoxes; ++nBox )
142 const SwTableBox *pBox = rBoxes[nBox];
144 if( nBox < nBoxes-1U || m_nWidth==0 )
146 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
147 std::unique_ptr<SwXMLTableColumn_Impl> pCol(
148 new SwXMLTableColumn_Impl( nCPos ));
150 m_aCols.insert( std::move(pCol) );
152 if( nBox==nBoxes-1U )
154 OSL_ENSURE( nLine==0U && m_nWidth==0,
155 "parent width will be lost" );
156 m_nWidth = nCPos;
159 else
161 #if OSL_DEBUG_LEVEL > 0
162 sal_uInt32 nCheckPos =
163 nCPos + SwWriteTable::GetBoxWidth( pBox );
164 if( !nEndCPos )
166 nEndCPos = nCheckPos;
168 #endif
169 nCPos = m_nWidth;
170 #if OSL_DEBUG_LEVEL > 0
171 SwXMLTableColumn_Impl aCol( m_nWidth );
172 OSL_ENSURE( m_aCols.find(&aCol) != m_aCols.end(), "couldn't find last column" );
173 OSL_ENSURE( SwXMLTableColumn_Impl(nCheckPos) ==
174 SwXMLTableColumn_Impl(nCPos),
175 "rows have different total widths" );
176 #endif
182 typedef vector< SwFrameFormat* > SwXMLFrameFormats_Impl;
184 class SwXMLTableFrameFormatsSort_Impl
186 private:
187 SwXMLFrameFormats_Impl m_aFormatList;
188 SwXMLTextParagraphExport::FormatMap & m_rFormatMap;
190 public:
191 SwXMLTableFrameFormatsSort_Impl(SwXMLTextParagraphExport::FormatMap & rFormatMap)
192 : m_rFormatMap(rFormatMap)
194 ::std::optional<OUString> AddRow(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix, sal_uInt32 nLine );
195 ::std::optional<OUString> AddCell(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix,
196 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop );
199 ::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddRow(SwFrameFormat& rFrameFormat,
200 std::u16string_view rNamePrefix,
201 sal_uInt32 nLine )
203 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
205 const SwFormatFrameSize *pFrameSize = rItemSet.GetItemIfSet( RES_FRM_SIZE, false );
206 const SwFormatRowSplit* pRowSplit = rItemSet.GetItemIfSet( RES_ROW_SPLIT, false );
207 const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
208 const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
210 // empty styles have not to be exported
211 if( !pFrameSize && !pBrush && !pRowSplit && !pHasTextChangesOnly )
213 m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>()); // empty just to enable assert
214 return {};
217 // order is: -/brush, size/-, size/brush
218 SwXMLFrameFormats_Impl::iterator i;
219 for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
221 const SwFormatFrameSize *pTestFrameSize = nullptr;
222 const SwFormatRowSplit* pTestRowSplit = nullptr;
223 const SvxBrushItem *pTestBrush = nullptr;
224 const SvxPrintItem *pTestHasTextChangesOnly = nullptr;
225 const SwFrameFormat *pTestFormat = *i;
226 const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
227 if( const SwFormatFrameSize* pItem = rTestSet.GetItemIfSet( RES_FRM_SIZE, false ) )
229 if( !pFrameSize )
230 break;
232 pTestFrameSize = pItem;
234 else
236 if( pFrameSize )
237 continue;
240 if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false) )
242 if( !pBrush )
243 break;
245 pTestBrush = pItem;
247 else
249 if( pBrush )
250 continue;
253 if( const SwFormatRowSplit* pItem = rTestSet.GetItemIfSet( RES_ROW_SPLIT, false ) )
255 if( !pRowSplit )
256 break;
258 pTestRowSplit = pItem;
260 else
262 if( pRowSplit )
263 continue;
266 if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) )
268 if( !pHasTextChangesOnly )
269 break;
271 pTestHasTextChangesOnly = pItem;
273 else
275 if( pHasTextChangesOnly )
276 continue;
279 if( pFrameSize &&
280 ( pFrameSize->GetHeightSizeType() != pTestFrameSize->GetHeightSizeType() ||
281 pFrameSize->GetHeight() != pTestFrameSize->GetHeight() ) )
282 continue;
284 if( pBrush && (*pBrush != *pTestBrush) )
285 continue;
287 if( pRowSplit && (!pRowSplit->GetValue() != !pTestRowSplit->GetValue()) )
288 continue;
290 if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) )
291 continue;
293 // found!
294 auto const oName(m_rFormatMap.find(pTestFormat)->second);
295 assert(oName);
296 m_rFormatMap.emplace(&rFrameFormat, oName);
297 return {};
301 OUString const name(OUString::Concat(rNamePrefix) + "." + OUString::number(nLine+1));
302 m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>(name));
303 if ( i != m_aFormatList.end() ) ++i;
304 m_aFormatList.insert( i, &rFrameFormat );
305 return ::std::optional<OUString>(name);
309 static OUString lcl_xmltble_appendBoxPrefix(std::u16string_view rNamePrefix,
310 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
312 if( bTop )
314 OUString sTmp;
315 sw_GetTableBoxColStr( o3tl::narrowing<sal_uInt16>(nCol), sTmp );
316 return OUString::Concat(rNamePrefix) + "." + sTmp + OUString::number(nRow + 1);
318 return OUString::Concat(rNamePrefix)
319 + "." + OUString::number(nCol + 1)
320 + "." + OUString::number(nRow + 1);
323 ::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddCell(SwFrameFormat& rFrameFormat,
324 std::u16string_view rNamePrefix,
325 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
327 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
328 const SwFormatVertOrient *pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false );
329 const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
330 const SvxBoxItem *pBox = rItemSet.GetItemIfSet( RES_BOX, false );
331 const SwTableBoxNumFormat *pNumFormat = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT,
332 false );
333 const SvxFrameDirectionItem *pFrameDir = rItemSet.GetItemIfSet( RES_FRAMEDIR,
334 false );
335 const SvXMLAttrContainerItem *pAttCnt = rItemSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
336 false );
338 // empty styles have not to be exported
339 if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt )
341 m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>()); // empty just to enable assert
342 return {};
345 // order is: -/-/-/num,
346 // -/-/box/-, -/-/box/num,
347 // -/brush/-/-, -/brush/-/num, -/brush/box/-, -/brush/box/num,
348 // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num,
349 // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-,
350 // vert/brush/box/num
351 SwXMLFrameFormats_Impl::iterator i;
352 for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
354 const SwFormatVertOrient *pTestVertOrient = nullptr;
355 const SvxBrushItem *pTestBrush = nullptr;
356 const SvxBoxItem *pTestBox = nullptr;
357 const SwTableBoxNumFormat *pTestNumFormat = nullptr;
358 const SvxFrameDirectionItem *pTestFrameDir = nullptr;
359 const SvXMLAttrContainerItem *pTestAttCnt = nullptr;
360 const SwFrameFormat* pTestFormat = *i;
361 const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
362 if( const SwFormatVertOrient* pItem = rTestSet.GetItemIfSet( RES_VERT_ORIENT, false ) )
364 if( !pVertOrient )
365 break;
367 pTestVertOrient = pItem;
369 else
371 if( pVertOrient )
372 continue;
375 if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false ) )
377 if( !pBrush )
378 break;
380 pTestBrush = pItem;
382 else
384 if( pBrush )
385 continue;
388 if( const SvxBoxItem* pItem = rTestSet.GetItemIfSet( RES_BOX, false ) )
390 if( !pBox )
391 break;
393 pTestBox = pItem;
395 else
397 if( pBox )
398 continue;
401 if ( const SwTableBoxNumFormat* pItem = rTestSet.GetItemIfSet( RES_BOXATR_FORMAT,
402 false ) )
404 if( !pNumFormat )
405 break;
407 pTestNumFormat = pItem;
409 else
411 if( pNumFormat )
412 continue;
416 if ( const SvxFrameDirectionItem* pItem = rTestSet.GetItemIfSet( RES_FRAMEDIR,
417 false ) )
419 if( !pFrameDir )
420 break;
422 pTestFrameDir = pItem;
424 else
426 if( pFrameDir )
427 continue;
431 if ( const SvXMLAttrContainerItem* pItem = rTestSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
432 false ) )
434 if( !pAttCnt )
435 break;
437 pTestAttCnt = pItem;
439 else
441 if ( pAttCnt )
442 continue;
446 if( pVertOrient &&
447 pVertOrient->GetVertOrient() != pTestVertOrient->GetVertOrient() )
448 continue;
450 if( pBrush && ( *pBrush != *pTestBrush ) )
451 continue;
453 if( pBox && ( *pBox != *pTestBox ) )
454 continue;
456 if( pNumFormat && pNumFormat->GetValue() != pTestNumFormat->GetValue() )
457 continue;
459 if( pFrameDir && pFrameDir->GetValue() != pTestFrameDir->GetValue() )
460 continue;
462 if( pAttCnt && ( *pAttCnt != *pTestAttCnt ) )
463 continue;
465 // found!
466 auto const oName(m_rFormatMap.find(pTestFormat)->second);
467 assert(oName);
468 m_rFormatMap.emplace(&rFrameFormat, oName);
469 return {};
473 OUString const name(lcl_xmltble_appendBoxPrefix(rNamePrefix, nCol, nRow, bTop));
474 m_rFormatMap.emplace(&rFrameFormat, ::std::optional<OUString>(name));
475 if ( i != m_aFormatList.end() ) ++i;
476 m_aFormatList.insert( i, &rFrameFormat );
477 return ::std::optional<OUString>(name);
481 class SwXMLTableInfo_Impl
483 const SwTable *m_pTable;
484 Reference<XTextSection> m_xBaseSection;
485 bool m_bBaseSectionValid;
486 sal_uInt32 m_nPrefix;
487 SwXMLTextParagraphExport::FormatMap const& m_rLineFormats;
488 SwXMLTextParagraphExport::FormatMap const& m_rBoxFormats;
490 public:
492 inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix,
493 SwXMLTextParagraphExport::FormatMap const& rLineFormats,
494 SwXMLTextParagraphExport::FormatMap const& rBoxFormats)
495 : m_pTable(pTable)
496 , m_bBaseSectionValid(false)
497 , m_nPrefix(nPrefix)
498 , m_rLineFormats(rLineFormats)
499 , m_rBoxFormats(rBoxFormats)
503 const SwTable *GetTable() const { return m_pTable; }
504 const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); }
506 bool IsBaseSectionValid() const { return m_bBaseSectionValid; }
507 const Reference<XTextSection>& GetBaseSection() const { return m_xBaseSection; }
508 inline void SetBaseSection( const Reference < XTextSection >& rBase );
509 /// The namespace (table or loext) that should be used for the elements.
510 sal_uInt16 GetPrefix() const { return m_nPrefix; }
511 SwXMLTextParagraphExport::FormatMap const& GetLineFormats() const { return m_rLineFormats; }
512 SwXMLTextParagraphExport::FormatMap const& GetBoxFormats() const { return m_rBoxFormats; }
515 inline void SwXMLTableInfo_Impl::SetBaseSection(
516 const Reference < XTextSection >& rBaseSection )
518 m_xBaseSection = rBaseSection;
519 m_bBaseSectionValid = true;
522 void SwXMLExport::ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol )
524 // <style:style ...>
525 CheckAttrList();
527 // style:name="..."
528 bool bEncoded = false;
529 AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
530 EncodeStyleName( rCol.GetStyleName(), &bEncoded ) );
531 if( bEncoded )
532 AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCol.GetStyleName() );
534 // style:family="table-column"
535 AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE_COLUMN );
538 SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, true,
539 true );
540 OUStringBuffer sValue;
541 if( rCol.GetWidthOpt() )
543 GetTwipUnitConverter().convertMeasureToXML( sValue,
544 rCol.GetWidthOpt() );
545 AddAttribute( XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH,
546 sValue.makeStringAndClear() );
548 if( rCol.GetRelWidth() )
550 sValue.append( static_cast<sal_Int32>(rCol.GetRelWidth()) );
551 sValue.append( '*' );
552 AddAttribute( XML_NAMESPACE_STYLE, XML_REL_COLUMN_WIDTH,
553 sValue.makeStringAndClear() );
557 SvXMLElementExport aElemExport( *this, XML_NAMESPACE_STYLE,
558 XML_TABLE_COLUMN_PROPERTIES,
559 true, true );
564 void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines,
565 sal_uInt32 nAbsWidth, sal_uInt32 nBaseWidth,
566 std::u16string_view rNamePrefix,
567 SwXMLTableColumnsSortByWidth_Impl& rExpCols,
568 SwXMLTableFrameFormatsSort_Impl& rExpRows,
569 SwXMLTableFrameFormatsSort_Impl& rExpCells,
570 SwXMLTableInfo_Impl& rTableInfo,
571 bool bTop )
573 // pass 1: calculate columns
574 SwXMLTableLines_Impl *pLines = new SwXMLTableLines_Impl( rLines );
575 if( !m_pTableLines )
576 m_pTableLines.reset(new SwXMLTableLinesCache_Impl);
578 m_pTableLines->push_back( pLines );
580 // pass 2: export column styles
582 const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
583 sal_uInt32 nCPos = 0U;
584 const size_t nColumns = rCols.size();
585 for( size_t nColumn=0U; nColumn<nColumns; ++nColumn )
587 SwXMLTableColumn_Impl *pColumn = rCols[nColumn].get();
589 sal_uInt32 nOldCPos = nCPos;
590 nCPos = pColumn->GetPos();
592 sal_uInt32 nWidth = nCPos - nOldCPos;
594 // If a base width is given, the table has either an automatic
595 // or margin alignment, or a percentage width. In either case,
596 // relative widths should be exported.
597 if( nBaseWidth )
599 pColumn->SetRelWidth( nWidth );
602 // If an absolute width is given, the table either has a fixed
603 // width, or the current width is known from the layout. In the
604 // later case, a base width is set in addition and must be used
605 // to "absolutize" the relative column width.
606 if( nAbsWidth )
608 sal_uInt32 nColAbsWidth = nWidth;
609 if( nBaseWidth )
611 nColAbsWidth *= nAbsWidth;
612 nColAbsWidth += (nBaseWidth/2UL);
613 nColAbsWidth /= nBaseWidth;
615 pColumn->SetWidthOpt( nColAbsWidth, false );
618 SwXMLTableColumnsSortByWidth_Impl::const_iterator it = rExpCols.find( pColumn );
619 if( it != rExpCols.end() )
621 pColumn->SetStyleName( (*it)->GetStyleName() );
623 else
625 if( bTop )
627 OUString sTmp;
628 sw_GetTableBoxColStr( nColumn, sTmp );
629 pColumn->SetStyleName( OUString::Concat(rNamePrefix) + "." + sTmp );
631 else
633 pColumn->SetStyleName(
634 OUString::Concat(rNamePrefix) + "." + OUString::number(nColumn + 1U) );
636 ExportTableColumnStyle( *pColumn );
637 rExpCols.insert( pColumn );
642 // pass 3: export line/rows
643 const size_t nLines = rLines.size();
644 for( size_t nLine=0U; nLine<nLines; ++nLine )
646 SwTableLine *pLine = rLines[nLine];
648 SwFrameFormat *pFrameFormat = pLine->GetFrameFormat();
649 if (auto oNew = rExpRows.AddRow(*pFrameFormat, rNamePrefix, nLine))
651 ExportFormat(*pFrameFormat, XML_TABLE_ROW, std::move(oNew));
654 const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
655 const size_t nBoxes = rBoxes.size();
657 sal_uInt32 nCPos = 0U;
658 size_t nCol = 0U;
659 for( size_t nBox=0U; nBox<nBoxes; nBox++ )
661 SwTableBox *pBox = rBoxes[nBox];
663 if( nBox < nBoxes-1U )
664 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
665 else
666 nCPos = pLines->GetWidth();
668 // and their index
669 const size_t nOldCol = nCol;
670 SwXMLTableColumn_Impl aCol( nCPos );
671 SwXMLTableColumns_Impl::const_iterator it = pLines->GetColumns().find( &aCol );
672 OSL_ENSURE( it != pLines->GetColumns().end(), "couldn't find column" );
673 nCol = it - pLines->GetColumns().begin();
675 const SwStartNode *pBoxSttNd = pBox->GetSttNd();
676 if( pBoxSttNd )
678 SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat();
679 if (auto oNew = rExpCells.AddCell(*pFrameFormat2, rNamePrefix, nOldCol, nLine,
680 bTop) )
682 ExportFormat(*pFrameFormat2, XML_TABLE_CELL, std::move(oNew));
685 Reference < XCell > xCell = SwXCell::CreateXCell(
686 const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
687 pBox,
688 const_cast<SwTable *>(rTableInfo.GetTable()) );
689 if (xCell.is())
691 Reference < XText > xText( xCell, UNO_QUERY );
692 if( !rTableInfo.IsBaseSectionValid() )
694 Reference<XPropertySet> xCellPropertySet( xCell,
695 UNO_QUERY );
696 Any aAny = xCellPropertySet->getPropertyValue("TextSection");
697 Reference < XTextSection > xTextSection;
698 aAny >>= xTextSection;
699 rTableInfo.SetBaseSection( xTextSection );
702 const bool bExportContent = bool(getExportFlags() & SvXMLExportFlags::CONTENT );
703 if ( !bExportContent )
705 // AUTOSTYLES - not needed anymore if we are currently exporting content.xml
706 GetTextParagraphExport()->collectTextAutoStyles(
707 xText, rTableInfo.GetBaseSection(), IsShowProgress() );
710 else {
711 OSL_FAIL("here should be a XCell");
714 else
716 ExportTableLinesAutoStyles( pBox->GetTabLines(),
717 nAbsWidth, nBaseWidth,
718 lcl_xmltble_appendBoxPrefix( rNamePrefix,
719 nOldCol, nLine, bTop ),
720 rExpCols, rExpRows, rExpCells,
721 rTableInfo );
724 nCol++;
729 void SwXMLExport::ExportTableAutoStyles(const SwTableNode& rTableNd)
731 auto & rFormats(static_cast<SwXMLTextParagraphExport *>(GetTextParagraphExport().get())->GetTableFormats());
732 auto const it(rFormats.find(&rTableNd));
733 assert(it != rFormats.end());
734 SwXMLTextParagraphExport::FormatMap & rRowFormats(it->second.first);
735 SwXMLTextParagraphExport::FormatMap & rBoxFormats(it->second.second);
736 const SwTable& rTable = rTableNd.GetTable();
737 const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
739 if( !pTableFormat )
740 return;
742 sal_Int16 eTabHoriOri = pTableFormat->GetHoriOrient().GetHoriOrient();
743 const SwFormatFrameSize& rFrameSize = pTableFormat->GetFrameSize();
745 sal_uInt32 nAbsWidth = rFrameSize.GetSize().Width();
746 sal_uInt32 nBaseWidth = 0;
747 sal_Int8 nPercentWidth = rFrameSize.GetWidthPercent();
749 bool bFixAbsWidth = nPercentWidth != 0 || /*text::*/HoriOrientation::NONE == eTabHoriOri
750 || /*text::*/HoriOrientation::FULL == eTabHoriOri;
751 if( bFixAbsWidth )
753 nBaseWidth = nAbsWidth;
754 nAbsWidth = pTableFormat->FindLayoutRect(true).Width();
755 if( !nAbsWidth )
757 // TODO?
760 ExportTableFormat( *pTableFormat, nAbsWidth );
762 SwXMLTableColumnsSortByWidth_Impl aExpCols;
763 SwXMLTableFrameFormatsSort_Impl aExpRows(rRowFormats);
764 SwXMLTableFrameFormatsSort_Impl aExpCells(rBoxFormats);
765 SwXMLTableInfo_Impl aTableInfo(&rTable, XML_NAMESPACE_TABLE, rRowFormats, rBoxFormats);
766 ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth,
767 pTableFormat->GetName(), aExpCols, aExpRows, aExpCells,
768 aTableInfo, true);
772 void SwXMLExport::ExportTableBox( const SwTableBox& rBox,
773 sal_uInt32 nColSpan,
774 sal_uInt32 nRowSpan,
775 SwXMLTableInfo_Impl& rTableInfo )
777 const SwStartNode *pBoxSttNd = rBox.GetSttNd();
778 if( pBoxSttNd )
780 const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat();
781 if( pFrameFormat )
783 auto const it(rTableInfo.GetBoxFormats().find(pFrameFormat));
784 assert(it != rTableInfo.GetBoxFormats().end());
785 if (it->second)
787 assert(!it->second->isEmpty());
788 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
793 if( nRowSpan != 1 )
795 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED,
796 OUString::number(nRowSpan) );
799 if( nColSpan != 1 )
801 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED,
802 OUString::number(nColSpan) );
806 if( pBoxSttNd )
808 // start node -> normal cell
809 // get cell range for table
810 Reference<XCell> xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
811 const_cast<SwTableBox *>(&rBox),
812 const_cast<SwTable *>(rTableInfo.GetTable()) );
814 if (xCell.is())
816 Reference<XText> xText( xCell, UNO_QUERY );
818 // get formula (and protection)
819 const OUString sCellFormula = xCell->getFormula();
821 // if this cell has a formula, export it
822 // (with value and number format)
823 if (!sCellFormula.isEmpty())
825 const OUString sQValue =
826 GetNamespaceMap().GetQNameByKey(
827 XML_NAMESPACE_OOOW, sCellFormula, false );
828 // formula
829 AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue );
832 // value and format (if NumberFormat != -1)
833 Reference<XPropertySet> xCellPropertySet(xCell,
834 UNO_QUERY);
835 if (xCellPropertySet.is())
837 sal_Int32 nNumberFormat = 0;
838 Any aAny = xCellPropertySet->getPropertyValue("NumberFormat");
839 aAny >>= nNumberFormat;
841 if (static_cast<sal_Int32>(getSwDefaultTextFormat()) == nNumberFormat)
843 // text format
844 AddAttribute( XML_NAMESPACE_OFFICE,
845 XML_VALUE_TYPE, XML_STRING );
847 else if ( (-1 != nNumberFormat) && !xText->getString().isEmpty() )
849 // number format key:
850 // (export values only if cell contains text;)
851 XMLNumberFormatAttributesExportHelper::
852 SetNumberFormatAttributes(
853 *this, nNumberFormat, xCell->getValue() );
855 // else: invalid key; ignore
857 // cell protection
858 aAny = xCellPropertySet->getPropertyValue("IsProtected");
859 if (*o3tl::doAccess<bool>(aAny))
861 AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED,
862 XML_TRUE );
865 if( !rTableInfo.IsBaseSectionValid() )
867 aAny = xCellPropertySet->getPropertyValue("TextSection");
868 Reference < XTextSection > xTextSection;
869 aAny >>= xTextSection;
870 rTableInfo.SetBaseSection( xTextSection );
874 // export cell element
875 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(),
876 XML_TABLE_CELL, true, true );
878 // export cell content
879 GetTextParagraphExport()->exportText( xText,
880 rTableInfo.GetBaseSection(),
881 IsShowProgress() );
883 else
885 OSL_FAIL("here should be a XCell");
886 ClearAttrList();
889 else
891 // no start node -> merged cells: export subtable in cell
892 SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
893 XML_TABLE_CELL, true, true );
895 AddAttribute( XML_NAMESPACE_TABLE, XML_IS_SUB_TABLE,
896 GetXMLToken( XML_TRUE ) );
898 SvXMLElementExport aElemExport( *this, XML_NAMESPACE_TABLE,
899 XML_TABLE, true, true );
900 ExportTableLines( rBox.GetTabLines(), rTableInfo );
906 void SwXMLExport::ExportTableLine( const SwTableLine& rLine,
907 const SwXMLTableLines_Impl& rLines,
908 SwXMLTableInfo_Impl& rTableInfo )
910 if( rLine.hasSoftPageBreak() )
912 SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT,
913 XML_SOFT_PAGE_BREAK, true, true );
915 const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat();
916 if( pFrameFormat )
918 auto const it(rTableInfo.GetLineFormats().find(pFrameFormat));
919 assert(it != rTableInfo.GetLineFormats().end());
920 if (it->second)
922 assert(!it->second->isEmpty());
923 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
928 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_ROW, true, true );
929 const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
930 const size_t nBoxes = rBoxes.size();
932 sal_uInt32 nCPos = 0U;
933 size_t nCol = 0U;
934 for( size_t nBox=0U; nBox<nBoxes; ++nBox )
936 const SwTableBox *pBox = rBoxes[nBox];
938 // NEW TABLES
939 const sal_Int32 nRowSpan = pBox->getRowSpan();
940 if( nRowSpan < 1 )
942 // Export style of covered cell, it includes border information.
943 const SwFrameFormat* pFormat = pBox->GetFrameFormat();
944 if (pFormat)
946 auto const it(rTableInfo.GetBoxFormats().find(pFormat));
947 assert(it != rTableInfo.GetBoxFormats().end());
948 if (it->second)
950 assert(!it->second->isEmpty());
951 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
955 SvXMLElementExport aElem2( *this, rTableInfo.GetPrefix(),
956 XML_COVERED_TABLE_CELL, true,
957 false );
960 if( nBox < nBoxes-1U )
961 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
962 else
963 nCPos = rLines.GetWidth();
965 // and their index
966 const size_t nOldCol = nCol;
967 SwXMLTableColumn_Impl aCol( nCPos );
968 SwXMLTableColumns_Impl::const_iterator it = rLines.GetColumns().find( &aCol );
969 OSL_ENSURE( it != rLines.GetColumns().end(), "couldn't find column" );
970 nCol = it - rLines.GetColumns().begin();
972 // #i95726# - Some fault tolerance, if table is somehow corrupted.
973 if ( nCol < nOldCol )
975 OSL_FAIL( "table and/or table information seems to be corrupted." );
976 // NOTE: nOldCol is not necessarily a valid index into
977 // GetColumns(), but that doesn't matter here
978 nCol = nOldCol;
981 const sal_uInt32 nColSpan = nCol - nOldCol + 1U;
983 if ( nRowSpan >= 1 )
984 ExportTableBox( *pBox, nColSpan, static_cast< sal_uInt32 >(nRowSpan), rTableInfo );
986 for( size_t i=nOldCol; i<nCol; ++i )
988 SvXMLElementExport aElemExport( *this, rTableInfo.GetPrefix(),
989 XML_COVERED_TABLE_CELL, true,
990 false );
993 nCol++;
998 void SwXMLExport::ExportTableLines( const SwTableLines& rLines,
999 SwXMLTableInfo_Impl& rTableInfo,
1000 sal_uInt32 nHeaderRows )
1002 OSL_ENSURE( m_pTableLines && !m_pTableLines->empty(),
1003 "SwXMLExport::ExportTableLines: table columns infos missing" );
1004 if( !m_pTableLines || m_pTableLines->empty() )
1005 return;
1007 SwXMLTableLines_Impl* pLines = nullptr;
1008 size_t nInfoPos;
1009 for( nInfoPos=0; nInfoPos < m_pTableLines->size(); nInfoPos++ )
1011 if( m_pTableLines->at( nInfoPos )->GetLines() == &rLines )
1013 pLines = m_pTableLines->at( nInfoPos );
1014 break;
1017 OSL_ENSURE( pLines,
1018 "SwXMLExport::ExportTableLines: table columns info missing" );
1019 OSL_ENSURE( 0==nInfoPos,
1020 "SwXMLExport::ExportTableLines: table columns infos are unsorted" );
1021 if( !pLines )
1022 return;
1024 SwXMLTableLinesCache_Impl::iterator it = m_pTableLines->begin();
1025 advance( it, nInfoPos );
1026 m_pTableLines->erase( it );
1028 if( m_pTableLines->empty() )
1029 m_pTableLines.reset();
1031 // pass 2: export columns
1032 const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
1033 size_t nColumn = 0U;
1034 const size_t nColumns = rCols.size();
1035 sal_Int32 nColRep = 1;
1036 SwXMLTableColumn_Impl *pColumn = (nColumns > 0) ? rCols.front().get() : nullptr;
1037 while( pColumn )
1039 nColumn++;
1040 SwXMLTableColumn_Impl *pNextColumn =
1041 (nColumn < nColumns) ? rCols[nColumn].get() : nullptr;
1042 if( pNextColumn &&
1043 pNextColumn->GetStyleName() == pColumn->GetStyleName() )
1045 nColRep++;
1047 else
1049 AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME,
1050 EncodeStyleName(pColumn->GetStyleName()) );
1052 if( nColRep > 1 )
1054 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1055 OUString::number(nColRep) );
1059 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_COLUMN, true, true );
1062 nColRep = 1;
1064 pColumn = pNextColumn;
1067 // pass 3: export line/rows
1068 const size_t nLines = rLines.size();
1069 // export header rows, if present
1070 if( nHeaderRows > 0 )
1072 SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
1073 XML_TABLE_HEADER_ROWS, true, true );
1075 OSL_ENSURE( nHeaderRows <= nLines, "more headers then lines?" );
1076 for( size_t nLine = 0U; nLine < nHeaderRows; ++nLine )
1077 ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
1079 // export remaining rows
1080 for( size_t nLine = nHeaderRows; nLine < nLines; ++nLine )
1082 ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
1085 delete pLines;
1088 void SwXMLExport::ExportTable( const SwTableNode& rTableNd )
1090 ::std::optional<sal_uInt16> oPrefix = XML_NAMESPACE_TABLE;
1091 if (const SwFrameFormat* pFlyFormat = rTableNd.GetFlyFormat())
1093 if (SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
1095 // TODO ODF 1.4 OFFICE-3761
1096 if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
1098 oPrefix = XML_NAMESPACE_LO_EXT;
1100 else
1102 oPrefix.reset(); // no export to OASIS namespace yet
1107 if (!oPrefix)
1108 return;
1110 const SwTable& rTable = rTableNd.GetTable();
1111 const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
1112 if (pTableFormat && !pTableFormat->GetName().isEmpty())
1114 AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pTableFormat->GetName());
1115 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME,
1116 EncodeStyleName(pTableFormat->GetName()));
1119 // table:template-name=
1120 if (!rTable.GetTableStyleName().isEmpty())
1122 OUString sStyleName;
1123 SwStyleNameMapper::FillProgName(rTable.GetTableStyleName(), sStyleName, SwGetPoolIdFromName::TabStyle);
1124 AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sStyleName);
1127 SvXMLElementExport aElem(*this, *oPrefix, XML_TABLE, true, true);
1129 // export DDE source (if this is a DDE table)
1130 if ( auto pSwDdeTable = dynamic_cast<const SwDDETable*>( &rTable) )
1132 // get DDE Field Type (contains the DDE connection)
1133 const SwDDEFieldType* pDDEFieldType = pSwDdeTable->GetDDEFieldType();
1135 // connection name
1136 AddAttribute( XML_NAMESPACE_OFFICE, XML_NAME,
1137 pDDEFieldType->GetName() );
1139 // DDE command
1140 const OUString& sCmd = pDDEFieldType->GetCmd();
1141 sal_Int32 nIdx{ 0 };
1142 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION,
1143 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1144 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_ITEM,
1145 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1146 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_TOPIC,
1147 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1149 // auto update
1150 if (pDDEFieldType->GetType() == SfxLinkUpdateMode::ALWAYS)
1152 AddAttribute( XML_NAMESPACE_OFFICE,
1153 XML_AUTOMATIC_UPDATE, XML_TRUE );
1156 // DDE source element (always empty)
1157 SvXMLElementExport aSource(*this, XML_NAMESPACE_OFFICE,
1158 XML_DDE_SOURCE, true, false);
1161 auto const& rFormats(static_cast<SwXMLTextParagraphExport const*>(GetTextParagraphExport().get())->GetTableFormats());
1162 auto const it(rFormats.find(&rTableNd));
1163 assert(it != rFormats.end());
1164 SwXMLTableInfo_Impl aTableInfo(&rTable, *oPrefix, it->second.first, it->second.second);
1165 ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() );
1168 void SwXMLTextParagraphExport::exportTableAutoStyles() {
1169 // note: maTableNodes is used here only to keep the iteration order as before
1170 for (const auto* pTableNode : maTableNodes)
1172 static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode);
1176 void SwXMLTextParagraphExport::CollectTableLinesAutoStyles(const SwTableLines& rLines,
1177 SwFrameFormat& rFormat, bool _bProgress)
1179 // Follow SwXMLExport::ExportTableLines/ExportTableLine/ExportTableBox
1180 for (const SwTableLine* pLine : rLines)
1182 for (SwTableBox* pBox : pLine->GetTabBoxes())
1184 if (pBox->getRowSpan() <= 0)
1185 continue;
1186 if (pBox->GetSttNd())
1188 if (rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell(&rFormat, pBox))
1189 exportText(xCell, true /*bAutoStyles*/, _bProgress, true /*bExportParagraph*/);
1191 else
1193 // no start node -> merged cells: export subtable in cell
1194 CollectTableLinesAutoStyles(pBox->GetTabLines(), rFormat, _bProgress);
1200 void SwXMLTextParagraphExport::exportTable(
1201 const Reference < XTextContent > & rTextContent,
1202 bool bAutoStyles, bool _bProgress )
1204 bool bOldShowProgress = static_cast<SwXMLExport&>(GetExport()).IsShowProgress();
1205 static_cast<SwXMLExport&>(GetExport()).SetShowProgress( _bProgress );
1207 Reference < XTextTable > xTextTable( rTextContent, UNO_QUERY );
1208 OSL_ENSURE( xTextTable.is(), "text table missing" );
1209 if( xTextTable.is() )
1211 Reference<XUnoTunnel> xTableTunnel( rTextContent, UNO_QUERY);
1212 SwXTextTable* pXTable = comphelper::getFromUnoTunnel<SwXTextTable>(xTableTunnel);
1213 if( pXTable )
1215 SwFrameFormat *const pFormat = pXTable->GetFrameFormat();
1216 OSL_ENSURE( pFormat, "table format missing" );
1217 const SwTable *pTable = SwTable::FindTable( pFormat );
1218 OSL_ENSURE( pTable, "table missing" );
1219 const SwTableNode *pTableNd = pTable->GetTableNode();
1220 OSL_ENSURE( pTableNd, "table node missing" );
1221 if( bAutoStyles )
1223 // AUTOSTYLES: Optimization: Do not export table autostyle if
1224 // we are currently exporting the content.xml stuff and
1225 // the table is located in header/footer:
1226 // During the flat XML export (used e.g. by .sdw-export)
1227 // ALL flags are set at the same time.
1228 const bool bExportStyles = bool( GetExport().getExportFlags() & SvXMLExportFlags::STYLES );
1229 if (!isAutoStylesCollected()
1230 && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(*pTableNd)))
1232 maTableNodes.push_back(pTableNd);
1233 m_TableFormats.emplace(pTableNd, ::std::make_pair(SwXMLTextParagraphExport::FormatMap(), SwXMLTextParagraphExport::FormatMap()));
1234 // Collect all tables inside cells of this table, too
1235 CollectTableLinesAutoStyles(pTable->GetTabLines(), *pFormat, _bProgress);
1238 else
1240 static_cast<SwXMLExport&>(GetExport()).ExportTable( *pTableNd );
1245 static_cast<SwXMLExport&>(GetExport()).SetShowProgress( bOldShowProgress );
1248 void SwXMLExport::DeleteTableLines()
1250 if ( m_pTableLines )
1252 for (SwXMLTableLines_Impl* p : *m_pTableLines)
1253 delete p;
1254 m_pTableLines->clear();
1255 m_pTableLines.reset();
1259 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */