sc: factor out some more code
[LibreOffice.git] / sw / source / filter / xml / xmltble.cxx
blobb000c476b1f4b8b207d26cfea410bd2739ef16d9
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>
57 #include <names.hxx>
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::text;
62 using namespace ::com::sun::star::beans;
63 using namespace ::xmloff::token;
64 using table::XCell;
65 using std::vector;
66 using std::advance;
69 class SwXMLTableColumn_Impl : public SwWriteTableCol
71 OUString m_sStyleName;
72 sal_uInt32 m_nRelWidth;
74 public:
76 explicit SwXMLTableColumn_Impl(sal_uInt32 nPosition)
77 : SwWriteTableCol(nPosition)
78 , m_nRelWidth(0)
79 {};
81 void SetStyleName( const OUString& rName ) { m_sStyleName = rName; }
82 const OUString& GetStyleName() const { return m_sStyleName; }
84 void SetRelWidth( sal_uInt32 nSet ) { m_nRelWidth = nSet; }
85 sal_uInt32 GetRelWidth() const { return m_nRelWidth; }
88 namespace {
90 struct SwXMLTableColumnCmpWidth_Impl
92 bool operator()( SwXMLTableColumn_Impl* const& lhs, SwXMLTableColumn_Impl* const& rhs ) const
94 sal_Int32 n = static_cast<sal_Int32>(lhs->GetWidthOpt()) - static_cast<sal_Int32>(rhs->GetWidthOpt());
95 if( !n )
96 n = static_cast<sal_Int32>(lhs->GetRelWidth()) - static_cast<sal_Int32>(rhs->GetRelWidth());
97 return n < 0;
101 class SwXMLTableColumns_Impl : public o3tl::sorted_vector<std::unique_ptr<SwXMLTableColumn_Impl>, o3tl::less_ptr_to > {
106 class SwXMLTableColumnsSortByWidth_Impl : public o3tl::sorted_vector<SwXMLTableColumn_Impl*, SwXMLTableColumnCmpWidth_Impl> {};
108 class SwXMLTableLines_Impl
110 SwXMLTableColumns_Impl m_aCols;
111 const SwTableLines *m_pLines;
112 sal_uInt32 m_nWidth;
114 public:
116 explicit SwXMLTableLines_Impl( const SwTableLines& rLines );
118 sal_uInt32 GetWidth() const { return m_nWidth; }
119 const SwTableLines *GetLines() const { return m_pLines; }
121 const SwXMLTableColumns_Impl& GetColumns() const { return m_aCols; }
124 SwXMLTableLines_Impl::SwXMLTableLines_Impl( const SwTableLines& rLines ) :
125 m_pLines( &rLines ),
126 m_nWidth( 0 )
128 #if OSL_DEBUG_LEVEL > 0
129 sal_uInt32 nEndCPos = 0U;
130 #endif
131 const size_t nLines = rLines.size();
132 for( size_t nLine=0U; nLine<nLines; ++nLine )
134 const SwTableLine *pLine = rLines[nLine];
135 const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
136 const size_t nBoxes = rBoxes.size();
138 sal_uInt32 nCPos = 0U;
139 for( size_t nBox=0U; nBox<nBoxes; ++nBox )
141 const SwTableBox *pBox = rBoxes[nBox];
143 if( nBox < nBoxes-1U || m_nWidth==0 )
145 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
146 std::unique_ptr<SwXMLTableColumn_Impl> pCol(
147 new SwXMLTableColumn_Impl( nCPos ));
149 m_aCols.insert( std::move(pCol) );
151 if( nBox==nBoxes-1U )
153 OSL_ENSURE( nLine==0U && m_nWidth==0,
154 "parent width will be lost" );
155 m_nWidth = nCPos;
158 else
160 #if OSL_DEBUG_LEVEL > 0
161 sal_uInt32 nCheckPos =
162 nCPos + SwWriteTable::GetBoxWidth( pBox );
163 if( !nEndCPos )
165 nEndCPos = nCheckPos;
167 #endif
168 nCPos = m_nWidth;
169 #if OSL_DEBUG_LEVEL > 0
170 SwXMLTableColumn_Impl aCol( m_nWidth );
171 OSL_ENSURE( m_aCols.find(&aCol) != m_aCols.end(), "couldn't find last column" );
172 OSL_ENSURE( SwXMLTableColumn_Impl(nCheckPos) ==
173 SwXMLTableColumn_Impl(nCPos),
174 "rows have different total widths" );
175 #endif
181 typedef vector< SwFrameFormat* > SwXMLFrameFormats_Impl;
183 class SwXMLTableFrameFormatsSort_Impl
185 private:
186 SwXMLFrameFormats_Impl m_aFormatList;
187 SwXMLTextParagraphExport::FormatMap & m_rFormatMap;
189 public:
190 SwXMLTableFrameFormatsSort_Impl(SwXMLTextParagraphExport::FormatMap & rFormatMap)
191 : m_rFormatMap(rFormatMap)
193 ::std::optional<OUString> AddRow(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix, sal_uInt32 nLine );
194 ::std::optional<OUString> AddCell(SwFrameFormat& rFrameFormat, std::u16string_view rNamePrefix,
195 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop );
198 ::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddRow(SwFrameFormat& rFrameFormat,
199 std::u16string_view rNamePrefix,
200 sal_uInt32 nLine )
202 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
204 const SwFormatFrameSize *pFrameSize = rItemSet.GetItemIfSet( RES_FRM_SIZE, false );
205 const SwFormatRowSplit* pRowSplit = rItemSet.GetItemIfSet( RES_ROW_SPLIT, false );
206 const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
207 const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
209 // empty styles have not to be exported
210 if( !pFrameSize && !pBrush && !pRowSplit && !pHasTextChangesOnly )
212 m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert
213 return {};
216 // order is: -/brush, size/-, size/brush
217 SwXMLFrameFormats_Impl::iterator i;
218 for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
220 const SwFormatFrameSize *pTestFrameSize = nullptr;
221 const SwFormatRowSplit* pTestRowSplit = nullptr;
222 const SvxBrushItem *pTestBrush = nullptr;
223 const SvxPrintItem *pTestHasTextChangesOnly = nullptr;
224 const SwFrameFormat *pTestFormat = *i;
225 const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
226 if( const SwFormatFrameSize* pItem = rTestSet.GetItemIfSet( RES_FRM_SIZE, false ) )
228 if( !pFrameSize )
229 break;
231 pTestFrameSize = pItem;
233 else
235 if( pFrameSize )
236 continue;
239 if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false) )
241 if( !pBrush )
242 break;
244 pTestBrush = pItem;
246 else
248 if( pBrush )
249 continue;
252 if( const SwFormatRowSplit* pItem = rTestSet.GetItemIfSet( RES_ROW_SPLIT, false ) )
254 if( !pRowSplit )
255 break;
257 pTestRowSplit = pItem;
259 else
261 if( pRowSplit )
262 continue;
265 if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) )
267 if( !pHasTextChangesOnly )
268 break;
270 pTestHasTextChangesOnly = pItem;
272 else
274 if( pHasTextChangesOnly )
275 continue;
278 if( pFrameSize &&
279 ( pFrameSize->GetHeightSizeType() != pTestFrameSize->GetHeightSizeType() ||
280 pFrameSize->GetHeight() != pTestFrameSize->GetHeight() ) )
281 continue;
283 if( pBrush && (*pBrush != *pTestBrush) )
284 continue;
286 if( pRowSplit && (!pRowSplit->GetValue() != !pTestRowSplit->GetValue()) )
287 continue;
289 if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) )
290 continue;
292 // found!
293 auto const oNameIt(m_rFormatMap.find(pTestFormat));
294 assert(oNameIt != m_rFormatMap.end());
295 auto const oName(oNameIt->second);
296 assert(oName);
297 m_rFormatMap.try_emplace(&rFrameFormat, oName);
298 return {};
302 OUString const name(OUString::Concat(rNamePrefix) + "." + OUString::number(nLine+1));
303 m_rFormatMap.try_emplace(&rFrameFormat, name);
304 if ( i != m_aFormatList.end() ) ++i;
305 m_aFormatList.insert( i, &rFrameFormat );
306 return ::std::optional<OUString>(name);
310 static OUString lcl_xmltble_appendBoxPrefix(std::u16string_view rNamePrefix,
311 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
313 if( bTop )
315 OUString sTmp;
316 sw_GetTableBoxColStr( o3tl::narrowing<sal_uInt16>(nCol), sTmp );
317 return OUString::Concat(rNamePrefix) + "." + sTmp + OUString::number(nRow + 1);
319 return OUString::Concat(rNamePrefix)
320 + "." + OUString::number(nCol + 1)
321 + "." + OUString::number(nRow + 1);
324 ::std::optional<OUString> SwXMLTableFrameFormatsSort_Impl::AddCell(SwFrameFormat& rFrameFormat,
325 std::u16string_view rNamePrefix,
326 sal_uInt32 nCol, sal_uInt32 nRow, bool bTop )
328 const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
329 const SwFormatVertOrient *pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT, false );
330 const SvxBrushItem *pBrush = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
331 const SvxBoxItem *pBox = rItemSet.GetItemIfSet( RES_BOX, false );
332 const SwTableBoxNumFormat *pNumFormat = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT,
333 false );
334 const SvxFrameDirectionItem *pFrameDir = rItemSet.GetItemIfSet( RES_FRAMEDIR,
335 false );
336 const SvXMLAttrContainerItem *pAttCnt = rItemSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
337 false );
338 const SvxPrintItem *pHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
340 // empty styles have not to be exported
341 if( !pVertOrient && !pBrush && !pBox && !pNumFormat && !pFrameDir && !pAttCnt &&
342 !pHasTextChangesOnly )
344 m_rFormatMap.try_emplace(&rFrameFormat); // empty just to enable assert
345 return {};
348 // order is: -/-/-/num,
349 // -/-/box/-, -/-/box/num,
350 // -/brush/-/-, -/brush/-/num, -/brush/box/-, -/brush/box/num,
351 // vert/-/-/-, vert/-/-/num, vert/-/box/-, ver/-/box/num,
352 // vert/brush/-/-, vert/brush/-/num, vert/brush/box/-,
353 // vert/brush/box/num
354 SwXMLFrameFormats_Impl::iterator i;
355 for( i = m_aFormatList.begin(); i < m_aFormatList.end(); ++i )
357 const SwFormatVertOrient *pTestVertOrient = nullptr;
358 const SvxBrushItem *pTestBrush = nullptr;
359 const SvxBoxItem *pTestBox = nullptr;
360 const SwTableBoxNumFormat *pTestNumFormat = nullptr;
361 const SvxFrameDirectionItem *pTestFrameDir = nullptr;
362 const SvXMLAttrContainerItem *pTestAttCnt = nullptr;
363 const SvxPrintItem *pTestHasTextChangesOnly = rItemSet.GetItemIfSet( RES_PRINT, false);
364 const SwFrameFormat* pTestFormat = *i;
365 const SfxItemSet& rTestSet = pTestFormat->GetAttrSet();
366 if( const SwFormatVertOrient* pItem = rTestSet.GetItemIfSet( RES_VERT_ORIENT, false ) )
368 if( !pVertOrient )
369 break;
371 pTestVertOrient = pItem;
373 else
375 if( pVertOrient )
376 continue;
379 if( const SvxBrushItem* pItem = rTestSet.GetItemIfSet( RES_BACKGROUND, false ) )
381 if( !pBrush )
382 break;
384 pTestBrush = pItem;
386 else
388 if( pBrush )
389 continue;
392 if( const SvxBoxItem* pItem = rTestSet.GetItemIfSet( RES_BOX, false ) )
394 if( !pBox )
395 break;
397 pTestBox = pItem;
399 else
401 if( pBox )
402 continue;
405 if ( const SwTableBoxNumFormat* pItem = rTestSet.GetItemIfSet( RES_BOXATR_FORMAT,
406 false ) )
408 if( !pNumFormat )
409 break;
411 pTestNumFormat = pItem;
413 else
415 if( pNumFormat )
416 continue;
420 if ( const SvxFrameDirectionItem* pItem = rTestSet.GetItemIfSet( RES_FRAMEDIR,
421 false ) )
423 if( !pFrameDir )
424 break;
426 pTestFrameDir = pItem;
428 else
430 if( pFrameDir )
431 continue;
435 if ( const SvXMLAttrContainerItem* pItem = rTestSet.GetItemIfSet( RES_UNKNOWNATR_CONTAINER,
436 false ) )
438 if( !pAttCnt )
439 break;
441 pTestAttCnt = pItem;
443 else
445 if ( pAttCnt )
446 continue;
450 if( const SvxPrintItem* pItem = rTestSet.GetItemIfSet( RES_PRINT, false ) )
452 if( !pHasTextChangesOnly )
453 break;
455 pTestHasTextChangesOnly = pItem;
457 else
459 if( pHasTextChangesOnly )
460 continue;
463 if( pVertOrient &&
464 pVertOrient->GetVertOrient() != pTestVertOrient->GetVertOrient() )
465 continue;
467 if( pBrush && ( *pBrush != *pTestBrush ) )
468 continue;
470 if( pBox && ( *pBox != *pTestBox ) )
471 continue;
473 if( pNumFormat && pNumFormat->GetValue() != pTestNumFormat->GetValue() )
474 continue;
476 if( pFrameDir && pFrameDir->GetValue() != pTestFrameDir->GetValue() )
477 continue;
479 if( pAttCnt && ( *pAttCnt != *pTestAttCnt ) )
480 continue;
482 if( pHasTextChangesOnly && (!pHasTextChangesOnly->GetValue() != !pTestHasTextChangesOnly->GetValue()) )
483 continue;
485 // found!
486 auto const oNameIt(m_rFormatMap.find(pTestFormat));
487 assert(oNameIt != m_rFormatMap.end());
488 auto const oName(oNameIt->second);
489 assert(oName);
490 m_rFormatMap.try_emplace(&rFrameFormat, oName);
491 return {};
495 OUString const name(lcl_xmltble_appendBoxPrefix(rNamePrefix, nCol, nRow, bTop));
496 m_rFormatMap.try_emplace(&rFrameFormat, name);
497 if ( i != m_aFormatList.end() ) ++i;
498 m_aFormatList.insert( i, &rFrameFormat );
499 return ::std::optional<OUString>(name);
503 class SwXMLTableInfo_Impl
505 const SwTable *m_pTable;
506 Reference<XTextSection> m_xBaseSection;
507 bool m_bBaseSectionValid;
508 sal_uInt32 m_nPrefix;
509 SwXMLTextParagraphExport::FormatMap const& m_rLineFormats;
510 SwXMLTextParagraphExport::FormatMap const& m_rBoxFormats;
512 public:
514 inline SwXMLTableInfo_Impl( const SwTable *pTable, sal_uInt16 nPrefix,
515 SwXMLTextParagraphExport::FormatMap const& rLineFormats,
516 SwXMLTextParagraphExport::FormatMap const& rBoxFormats)
517 : m_pTable(pTable)
518 , m_bBaseSectionValid(false)
519 , m_nPrefix(nPrefix)
520 , m_rLineFormats(rLineFormats)
521 , m_rBoxFormats(rBoxFormats)
525 const SwTable *GetTable() const { return m_pTable; }
526 const SwFrameFormat *GetTableFormat() const { return m_pTable->GetFrameFormat(); }
528 bool IsBaseSectionValid() const { return m_bBaseSectionValid; }
529 const Reference<XTextSection>& GetBaseSection() const { return m_xBaseSection; }
530 inline void SetBaseSection( const Reference < XTextSection >& rBase );
531 /// The namespace (table or loext) that should be used for the elements.
532 sal_uInt16 GetPrefix() const { return m_nPrefix; }
533 SwXMLTextParagraphExport::FormatMap const& GetLineFormats() const { return m_rLineFormats; }
534 SwXMLTextParagraphExport::FormatMap const& GetBoxFormats() const { return m_rBoxFormats; }
537 inline void SwXMLTableInfo_Impl::SetBaseSection(
538 const Reference < XTextSection >& rBaseSection )
540 m_xBaseSection = rBaseSection;
541 m_bBaseSectionValid = true;
544 void SwXMLExport::ExportTableColumnStyle( const SwXMLTableColumn_Impl& rCol )
546 // <style:style ...>
547 CheckAttrList();
549 // style:name="..."
550 bool bEncoded = false;
551 AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
552 EncodeStyleName( rCol.GetStyleName(), &bEncoded ) );
553 if( bEncoded )
554 AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCol.GetStyleName() );
556 // style:family="table-column"
557 AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE_COLUMN );
560 SvXMLElementExport aElem( *this, XML_NAMESPACE_STYLE, XML_STYLE, true,
561 true );
562 if( rCol.GetWidthOpt() )
564 OUStringBuffer sValue;
565 GetTwipUnitConverter().convertMeasureToXML( sValue,
566 rCol.GetWidthOpt() );
567 AddAttribute( XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH,
568 sValue.makeStringAndClear() );
570 if( rCol.GetRelWidth() )
572 OUString sValue = OUString::number(static_cast<sal_Int32>(rCol.GetRelWidth()) ) + "*";
573 AddAttribute( XML_NAMESPACE_STYLE, XML_REL_COLUMN_WIDTH,
574 sValue );
578 SvXMLElementExport aElemExport( *this, XML_NAMESPACE_STYLE,
579 XML_TABLE_COLUMN_PROPERTIES,
580 true, true );
585 void SwXMLExport::ExportTableLinesAutoStyles( const SwTableLines& rLines,
586 sal_uInt32 nAbsWidth, sal_uInt32 nBaseWidth,
587 std::u16string_view rNamePrefix,
588 SwXMLTableColumnsSortByWidth_Impl& rExpCols,
589 SwXMLTableFrameFormatsSort_Impl& rExpRows,
590 SwXMLTableFrameFormatsSort_Impl& rExpCells,
591 SwXMLTableInfo_Impl& rTableInfo,
592 bool bTop )
594 // pass 1: calculate columns
595 SwXMLTableLines_Impl *pLines = new SwXMLTableLines_Impl( rLines );
596 if( !m_pTableLines )
597 m_pTableLines.reset(new SwXMLTableLinesCache_Impl);
599 m_pTableLines->push_back( pLines );
601 // pass 2: export column styles
603 const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
604 sal_uInt32 nCPos = 0U;
605 const size_t nColumns = rCols.size();
606 for( size_t nColumn=0U; nColumn<nColumns; ++nColumn )
608 SwXMLTableColumn_Impl *pColumn = rCols[nColumn].get();
610 sal_uInt32 nOldCPos = nCPos;
611 nCPos = pColumn->GetPos();
613 sal_uInt32 nWidth = nCPos - nOldCPos;
615 // If a base width is given, the table has either an automatic
616 // or margin alignment, or a percentage width. In either case,
617 // relative widths should be exported.
618 if( nBaseWidth )
620 pColumn->SetRelWidth( nWidth );
623 // If an absolute width is given, the table either has a fixed
624 // width, or the current width is known from the layout. In the
625 // later case, a base width is set in addition and must be used
626 // to "absolutize" the relative column width.
627 if( nAbsWidth )
629 sal_uInt32 nColAbsWidth = nWidth;
630 if( nBaseWidth )
632 nColAbsWidth *= nAbsWidth;
633 nColAbsWidth += (nBaseWidth/2UL);
634 nColAbsWidth /= nBaseWidth;
636 pColumn->SetWidthOpt( nColAbsWidth, false );
639 SwXMLTableColumnsSortByWidth_Impl::const_iterator it = rExpCols.find( pColumn );
640 if( it != rExpCols.end() )
642 pColumn->SetStyleName( (*it)->GetStyleName() );
644 else
646 if( bTop )
648 OUString sTmp;
649 sw_GetTableBoxColStr( nColumn, sTmp );
650 pColumn->SetStyleName( OUString::Concat(rNamePrefix) + "." + sTmp );
652 else
654 pColumn->SetStyleName(
655 OUString::Concat(rNamePrefix) + "." + OUString::number(nColumn + 1U) );
657 ExportTableColumnStyle( *pColumn );
658 rExpCols.insert( pColumn );
663 // pass 3: export line/rows
664 const size_t nLines = rLines.size();
665 for( size_t nLine=0U; nLine<nLines; ++nLine )
667 SwTableLine *pLine = rLines[nLine];
669 SwFrameFormat *pFrameFormat = pLine->GetFrameFormat();
670 if (auto oNew = rExpRows.AddRow(*pFrameFormat, rNamePrefix, nLine))
672 ExportFormat(*pFrameFormat, XML_TABLE_ROW, std::move(oNew));
675 const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
676 const size_t nBoxes = rBoxes.size();
678 sal_uInt32 nCPos = 0U;
679 size_t nCol = 0U;
680 for( size_t nBox=0U; nBox<nBoxes; nBox++ )
682 SwTableBox *pBox = rBoxes[nBox];
684 if( nBox < nBoxes-1U )
685 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
686 else
687 nCPos = pLines->GetWidth();
689 // and their index
690 const size_t nOldCol = nCol;
691 SwXMLTableColumn_Impl aCol( nCPos );
692 SwXMLTableColumns_Impl::const_iterator it = pLines->GetColumns().find( &aCol );
693 OSL_ENSURE( it != pLines->GetColumns().end(), "couldn't find column" );
694 nCol = it - pLines->GetColumns().begin();
696 const SwStartNode *pBoxSttNd = pBox->GetSttNd();
697 if( pBoxSttNd )
699 SwFrameFormat *pFrameFormat2 = pBox->GetFrameFormat();
700 if (auto oNew = rExpCells.AddCell(*pFrameFormat2, rNamePrefix, nOldCol, nLine,
701 bTop) )
703 ExportFormat(*pFrameFormat2, XML_TABLE_CELL, std::move(oNew));
706 rtl::Reference < SwXCell > xCell = SwXCell::CreateXCell(
707 const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
708 pBox,
709 const_cast<SwTable *>(rTableInfo.GetTable()) );
710 if (xCell.is())
712 if( !rTableInfo.IsBaseSectionValid() )
714 Any aAny = xCell->getPropertyValue(u"TextSection"_ustr);
715 Reference < XTextSection > xTextSection;
716 aAny >>= xTextSection;
717 rTableInfo.SetBaseSection( xTextSection );
720 const bool bExportContent = bool(getExportFlags() & SvXMLExportFlags::CONTENT );
721 if ( !bExportContent )
723 // AUTOSTYLES - not needed anymore if we are currently exporting content.xml
724 GetTextParagraphExport()->collectTextAutoStyles(
725 xCell, rTableInfo.GetBaseSection(), IsShowProgress() );
728 else {
729 OSL_FAIL("here should be a XCell");
732 else
734 ExportTableLinesAutoStyles( pBox->GetTabLines(),
735 nAbsWidth, nBaseWidth,
736 lcl_xmltble_appendBoxPrefix( rNamePrefix,
737 nOldCol, nLine, bTop ),
738 rExpCols, rExpRows, rExpCells,
739 rTableInfo );
742 nCol++;
747 void SwXMLExport::ExportTableAutoStyles(const SwTableNode& rTableNd)
749 auto & rFormats(static_cast<SwXMLTextParagraphExport *>(GetTextParagraphExport().get())->GetTableFormats());
750 auto const it(rFormats.find(&rTableNd));
751 assert(it != rFormats.end());
752 SwXMLTextParagraphExport::FormatMap & rRowFormats(it->second.first);
753 SwXMLTextParagraphExport::FormatMap & rBoxFormats(it->second.second);
754 const SwTable& rTable = rTableNd.GetTable();
755 const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
757 if( !pTableFormat )
758 return;
760 sal_Int16 eTabHoriOri = pTableFormat->GetHoriOrient().GetHoriOrient();
761 const SwFormatFrameSize& rFrameSize = pTableFormat->GetFrameSize();
763 sal_uInt32 nAbsWidth = rFrameSize.GetSize().Width();
764 sal_uInt32 nBaseWidth = 0;
765 sal_Int8 nPercentWidth = rFrameSize.GetWidthPercent();
767 bool bFixAbsWidth = nPercentWidth != 0 || /*text::*/HoriOrientation::NONE == eTabHoriOri
768 || /*text::*/HoriOrientation::FULL == eTabHoriOri;
769 if( bFixAbsWidth )
771 nBaseWidth = nAbsWidth;
772 nAbsWidth = pTableFormat->FindLayoutRect(true).Width();
773 if( !nAbsWidth )
775 // TODO?
778 ExportTableFormat( *pTableFormat, nAbsWidth );
780 SwXMLTableColumnsSortByWidth_Impl aExpCols;
781 SwXMLTableFrameFormatsSort_Impl aExpRows(rRowFormats);
782 SwXMLTableFrameFormatsSort_Impl aExpCells(rBoxFormats);
783 SwXMLTableInfo_Impl aTableInfo(&rTable, XML_NAMESPACE_TABLE, rRowFormats, rBoxFormats);
784 ExportTableLinesAutoStyles( rTable.GetTabLines(), nAbsWidth, nBaseWidth,
785 pTableFormat->GetName(), aExpCols, aExpRows, aExpCells,
786 aTableInfo, true);
790 void SwXMLExport::ExportTableBox( const SwTableBox& rBox,
791 sal_uInt32 nColSpan,
792 sal_uInt32 nRowSpan,
793 SwXMLTableInfo_Impl& rTableInfo )
795 const SwStartNode *pBoxSttNd = rBox.GetSttNd();
796 if( pBoxSttNd )
798 const SwFrameFormat *pFrameFormat = rBox.GetFrameFormat();
799 if( pFrameFormat )
801 auto const it(rTableInfo.GetBoxFormats().find(pFrameFormat));
802 assert(it != rTableInfo.GetBoxFormats().end());
803 if (it->second)
805 assert(!it->second->isEmpty());
806 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
811 if( nRowSpan != 1 )
813 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED,
814 OUString::number(nRowSpan) );
817 if( nColSpan != 1 )
819 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED,
820 OUString::number(nColSpan) );
824 if( pBoxSttNd )
826 // start node -> normal cell
827 // get cell range for table
828 rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell( const_cast<SwFrameFormat *>(rTableInfo.GetTableFormat()),
829 const_cast<SwTableBox *>(&rBox),
830 const_cast<SwTable *>(rTableInfo.GetTable()) );
832 if (xCell.is())
834 // get formula (and protection)
835 const OUString sCellFormula = xCell->getFormula();
837 // if this cell has a formula, export it
838 // (with value and number format)
839 if (!sCellFormula.isEmpty())
841 const OUString sQValue =
842 GetNamespaceMap().GetQNameByKey(
843 XML_NAMESPACE_OOOW, sCellFormula, false );
844 // formula
845 AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue );
848 // value and format (if NumberFormat != -1)
849 sal_Int32 nNumberFormat = 0;
850 Any aAny = xCell->getPropertyValue(u"NumberFormat"_ustr);
851 aAny >>= nNumberFormat;
853 if (static_cast<sal_Int32>(getSwDefaultTextFormat()) == nNumberFormat)
855 // text format
856 AddAttribute( XML_NAMESPACE_OFFICE,
857 XML_VALUE_TYPE, XML_STRING );
859 else if ( (-1 != nNumberFormat) && !xCell->getString().isEmpty() )
861 // number format key:
862 // (export values only if cell contains text;)
863 XMLNumberFormatAttributesExportHelper::
864 SetNumberFormatAttributes(
865 *this, nNumberFormat, xCell->getValue() );
867 // else: invalid key; ignore
869 // cell protection
870 aAny = xCell->getPropertyValue(u"IsProtected"_ustr);
871 if (*o3tl::doAccess<bool>(aAny))
873 AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED,
874 XML_TRUE );
877 if( !rTableInfo.IsBaseSectionValid() )
879 aAny = xCell->getPropertyValue(u"TextSection"_ustr);
880 Reference < XTextSection > xTextSection;
881 aAny >>= xTextSection;
882 rTableInfo.SetBaseSection( xTextSection );
885 // export cell element
886 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(),
887 XML_TABLE_CELL, true, true );
889 // export cell content
890 GetTextParagraphExport()->exportText( xCell,
891 rTableInfo.GetBaseSection(),
892 IsShowProgress() );
894 else
896 OSL_FAIL("here should be a XCell");
897 ClearAttrList();
900 else
902 // no start node -> merged cells: export subtable in cell
903 SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
904 XML_TABLE_CELL, true, true );
906 AddAttribute( XML_NAMESPACE_TABLE, XML_IS_SUB_TABLE,
907 GetXMLToken( XML_TRUE ) );
909 SvXMLElementExport aElemExport( *this, XML_NAMESPACE_TABLE,
910 XML_TABLE, true, true );
911 ExportTableLines( rBox.GetTabLines(), rTableInfo );
917 void SwXMLExport::ExportTableLine( const SwTableLine& rLine,
918 const SwXMLTableLines_Impl& rLines,
919 SwXMLTableInfo_Impl& rTableInfo )
921 if( rLine.hasSoftPageBreak() )
923 SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT,
924 XML_SOFT_PAGE_BREAK, true, true );
926 const SwFrameFormat *pFrameFormat = rLine.GetFrameFormat();
927 if( pFrameFormat )
929 auto const it(rTableInfo.GetLineFormats().find(pFrameFormat));
930 assert(it != rTableInfo.GetLineFormats().end());
931 if (it->second)
933 assert(!it->second->isEmpty());
934 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
939 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_ROW, true, true );
940 const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
941 const size_t nBoxes = rBoxes.size();
943 sal_uInt32 nCPos = 0U;
944 size_t nCol = 0U;
945 for( size_t nBox=0U; nBox<nBoxes; ++nBox )
947 const SwTableBox *pBox = rBoxes[nBox];
949 // NEW TABLES
950 const sal_Int32 nRowSpan = pBox->getRowSpan();
951 if( nRowSpan < 1 )
953 // Export style of covered cell, it includes border information.
954 const SwFrameFormat* pFormat = pBox->GetFrameFormat();
955 if (pFormat)
957 auto const it(rTableInfo.GetBoxFormats().find(pFormat));
958 assert(it != rTableInfo.GetBoxFormats().end());
959 if (it->second)
961 assert(!it->second->isEmpty());
962 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, EncodeStyleName(*it->second));
966 SvXMLElementExport aElem2( *this, rTableInfo.GetPrefix(),
967 XML_COVERED_TABLE_CELL, true,
968 false );
971 if( nBox < nBoxes-1U )
972 nCPos = nCPos + SwWriteTable::GetBoxWidth( pBox );
973 else
974 nCPos = rLines.GetWidth();
976 // and their index
977 const size_t nOldCol = nCol;
978 SwXMLTableColumn_Impl aCol( nCPos );
979 SwXMLTableColumns_Impl::const_iterator it = rLines.GetColumns().find( &aCol );
980 OSL_ENSURE( it != rLines.GetColumns().end(), "couldn't find column" );
981 nCol = it - rLines.GetColumns().begin();
983 // #i95726# - Some fault tolerance, if table is somehow corrupted.
984 if ( nCol < nOldCol )
986 OSL_FAIL( "table and/or table information seems to be corrupted." );
987 // NOTE: nOldCol is not necessarily a valid index into
988 // GetColumns(), but that doesn't matter here
989 nCol = nOldCol;
992 const sal_uInt32 nColSpan = nCol - nOldCol + 1U;
994 if ( nRowSpan >= 1 )
995 ExportTableBox( *pBox, nColSpan, static_cast< sal_uInt32 >(nRowSpan), rTableInfo );
997 for( size_t i=nOldCol; i<nCol; ++i )
999 SvXMLElementExport aElemExport( *this, rTableInfo.GetPrefix(),
1000 XML_COVERED_TABLE_CELL, true,
1001 false );
1004 nCol++;
1009 void SwXMLExport::ExportTableLines( const SwTableLines& rLines,
1010 SwXMLTableInfo_Impl& rTableInfo,
1011 sal_uInt32 nHeaderRows )
1013 OSL_ENSURE( m_pTableLines && !m_pTableLines->empty(),
1014 "SwXMLExport::ExportTableLines: table columns infos missing" );
1015 if( !m_pTableLines || m_pTableLines->empty() )
1016 return;
1018 SwXMLTableLines_Impl* pLines = nullptr;
1019 size_t nInfoPos;
1020 for( nInfoPos=0; nInfoPos < m_pTableLines->size(); nInfoPos++ )
1022 if( m_pTableLines->at( nInfoPos )->GetLines() == &rLines )
1024 pLines = m_pTableLines->at( nInfoPos );
1025 break;
1028 OSL_ENSURE( pLines,
1029 "SwXMLExport::ExportTableLines: table columns info missing" );
1030 OSL_ENSURE( 0==nInfoPos,
1031 "SwXMLExport::ExportTableLines: table columns infos are unsorted" );
1032 if( !pLines )
1033 return;
1035 SwXMLTableLinesCache_Impl::iterator it = m_pTableLines->begin();
1036 advance( it, nInfoPos );
1037 m_pTableLines->erase( it );
1039 if( m_pTableLines->empty() )
1040 m_pTableLines.reset();
1042 // pass 2: export columns
1043 const SwXMLTableColumns_Impl& rCols = pLines->GetColumns();
1044 size_t nColumn = 0U;
1045 const size_t nColumns = rCols.size();
1046 sal_Int32 nColRep = 1;
1047 SwXMLTableColumn_Impl *pColumn = (nColumns > 0) ? rCols.front().get() : nullptr;
1048 while( pColumn )
1050 nColumn++;
1051 SwXMLTableColumn_Impl *pNextColumn =
1052 (nColumn < nColumns) ? rCols[nColumn].get() : nullptr;
1053 if( pNextColumn &&
1054 pNextColumn->GetStyleName() == pColumn->GetStyleName() )
1056 nColRep++;
1058 else
1060 AddAttribute( XML_NAMESPACE_TABLE, XML_STYLE_NAME,
1061 EncodeStyleName(pColumn->GetStyleName()) );
1063 if( nColRep > 1 )
1065 AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1066 OUString::number(nColRep) );
1070 SvXMLElementExport aElem( *this, rTableInfo.GetPrefix(), XML_TABLE_COLUMN, true, true );
1073 nColRep = 1;
1075 pColumn = pNextColumn;
1078 // pass 3: export line/rows
1079 const size_t nLines = rLines.size();
1080 // export header rows, if present
1081 if( nHeaderRows > 0 )
1083 SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE,
1084 XML_TABLE_HEADER_ROWS, true, true );
1086 OSL_ENSURE( nHeaderRows <= nLines, "more headers then lines?" );
1087 for( size_t nLine = 0U; nLine < nHeaderRows; ++nLine )
1088 ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
1090 // export remaining rows
1091 for( size_t nLine = nHeaderRows; nLine < nLines; ++nLine )
1093 ExportTableLine( *(rLines[nLine]), *pLines, rTableInfo );
1096 delete pLines;
1099 void SwXMLExport::ExportTable( const SwTableNode& rTableNd )
1101 ::std::optional<sal_uInt16> oPrefix = XML_NAMESPACE_TABLE;
1102 if (const SwFrameFormat* pFlyFormat = rTableNd.GetFlyFormat())
1104 if (SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
1106 // TODO ODF 1.4 OFFICE-3761
1107 if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
1109 oPrefix = XML_NAMESPACE_LO_EXT;
1111 else
1113 oPrefix.reset(); // no export to OASIS namespace yet
1118 if (!oPrefix)
1119 return;
1121 const SwTable& rTable = rTableNd.GetTable();
1122 const SwFrameFormat *pTableFormat = rTable.GetFrameFormat();
1123 if (pTableFormat && !pTableFormat->GetName().isEmpty())
1125 AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pTableFormat->GetName());
1126 AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME,
1127 EncodeStyleName(pTableFormat->GetName()));
1130 // table:template-name=
1131 if (!rTable.GetTableStyleName().isEmpty())
1133 ProgName sStyleName;
1134 SwStyleNameMapper::FillProgName(rTable.GetTableStyleName(), sStyleName, SwGetPoolIdFromName::TabStyle);
1135 AddAttribute(XML_NAMESPACE_TABLE, XML_TEMPLATE_NAME, sStyleName.toString());
1138 SvXMLElementExport aElem(*this, *oPrefix, XML_TABLE, true, true);
1140 // export DDE source (if this is a DDE table)
1141 if ( auto pSwDdeTable = dynamic_cast<const SwDDETable*>( &rTable) )
1143 // get DDE Field Type (contains the DDE connection)
1144 const SwDDEFieldType* pDDEFieldType = pSwDdeTable->GetDDEFieldType();
1146 // connection name
1147 AddAttribute( XML_NAMESPACE_OFFICE, XML_NAME,
1148 pDDEFieldType->GetName() );
1150 // DDE command
1151 const OUString& sCmd = pDDEFieldType->GetCmd();
1152 sal_Int32 nIdx{ 0 };
1153 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION,
1154 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1155 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_ITEM,
1156 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1157 AddAttribute( XML_NAMESPACE_OFFICE, XML_DDE_TOPIC,
1158 sCmd.getToken(0, sfx2::cTokenSeparator, nIdx) );
1160 // auto update
1161 if (pDDEFieldType->GetType() == SfxLinkUpdateMode::ALWAYS)
1163 AddAttribute( XML_NAMESPACE_OFFICE,
1164 XML_AUTOMATIC_UPDATE, XML_TRUE );
1167 // DDE source element (always empty)
1168 SvXMLElementExport aSource(*this, XML_NAMESPACE_OFFICE,
1169 XML_DDE_SOURCE, true, false);
1172 auto const& rFormats(static_cast<SwXMLTextParagraphExport const*>(GetTextParagraphExport().get())->GetTableFormats());
1173 auto const it(rFormats.find(&rTableNd));
1174 assert(it != rFormats.end());
1175 SwXMLTableInfo_Impl aTableInfo(&rTable, *oPrefix, it->second.first, it->second.second);
1176 ExportTableLines( rTable.GetTabLines(), aTableInfo, rTable.GetRowsToRepeat() );
1179 void SwXMLTextParagraphExport::exportTableAutoStyles() {
1180 // note: maTableNodes is used here only to keep the iteration order as before
1181 for (const auto* pTableNode : maTableNodes)
1183 static_cast<SwXMLExport&>(GetExport()).ExportTableAutoStyles(*pTableNode);
1187 void SwXMLTextParagraphExport::CollectTableLinesAutoStyles(const SwTableLines& rLines,
1188 SwFrameFormat& rFormat, bool _bProgress)
1190 // Follow SwXMLExport::ExportTableLines/ExportTableLine/ExportTableBox
1191 for (const SwTableLine* pLine : rLines)
1193 for (SwTableBox* pBox : pLine->GetTabBoxes())
1195 if (pBox->getRowSpan() <= 0)
1196 continue;
1197 if (pBox->GetSttNd())
1199 if (rtl::Reference<SwXCell> xCell = SwXCell::CreateXCell(&rFormat, pBox))
1200 exportText(xCell, true /*bAutoStyles*/, _bProgress, true /*bExportParagraph*/);
1202 else
1204 // no start node -> merged cells: export subtable in cell
1205 CollectTableLinesAutoStyles(pBox->GetTabLines(), rFormat, _bProgress);
1211 void SwXMLTextParagraphExport::exportTable(
1212 const Reference < XTextContent > & rTextContent,
1213 bool bAutoStyles, bool _bProgress )
1215 bool bOldShowProgress = static_cast<SwXMLExport&>(GetExport()).IsShowProgress();
1216 static_cast<SwXMLExport&>(GetExport()).SetShowProgress( _bProgress );
1218 Reference < XTextTable > xTextTable( rTextContent, UNO_QUERY );
1219 OSL_ENSURE( xTextTable.is(), "text table missing" );
1220 if( xTextTable.is() )
1222 SwXTextTable* pXTable = dynamic_cast<SwXTextTable*>(rTextContent.get());
1223 if( pXTable )
1225 SwFrameFormat *const pFormat = pXTable->GetFrameFormat();
1226 assert(pFormat && "table format missing");
1227 const SwTable *pTable = SwTable::FindTable( pFormat );
1228 assert(pTable && "table missing");
1229 const SwTableNode *pTableNd = pTable->GetTableNode();
1230 assert(pTableNd && "table node missing");
1231 if( bAutoStyles )
1233 // AUTOSTYLES: Optimization: Do not export table autostyle if
1234 // we are currently exporting the content.xml stuff and
1235 // the table is located in header/footer:
1236 // During the flat XML export (used e.g. by .sdw-export)
1237 // ALL flags are set at the same time.
1238 const bool bExportStyles = bool( GetExport().getExportFlags() & SvXMLExportFlags::STYLES );
1239 if (!isAutoStylesCollected()
1240 && (bExportStyles || !pFormat->GetDoc()->IsInHeaderFooter(*pTableNd)))
1242 maTableNodes.push_back(pTableNd);
1243 m_TableFormats.try_emplace(pTableNd);
1244 // Collect all tables inside cells of this table, too
1245 CollectTableLinesAutoStyles(pTable->GetTabLines(), *pFormat, _bProgress);
1248 else
1250 static_cast<SwXMLExport&>(GetExport()).ExportTable( *pTableNd );
1255 static_cast<SwXMLExport&>(GetExport()).SetShowProgress( bOldShowProgress );
1258 void SwXMLExport::DeleteTableLines()
1260 if ( m_pTableLines )
1262 for (SwXMLTableLines_Impl* p : *m_pTableLines)
1263 delete p;
1264 m_pTableLines->clear();
1265 m_pTableLines.reset();
1269 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */