1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 #include <vcl/svapp.hxx>
22 #include <svtools/htmlout.hxx>
23 #include <svtools/htmltokn.h>
24 #include <svtools/htmlkywd.hxx>
25 #include <svtools/HtmlWriter.hxx>
26 #include <editeng/ulspitem.hxx>
27 #include <editeng/lrspitem.hxx>
28 #include <editeng/brushitem.hxx>
29 #include <editeng/boxitem.hxx>
30 #include <fmtornt.hxx>
31 #include <frmfmt.hxx>
32 #include <fmtfsize.hxx>
33 #include <fmtsrnd.hxx>
34 #include <frmatr.hxx>
35 #include <doc.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <pam.hxx>
38 #include <ndtxt.hxx>
39 #include <swrect.hxx>
40 #include <cellatr.hxx>
41 #include <poolfmt.hxx>
42 #include <swtable.hxx>
43 #include <htmltbl.hxx>
44 #include "htmlnum.hxx"
45 #include "wrthtml.hxx"
46 #include <wrtswtbl.hxx>
47 #ifdef DBG_UTIL
48 #include <viewsh.hxx>
49 #include <viewopt.hxx>
50 #endif
51 #include <rtl/strbuf.hxx>
52 #include <sal/types.h>
53 #include <osl/diagnose.h>
54 
55 #define MAX_DEPTH (3)
56 
57 using namespace ::com::sun::star;
58 
59 namespace {
60 
61 class SwHTMLWrtTable : public SwWriteTable
62 {
63     static void Pixelize( sal_uInt16& rValue );
64     void PixelizeBorders();
65 
66     void OutTableCell( SwHTMLWriter& rWrt, const SwWriteTableCell *pCell,
67                        bool bOutVAlign ) const;
68 
69     void OutTableCells( SwHTMLWriter& rWrt,
70                         const SwWriteTableCells& rCells,
71                         const SvxBrushItem *pBrushItem ) const;
72 
73     virtual bool ShouldExpandSub( const SwTableBox *pBox,
74                             bool bExpandedBefore, sal_uInt16 nDepth ) const override;
75 
76     static bool HasTabBackground( const SwTableLine& rLine,
77                         bool bTop, bool bBottom, bool bLeft, bool bRight );
78     static bool HasTabBackground( const SwTableBox& rBox,
79                         bool bTop, bool bBottom, bool bLeft, bool bRight );
80 
81 public:
82     SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth, sal_uInt32 nBWidth,
83                     bool bRel, sal_uInt16 nLeftSub, sal_uInt16 nRightSub,
84                     sal_uInt16 nNumOfRowsToRepeat );
85     explicit SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo );
86 
87     void Write( SwHTMLWriter& rWrt, sal_Int16 eAlign=text::HoriOrientation::NONE,
88                 bool bTHead=false, const SwFrameFormat *pFrameFormat=nullptr,
89                 const OUString *pCaption=nullptr, bool bTopCaption=false,
90                 sal_uInt16 nHSpace=0, sal_uInt16 nVSpace=0 ) const;
91 };
92 
93 }
94 
SwHTMLWrtTable(const SwTableLines & rLines,tools::Long nWidth,sal_uInt32 nBWidth,bool bRel,sal_uInt16 nLSub,sal_uInt16 nRSub,sal_uInt16 nNumOfRowsToRepeat)95 SwHTMLWrtTable::SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth,
96                                 sal_uInt32 nBWidth, bool bRel,
97                                 sal_uInt16 nLSub, sal_uInt16 nRSub,
98                                 sal_uInt16 nNumOfRowsToRepeat )
99     : SwWriteTable(nullptr, rLines, nWidth, nBWidth, bRel, MAX_DEPTH, nLSub, nRSub, nNumOfRowsToRepeat)
100 {
101     PixelizeBorders();
102 }
103 
SwHTMLWrtTable(const SwHTMLTableLayout * pLayoutInfo)104 SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo )
105     : SwWriteTable(nullptr, pLayoutInfo)
106 {
107     // Adjust some Twip values to pixel limits
108     if( m_bCollectBorderWidth )
109         PixelizeBorders();
110 }
111 
Pixelize(sal_uInt16 & rValue)112 void SwHTMLWrtTable::Pixelize( sal_uInt16& rValue )
113 {
114     if( rValue && Application::GetDefaultDevice() )
115     {
116         Size aSz( rValue, 0 );
117         aSz = Application::GetDefaultDevice()->LogicToPixel( aSz, MapMode(MapUnit::MapTwip) );
118         if( !aSz.Width() )
119             aSz.setWidth( 1 );
120         aSz = Application::GetDefaultDevice()->PixelToLogic( aSz, MapMode(MapUnit::MapTwip) );
121         rValue = o3tl::narrowing<sal_uInt16>(aSz.Width());
122     }
123 }
124 
PixelizeBorders()125 void SwHTMLWrtTable::PixelizeBorders()
126 {
127     Pixelize( m_nBorder );
128     Pixelize( m_nCellSpacing );
129     Pixelize( m_nCellPadding );
130 }
131 
HasTabBackground(const SwTableBox & rBox,bool bTop,bool bBottom,bool bLeft,bool bRight)132 bool SwHTMLWrtTable::HasTabBackground( const SwTableBox& rBox,
133                         bool bTop, bool bBottom, bool bLeft, bool bRight )
134 {
135     OSL_ENSURE( bTop || bBottom || bLeft || bRight,
136             "HasTabBackground: cannot be called" );
137 
138     bool bRet = false;
139     if( rBox.GetSttNd() )
140     {
141         std::unique_ptr<SvxBrushItem> aBrushItem =
142             rBox.GetFrameFormat()->makeBackgroundBrushItem();
143 
144         /// The table box has a background, if its background color is not "no fill"/
145         /// "auto fill" or it has a background graphic.
146         bRet = aBrushItem && (aBrushItem->GetColor() != COL_TRANSPARENT ||
147                !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic());
148     }
149     else
150     {
151         const SwTableLines& rLines = rBox.GetTabLines();
152         const SwTableLines::size_type nCount = rLines.size();
153         bool bLeftRight = bLeft || bRight;
154         for( SwTableLines::size_type i=0; !bRet && i<nCount; ++i )
155         {
156             bool bT = bTop && 0 == i;
157             bool bB = bBottom && nCount-1 == i;
158             if( bT || bB || bLeftRight )
159                 bRet = HasTabBackground( *rLines[i], bT, bB, bLeft, bRight);
160         }
161     }
162 
163     return bRet;
164 }
165 
HasTabBackground(const SwTableLine & rLine,bool bTop,bool bBottom,bool bLeft,bool bRight)166 bool SwHTMLWrtTable::HasTabBackground( const SwTableLine& rLine,
167                         bool bTop, bool bBottom, bool bLeft, bool bRight )
168 {
169     OSL_ENSURE( bTop || bBottom || bLeft || bRight,
170             "HasTabBackground: cannot be called" );
171 
172     std::unique_ptr<SvxBrushItem> aBrushItem = rLine.GetFrameFormat()->makeBackgroundBrushItem();
173     /// The table line has a background, if its background color is not "no fill"/
174     /// "auto fill" or it has a background graphic.
175     bool bRet = aBrushItem && (aBrushItem->GetColor() != COL_TRANSPARENT ||
176            !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic());
177 
178     if( !bRet )
179     {
180         const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
181         const SwTableBoxes::size_type nCount = rBoxes.size();
182         bool bTopBottom = bTop || bBottom;
183         for( SwTableBoxes::size_type i=0; !bRet && i<nCount; ++i )
184         {
185             bool bL = bLeft && 0 == i;
186             bool bR = bRight && nCount-1 == i;
187             if( bTopBottom || bL || bR )
188                 bRet = HasTabBackground( *rBoxes[i], bTop, bBottom, bL, bR );
189         }
190     }
191 
192     return bRet;
193 }
194 
195 static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders );
196 
lcl_TableBox_HasTabBorders(const SwTableBox * pBox,bool * pBorders)197 static bool lcl_TableBox_HasTabBorders( const SwTableBox* pBox, bool *pBorders )
198 {
199     if( *pBorders )
200         return false;
201 
202     if( !pBox->GetSttNd() )
203     {
204         for( const auto& rpLine : pBox->GetTabLines() )
205         {
206             if ( lcl_TableLine_HasTabBorders( rpLine, pBorders ) )
207                 break;
208         }
209     }
210     else
211     {
212         const SvxBoxItem& rBoxItem =
213             pBox->GetFrameFormat()->GetFormatAttr( RES_BOX );
214 
215         *pBorders = rBoxItem.GetTop() || rBoxItem.GetBottom() ||
216                     rBoxItem.GetLeft() || rBoxItem.GetRight();
217     }
218 
219     return !*pBorders;
220 }
221 
lcl_TableLine_HasTabBorders(const SwTableLine * pLine,bool * pBorders)222 static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders )
223 {
224     if( *pBorders )
225         return false;
226 
227     for( const auto& rpBox : pLine->GetTabBoxes() )
228     {
229         if ( lcl_TableBox_HasTabBorders( rpBox, pBorders ) )
230             break;
231     }
232     return !*pBorders;
233 }
234 
ShouldExpandSub(const SwTableBox * pBox,bool bExpandedBefore,sal_uInt16 nDepth) const235 bool SwHTMLWrtTable::ShouldExpandSub( const SwTableBox *pBox,
236                                       bool bExpandedBefore,
237                                       sal_uInt16 nDepth ) const
238 {
239     bool bExpand = !pBox->GetSttNd() && nDepth>0;
240     if( bExpand && bExpandedBefore )
241     {
242         // MIB 30.6.97: If a box was already expanded, another one is only
243         // expanded when it has a border.
244         bool bBorders = false;
245         lcl_TableBox_HasTabBorders( pBox, &bBorders );
246         if( !bBorders )
247             bBorders = HasTabBackground( *pBox, true, true, true, true );
248         bExpand = bBorders;
249     }
250 
251     return bExpand;
252 }
253 
254 // Write a box as single cell
OutTableCell(SwHTMLWriter & rWrt,const SwWriteTableCell * pCell,bool bOutVAlign) const255 void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt,
256                                    const SwWriteTableCell *pCell,
257                                    bool bOutVAlign ) const
258 {
259     const SwTableBox *pBox = pCell->GetBox();
260     sal_uInt16 nRow = pCell->GetRow();
261     sal_uInt16 nCol = pCell->GetCol();
262     sal_uInt16 nRowSpan = pCell->GetRowSpan();
263     sal_uInt16 nColSpan = pCell->GetColSpan();
264 
265     if ( !nRowSpan )
266         return;
267 
268     const SwStartNode* pSttNd = pBox->GetSttNd();
269     bool bHead = false;
270     if( pSttNd )
271     {
272         sal_uLong nNdPos = pSttNd->GetIndex()+1;
273 
274         // determine the type of cell (TD/TH)
275         SwNode* pNd;
276         while( !( pNd = rWrt.m_pDoc->GetNodes()[nNdPos])->IsEndNode() )
277         {
278             if( pNd->IsTextNode() )
279             {
280                 // The only paragraphs relevant for the distinction are those
281                 // where the style is one of the two table related styles
282                 // or inherits from one of these.
283                 const SwFormat *pFormat = &static_cast<SwTextNode*>(pNd)->GetAnyFormatColl();
284                 sal_uInt16 nPoolId = pFormat->GetPoolFormatId();
285                 while( !pFormat->IsDefault() &&
286                        RES_POOLCOLL_TABLE_HDLN!=nPoolId &&
287                        RES_POOLCOLL_TABLE!=nPoolId )
288                 {
289                     pFormat = pFormat->DerivedFrom();
290                     nPoolId = pFormat->GetPoolFormatId();
291                 }
292 
293                 if( !pFormat->IsDefault() )
294                 {
295                     bHead = (RES_POOLCOLL_TABLE_HDLN==nPoolId);
296                     break;
297                 }
298             }
299             nNdPos++;
300         }
301     }
302 
303     rWrt.OutNewLine();  // <TH>/<TD> in new line
304     OStringBuffer sOut;
305     sOut.append('<');
306     OString aTag(bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata);
307     sOut.append(rWrt.GetNamespace() + aTag);
308 
309     // output ROW- and COLSPAN
310     if( nRowSpan>1 )
311     {
312         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_rowspan
313                 "=\"" + OString::number(nRowSpan) + "\"");
314     }
315     if( nColSpan > 1 )
316     {
317         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan
318                 "=\"" + OString::number(nColSpan) + "\"");
319     }
320 
321     tools::Long nWidth = 0;
322     bool bOutWidth = true;
323     sal_uInt32 nPercentWidth = SAL_MAX_UINT32;
324 
325     if( m_bLayoutExport )
326     {
327         if( pCell->HasPercentWidthOpt() )
328         {
329             nPercentWidth = pCell->GetWidthOpt();
330         }
331         else
332         {
333             nWidth = pCell->GetWidthOpt();
334             if( !nWidth )
335                 bOutWidth = false;
336         }
337     }
338     else
339     {
340         if( HasRelWidths() )
341             nPercentWidth = GetPercentWidth(nCol, nColSpan);
342         else
343             nWidth = GetAbsWidth( nCol, nColSpan );
344     }
345 
346     if (rWrt.mbReqIF)
347         // ReqIF implies strict XHTML: no width for <td>.
348         bOutWidth = false;
349 
350     tools::Long nHeight = pCell->GetHeight() > 0
351                         ? GetAbsHeight( pCell->GetHeight(), nRow, nRowSpan )
352                         : 0;
353     Size aPixelSz( nWidth, nHeight );
354 
355     // output WIDTH (Argh: only for Netscape)
356     if( (aPixelSz.Width() || aPixelSz.Height()) && Application::GetDefaultDevice() )
357     {
358         Size aOldSz( aPixelSz );
359         aPixelSz = Application::GetDefaultDevice()->LogicToPixel( aPixelSz,
360                                                         MapMode(MapUnit::MapTwip) );
361         if( aOldSz.Width() && !aPixelSz.Width() )
362             aPixelSz.setWidth( 1 );
363         if( aOldSz.Height() && !aPixelSz.Height() )
364             aPixelSz.setHeight( 1 );
365     }
366 
367     // output WIDTH: from layout or calculated
368     if( bOutWidth )
369     {
370         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
371         if( nPercentWidth != SAL_MAX_UINT32 )
372         {
373             sOut.append(static_cast<sal_Int32>(nPercentWidth)).append('%');
374         }
375         else
376         {
377             sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
378         }
379         sOut.append("\"");
380     }
381 
382     if (rWrt.mbReqIF)
383     {
384         // ReqIF implies strict XHTML: no height for <td>.
385         nHeight = 0;
386     }
387 
388     if( nHeight )
389     {
390         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height
391                 "=\"" + OString::number(aPixelSz.Height()) + "\"");
392     }
393 
394     const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet();
395     const SfxPoolItem *pItem;
396 
397     // ALIGN is only outputted at the paragraphs from now on
398 
399     // output VALIGN
400     if( bOutVAlign )
401     {
402         sal_Int16 eVertOri = pCell->GetVertOri();
403         if( text::VertOrientation::TOP==eVertOri || text::VertOrientation::BOTTOM==eVertOri )
404         {
405             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
406                     "=\"").append(text::VertOrientation::TOP==eVertOri ?
407                     OOO_STRING_SVTOOLS_HTML_VA_top :
408                     OOO_STRING_SVTOOLS_HTML_VA_bottom)
409                 .append("\"");
410         }
411     }
412 
413     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
414 
415     rWrt.m_bTextAttr = false;
416     rWrt.m_bOutOpts = true;
417     const SvxBrushItem *pBrushItem = nullptr;
418     if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
419     {
420         pBrushItem = static_cast<const SvxBrushItem *>(pItem);
421     }
422     if( !pBrushItem )
423         pBrushItem = pCell->GetBackground();
424 
425     if( pBrushItem )
426     {
427         // output background
428         if (!rWrt.mbReqIF)
429             // Avoid non-CSS version in the ReqIF case.
430             rWrt.OutBackground( pBrushItem, false );
431 
432         if (!rWrt.m_bCfgOutStyles)
433             pBrushItem = nullptr;
434     }
435 
436     // tdf#132739 with rWrt.m_bCfgOutStyles of true bundle the brush item css
437     // properties into the same "style" tag as the borders so there is only one
438     // style tag
439     rWrt.OutCSS1_TableCellBordersAndBG(*pBox->GetFrameFormat(), pBrushItem);
440 
441     sal_uInt32 nNumFormat = 0;
442     double nValue = 0.0;
443     bool bNumFormat = false, bValue = false;
444     if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_FORMAT, false, &pItem ) )
445     {
446         nNumFormat = static_cast<const SwTableBoxNumFormat *>(pItem)->GetValue();
447         bNumFormat = true;
448     }
449     if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_VALUE, false, &pItem ) )
450     {
451         nValue = static_cast<const SwTableBoxValue *>(pItem)->GetValue();
452         bValue = true;
453         if( !bNumFormat )
454             nNumFormat = pBox->GetFrameFormat()->GetTableBoxNumFormat().GetValue();
455     }
456 
457     if( bNumFormat || bValue )
458     {
459         sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValue, nValue,
460             nNumFormat, *rWrt.m_pDoc->GetNumberFormatter(), rWrt.m_eDestEnc,
461             &rWrt.m_aNonConvertableCharacters));
462     }
463     sOut.append('>');
464     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
465     rWrt.m_bLFPossible = true;
466 
467     rWrt.IncIndentLevel();  // indent the content of <TD>...</TD>
468 
469     if( pSttNd )
470     {
471         HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1,
472                                 pSttNd->EndOfSectionIndex() );
473         rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
474     }
475     else
476     {
477         sal_uInt16 nTWidth;
478         sal_uInt32 nBWidth;
479         sal_uInt16 nLSub, nRSub;
480         if( HasRelWidths() )
481         {
482             nTWidth = 100;
483             nBWidth = GetRawWidth( nCol, nColSpan );
484             nLSub = 0;
485             nRSub = 0;
486         }
487         else
488         {
489             nTWidth = GetAbsWidth( nCol, nColSpan );
490             nBWidth = nTWidth;
491             nLSub = GetLeftSpace( nCol );
492             nRSub = GetRightSpace( nCol, nColSpan );
493         }
494 
495         SwHTMLWrtTable aTableWrt( pBox->GetTabLines(), nTWidth,
496                                   nBWidth, HasRelWidths(), nLSub, nRSub, /*nNumOfRowsToRepeat*/0 );
497         aTableWrt.Write( rWrt );
498     }
499 
500     rWrt.DecIndentLevel();  // indent the content of <TD>...</TD>
501 
502     if( rWrt.m_bLFPossible )
503         rWrt.OutNewLine();
504     aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata;
505     HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag), false);
506     rWrt.m_bLFPossible = true;
507 }
508 
509 // output a line as lines
OutTableCells(SwHTMLWriter & rWrt,const SwWriteTableCells & rCells,const SvxBrushItem * pBrushItem) const510 void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt,
511                                     const SwWriteTableCells& rCells,
512                                     const SvxBrushItem *pBrushItem ) const
513 {
514     // If the line contains more the one cell and all cells have the same
515     // alignment, then output the VALIGN at the line instead of the cell.
516     sal_Int16 eRowVertOri = text::VertOrientation::NONE;
517     if( rCells.size() > 1 )
518     {
519         for (SwWriteTableCells::size_type nCell = 0; nCell < rCells.size(); ++nCell)
520         {
521             sal_Int16 eCellVertOri = rCells[nCell]->GetVertOri();
522             if( 0==nCell )
523             {
524                 eRowVertOri = eCellVertOri;
525             }
526             else if( eRowVertOri != eCellVertOri )
527             {
528                 eRowVertOri = text::VertOrientation::NONE;
529                 break;
530             }
531         }
532     }
533 
534     rWrt.OutNewLine();  // <TR> in new line
535     rWrt.Strm().WriteChar( '<' ).WriteOString( OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow) );
536     if( pBrushItem )
537     {
538         rWrt.OutBackground( pBrushItem, false );
539 
540         rWrt.m_bTextAttr = false;
541         rWrt.m_bOutOpts = true;
542         if( rWrt.m_bCfgOutStyles )
543             OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
544     }
545 
546     if( text::VertOrientation::TOP==eRowVertOri || text::VertOrientation::BOTTOM==eRowVertOri )
547     {
548         OStringBuffer sOut;
549         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
550                 "=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
551             .append("\"");
552         rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
553     }
554 
555     rWrt.Strm().WriteChar( '>' );
556 
557     rWrt.IncIndentLevel(); // indent content of <TR>...</TR>
558 
559     for (const auto &rpCell : rCells)
560     {
561         OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri);
562     }
563 
564     rWrt.DecIndentLevel(); // indent content of <TR>...</TR>
565 
566     rWrt.OutNewLine();  // </TR> in new line
567     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow), false );
568 }
569 
Write(SwHTMLWriter & rWrt,sal_Int16 eAlign,bool bTHead,const SwFrameFormat * pFrameFormat,const OUString * pCaption,bool bTopCaption,sal_uInt16 nHSpace,sal_uInt16 nVSpace) const570 void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
571                             bool bTHead, const SwFrameFormat *pFrameFormat,
572                             const OUString *pCaption, bool bTopCaption,
573                             sal_uInt16 nHSpace, sal_uInt16 nVSpace ) const
574 {
575     // determine value of RULES
576     bool bRowsHaveBorder = false;
577     bool bRowsHaveBorderOnly = true;
578     SwWriteTableRow *pRow = m_aRows[0].get();
579     for( SwWriteTableRows::size_type nRow=1; nRow < m_aRows.size(); ++nRow )
580     {
581         SwWriteTableRow *pNextRow = m_aRows[nRow].get();
582         bool bBorder = ( pRow->bBottomBorder || pNextRow->bTopBorder );
583         bRowsHaveBorder |= bBorder;
584         bRowsHaveBorderOnly &= bBorder;
585 
586         sal_uInt16 nBorder2 = pRow->bBottomBorder ? pRow->nBottomBorder : USHRT_MAX;
587         if( pNextRow->bTopBorder && pNextRow->nTopBorder < nBorder2 )
588             nBorder2 = pNextRow->nTopBorder;
589 
590         pRow->bBottomBorder = bBorder;
591         pRow->nBottomBorder = nBorder2;
592 
593         pNextRow->bTopBorder = bBorder;
594         pNextRow->nTopBorder = nBorder2;
595 
596         pRow = pNextRow;
597     }
598 
599     bool bColsHaveBorder = false;
600     bool bColsHaveBorderOnly = true;
601     SwWriteTableCol *pCol = m_aCols[0].get();
602     for( SwWriteTableCols::size_type nCol=1; nCol<m_aCols.size(); ++nCol )
603     {
604         SwWriteTableCol *pNextCol = m_aCols[nCol].get();
605         bool bBorder = ( pCol->bRightBorder || pNextCol->bLeftBorder );
606         bColsHaveBorder |= bBorder;
607         bColsHaveBorderOnly &= bBorder;
608         pCol->bRightBorder = bBorder;
609         pNextCol->bLeftBorder = bBorder;
610         pCol = pNextCol;
611     }
612 
613     // close previous numbering, etc
614     rWrt.ChangeParaToken( HtmlTokenId::NONE );
615 
616     if( rWrt.m_bLFPossible )
617         rWrt.OutNewLine();  // <TABLE> in new line
618     OStringBuffer sOut;
619     sOut.append('<').append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table);
620 
621     const SvxFrameDirection nOldDirection = rWrt.m_nDirection;
622     if( pFrameFormat )
623         rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
624     if( rWrt.m_bOutFlyFrame || nOldDirection != rWrt.m_nDirection )
625     {
626         rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
627         rWrt.OutDirection( rWrt.m_nDirection );
628     }
629 
630     // output ALIGN=
631     if( text::HoriOrientation::RIGHT == eAlign )
632     {
633         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
634                 "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"");
635     }
636     else if( text::HoriOrientation::CENTER == eAlign )
637     {
638         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
639                 "=\"" OOO_STRING_SVTOOLS_HTML_AL_center "\"");
640     }
641     else if( text::HoriOrientation::LEFT == eAlign )
642     {
643         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
644                 "=\"" OOO_STRING_SVTOOLS_HTML_AL_left "\"");
645     }
646 
647     // output WIDTH: from layout or calculated
648     if( m_nTabWidth )
649     {
650         sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
651         if( HasRelWidths() )
652             sOut.append(static_cast<sal_Int32>(m_nTabWidth)).append('%');
653         else if( Application::GetDefaultDevice() )
654         {
655             sal_Int32 nPixWidth = Application::GetDefaultDevice()->LogicToPixel(
656                         Size(m_nTabWidth,0), MapMode(MapUnit::MapTwip) ).Width();
657             if( !nPixWidth )
658                 nPixWidth = 1;
659 
660             sOut.append(nPixWidth);
661         }
662         else
663         {
664             OSL_ENSURE( Application::GetDefaultDevice(), "no Application-Window!?" );
665             sOut.append("100%");
666         }
667         sOut.append("\"");
668     }
669 
670     if( (nHSpace || nVSpace) && Application::GetDefaultDevice())
671     {
672         Size aPixelSpc =
673             Application::GetDefaultDevice()->LogicToPixel( Size(nHSpace,nVSpace),
674                                                    MapMode(MapUnit::MapTwip) );
675         if( !aPixelSpc.Width() && nHSpace )
676             aPixelSpc.setWidth( 1 );
677         if( !aPixelSpc.Height() && nVSpace )
678             aPixelSpc.setHeight( 1 );
679 
680         if( aPixelSpc.Width() )
681         {
682             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
683                     "=\"" + OString::number(aPixelSpc.Width()) + "\"");
684         }
685 
686         if( aPixelSpc.Height() )
687         {
688             sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
689                     "=\"" + OString::number(aPixelSpc.Height()) + "\"");
690         }
691     }
692 
693     // output CELLPADDING: from layout or calculated
694     sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellpadding
695             "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellPadding,false)) + "\"");
696 
697     // output CELLSPACING: from layout or calculated
698     sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
699             "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellSpacing,false)) + "\"");
700 
701     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
702 
703     // output background
704     if( pFrameFormat )
705     {
706         rWrt.OutBackground( pFrameFormat->GetAttrSet(), false );
707 
708         if (rWrt.m_bCfgOutStyles)
709             rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
710     }
711 
712     sOut.append('>');
713     rWrt.Strm().WriteOString( sOut.makeStringAndClear() );
714 
715     rWrt.IncIndentLevel(); // indent content of table
716 
717     // output caption
718     if( pCaption && !pCaption->isEmpty() )
719     {
720         rWrt.OutNewLine(); // <CAPTION> in new line
721         OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption);
722         sOutStr.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"")
723                .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
724                .append("\"");
725         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + sOutStr) );
726         HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption, rWrt.m_eDestEnc, &rWrt.m_aNonConvertableCharacters );
727         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption), false );
728     }
729 
730     const SwWriteTableCols::size_type nCols = m_aCols.size();
731 
732     // output <COLGRP>/<COL>: If exporting via layout only when during import
733     // some were there, otherwise always.
734     bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly);
735     if( m_bColTags )
736     {
737         if( bColGroups )
738         {
739             rWrt.OutNewLine(); // <COLGRP> in new line
740             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
741 
742             rWrt.IncIndentLevel(); // indent content of <COLGRP>
743         }
744 
745         for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol )
746         {
747             rWrt.OutNewLine(); // </COL> in new line
748 
749             const SwWriteTableCol *pColumn = m_aCols[nCol].get();
750 
751             HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
752             html.start(OOO_STRING_SVTOOLS_HTML_col);
753 
754             sal_uInt32 nWidth;
755             bool bRel;
756             if( m_bLayoutExport )
757             {
758                 bRel = pColumn->HasRelWidthOpt();
759                 nWidth = pColumn->GetWidthOpt();
760             }
761             else
762             {
763                 bRel = HasRelWidths();
764                 nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1);
765             }
766 
767             if( bRel )
768                 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString(OString::number(nWidth) + "*"));
769             else
770                 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth,false)));
771             html.end();
772 
773             if( bColGroups && pColumn->bRightBorder && nCol<nCols-1 )
774             {
775                 rWrt.DecIndentLevel(); // indent content of <COLGRP>
776                 rWrt.OutNewLine(); // </COLGRP> in new line
777                 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
778                                             false );
779                 rWrt.OutNewLine(); // <COLGRP> in new line
780                 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
781                 rWrt.IncIndentLevel(); // indent content of <COLGRP>
782             }
783         }
784         if( bColGroups )
785         {
786             rWrt.DecIndentLevel(); // indent content of <COLGRP>
787 
788             rWrt.OutNewLine(); // </COLGRP> in new line
789             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
790                                         false );
791         }
792     }
793 
794     // output the lines as table lines
795 
796     // Output <TBODY>?
797     bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly);
798     bool bTBody = bTSections;
799 
800     // If sections must be outputted, then a THEAD around the first line only
801     // can be outputted if there is a line below the cell.
802     if( bTHead &&
803         (bTSections || bColGroups) &&
804         m_nHeadEndRow<m_aRows.size()-1 && !m_aRows[m_nHeadEndRow]->bBottomBorder )
805         bTHead = false;
806 
807     // Output <TBODY> only if <THEAD> is outputted.
808     bTSections |= bTHead;
809 
810     if( bTSections )
811     {
812         rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
813         OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
814         HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag));
815 
816         rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
817     }
818 
819     for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow )
820     {
821         const SwWriteTableRow *pRow2 = m_aRows[nRow].get();
822 
823         OutTableCells( rWrt, pRow2->GetCells(), pRow2->GetBackground() );
824         if( !m_nCellSpacing && nRow < m_aRows.size()-1 && pRow2->bBottomBorder &&
825             pRow2->nBottomBorder > DEF_LINE_WIDTH_1 )
826         {
827             for( auto nCnt = (pRow2->nBottomBorder / DEF_LINE_WIDTH_1) - 1; nCnt; --nCnt )
828             {
829                 rWrt.OutNewLine();
830                 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow ));
831                 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow),
832                                             false );
833             }
834         }
835         if( ( (bTHead && nRow==m_nHeadEndRow) ||
836               (bTBody && pRow2->bBottomBorder) ) &&
837             nRow < m_aRows.size()-1 )
838         {
839             rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
840             rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
841             OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
842             HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag), false);
843             rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
844 
845             if( bTHead && nRow==m_nHeadEndRow )
846                 bTHead = false;
847 
848             aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
849             HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag));
850             rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
851         }
852     }
853 
854     if( bTSections )
855     {
856         rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
857 
858         rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
859         OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
860         HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag), false);
861     }
862 
863     rWrt.DecIndentLevel(); // indent content of <TABLE>
864 
865     rWrt.OutNewLine(); // </TABLE> in new line
866     HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table), false );
867 
868     rWrt.m_nDirection = nOldDirection;
869 }
870 
OutHTML_SwTableNode(Writer & rWrt,SwTableNode & rNode,const SwFrameFormat * pFlyFrameFormat,const OUString * pCaption,bool bTopCaption)871 Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode,
872                            const SwFrameFormat *pFlyFrameFormat,
873                            const OUString *pCaption, bool bTopCaption )
874 {
875 
876     SwTable& rTable = rNode.GetTable();
877 
878     SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
879     rHTMLWrt.m_bOutTable = true;
880 
881     // The horizontal alignment of the frame (if exists) has priority.
882     // NONE means that no horizontal alignment was outputted.
883     sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE;
884     css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
885     sal_uInt8 nFlyPercentWidth = 0;
886     tools::Long nFlyWidth = 0;
887     sal_uInt16 nFlyHSpace = 0;
888     sal_uInt16 nFlyVSpace = 0;
889     if( pFlyFrameFormat )
890     {
891         eSurround = pFlyFrameFormat->GetSurround().GetSurround();
892         const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize();
893         nFlyPercentWidth = rFrameSize.GetWidthPercent();
894         nFlyWidth = rFrameSize.GetSize().Width();
895 
896         eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient();
897         if( text::HoriOrientation::NONE == eFlyHoriOri )
898             eFlyHoriOri = text::HoriOrientation::LEFT;
899 
900         const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace();
901         nFlyHSpace = static_cast< sal_uInt16 >((rLRSpace.GetLeft() + rLRSpace.GetRight()) / 2);
902 
903         const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace();
904         nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2;
905     }
906 
907     // maybe open a FORM
908     bool bPreserveForm = false;
909     if( !rHTMLWrt.m_bPreserveForm )
910     {
911         rHTMLWrt.OutForm( true, &rNode );
912         bPreserveForm = rHTMLWrt.mxFormComps.is();
913         rHTMLWrt.m_bPreserveForm = bPreserveForm;
914     }
915 
916     SwFrameFormat *pFormat = rTable.GetFrameFormat();
917 
918     const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
919     tools::Long nWidth = rFrameSize.GetSize().Width();
920     sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent();
921     sal_uInt16 nBaseWidth = o3tl::narrowing<sal_uInt16>(nWidth);
922 
923     sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient();
924 
925     // text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths
926     sal_uInt16 nNewDefListLvl = 0;
927     bool bRelWidths = false;
928     bool bCheckDefList = false;
929     switch( eTabHoriOri )
930     {
931     case text::HoriOrientation::FULL:
932         // Tables with automatic alignment become tables with 100% width.
933         bRelWidths = true;
934         nWidth = 100;
935         eTabHoriOri = text::HoriOrientation::LEFT;
936         break;
937     case text::HoriOrientation::NONE:
938         {
939             const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
940             if( aLRItem.GetRight() )
941             {
942                 // The table width is defined on the basis of the left and
943                 // right margin. Therefore we try to define the actual
944                 // width of the table. If that's not possible we transform
945                 // it to a table with width 100%.
946                 nWidth = pFormat->FindLayoutRect(true).Width();
947                 if( !nWidth )
948                 {
949                     bRelWidths = true;
950                     nWidth = 100;
951                 }
952 
953             }
954             else if( nPercentWidth  )
955             {
956                 // Without a right border the %-width is maintained.
957                 nWidth = nPercentWidth;
958                 bRelWidths = true;
959             }
960             else
961             {
962                 // Without a right margin also an absolute width is maintained.
963                 // We still try to define the actual width via the layout.
964                 tools::Long nRealWidth = pFormat->FindLayoutRect(true).Width();
965                 if( nRealWidth )
966                     nWidth = nRealWidth;
967             }
968             bCheckDefList = true;
969         }
970         break;
971     case text::HoriOrientation::LEFT_AND_WIDTH:
972         eTabHoriOri = text::HoriOrientation::LEFT;
973         bCheckDefList = true;
974         [[fallthrough]];
975     default:
976         // In all other case it's possible to use directly an absolute
977         // or relative width.
978         if( nPercentWidth )
979         {
980             bRelWidths = true;
981             nWidth = nPercentWidth;
982         }
983         break;
984     }
985 
986     if( bCheckDefList )
987     {
988         OSL_ENSURE( !rHTMLWrt.GetNumInfo().GetNumRule() ||
989                 rHTMLWrt.GetNextNumInfo(),
990                 "NumInfo for next paragraph is missing!" );
991         const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
992         if( aLRItem.GetLeft() > 0 && rHTMLWrt.m_nDefListMargin > 0 &&
993             ( !rHTMLWrt.GetNumInfo().GetNumRule() ||
994               ( rHTMLWrt.GetNextNumInfo() &&
995                 (rHTMLWrt.GetNextNumInfo()->IsRestart() ||
996                  rHTMLWrt.GetNumInfo().GetNumRule() !=
997                     rHTMLWrt.GetNextNumInfo()->GetNumRule()) ) ) )
998         {
999             // If the paragraph before the table is not numbered or the
1000             // paragraph after the table starts with a new numbering or with
1001             // a different rule, we can maintain the indentation with a DL.
1002             // Otherwise we keep the indentation of the numbering.
1003             nNewDefListLvl = static_cast< sal_uInt16 >(
1004                 (aLRItem.GetLeft() + (rHTMLWrt.m_nDefListMargin/2)) /
1005                 rHTMLWrt.m_nDefListMargin );
1006         }
1007     }
1008 
1009     if( !pFlyFrameFormat && nNewDefListLvl != rHTMLWrt.m_nDefListLvl )
1010         rHTMLWrt.OutAndSetDefList( nNewDefListLvl );
1011 
1012     if( nNewDefListLvl )
1013     {
1014         if( rHTMLWrt.m_bLFPossible )
1015             rHTMLWrt.OutNewLine();
1016         HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd) );
1017     }
1018 
1019     // eFlyHoriOri and eTabHoriOri now only contain the values of
1020     // LEFT/CENTER and RIGHT!
1021     if( eFlyHoriOri!=text::HoriOrientation::NONE )
1022     {
1023         eTabHoriOri = eFlyHoriOri;
1024         // MIB 4.7.97: If the table has a relative width, then the width is
1025         // adjusted to the width of the frame, therefore we export its width.
1026         // If fixed width, the table width is relevant. Whoever puts tables with
1027         // relative width <100% into frames is to blame when the result looks bad.
1028         if( bRelWidths )
1029         {
1030             nWidth = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth;
1031             bRelWidths = nFlyPercentWidth > 0;
1032         }
1033     }
1034 
1035     sal_Int16 eDivHoriOri = text::HoriOrientation::NONE;
1036     switch( eTabHoriOri )
1037     {
1038     case text::HoriOrientation::LEFT:
1039         // If a left-aligned table has no right sided flow, then we don't need
1040         // an ALIGN=LEFT in the table.
1041         if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT )
1042             eTabHoriOri = text::HoriOrientation::NONE;
1043         break;
1044     case text::HoriOrientation::RIGHT:
1045         // Something like that also applies to right-aligned tables,
1046         // here we use a <DIV ALIGN=RIGHT> instead.
1047         if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT )
1048         {
1049             eDivHoriOri = text::HoriOrientation::RIGHT;
1050             eTabHoriOri = text::HoriOrientation::NONE;
1051         }
1052         break;
1053     case text::HoriOrientation::CENTER:
1054         // Almost nobody understands ALIGN=CENTER, therefore we abstain
1055         // from it and use a <CENTER>.
1056         eDivHoriOri = text::HoriOrientation::CENTER;
1057         eTabHoriOri = text::HoriOrientation::NONE;
1058         break;
1059     default:
1060         ;
1061     }
1062     if( text::HoriOrientation::NONE==eTabHoriOri )
1063         nFlyHSpace = nFlyVSpace = 0;
1064 
1065     if( !pFormat->GetName().isEmpty() )
1066         rHTMLWrt.OutImplicitMark( pFormat->GetName(), "table" );
1067 
1068     if( text::HoriOrientation::NONE!=eDivHoriOri )
1069     {
1070         if( rHTMLWrt.m_bLFPossible )
1071             rHTMLWrt.OutNewLine();  // <CENTER> in new line
1072         if( text::HoriOrientation::CENTER==eDivHoriOri )
1073             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center) );
1074         else
1075         {
1076             OStringLiteral sOut = OOO_STRING_SVTOOLS_HTML_division
1077                 " " OOO_STRING_SVTOOLS_HTML_O_align "=\""
1078                 OOO_STRING_SVTOOLS_HTML_AL_right "\"";
1079             HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + sOut) );
1080         }
1081         rHTMLWrt.IncIndentLevel();  // indent content of <CENTER>
1082         rHTMLWrt.m_bLFPossible = true;
1083     }
1084 
1085     // If the table isn't in a frame, then you always can output a LF.
1086     if( text::HoriOrientation::NONE==eTabHoriOri )
1087         rHTMLWrt.m_bLFPossible = true;
1088 
1089     const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout();
1090 
1091 #ifdef DBG_UTIL
1092     {
1093     SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
1094     if ( pSh && pSh->GetViewOptions()->IsTest1() )
1095         pLayout = nullptr;
1096     }
1097 #endif
1098 
1099     if( pLayout && pLayout->IsExportable() )
1100     {
1101         SwHTMLWrtTable aTableWrt( pLayout );
1102         aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1103                          pFormat, pCaption, bTopCaption,
1104                          nFlyHSpace, nFlyVSpace );
1105     }
1106     else
1107     {
1108         SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth,
1109                                   nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() );
1110         aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1111                          pFormat, pCaption, bTopCaption,
1112                          nFlyHSpace, nFlyVSpace );
1113     }
1114 
1115     // If the table wasn't in a frame, then you always can output a LF.
1116     if( text::HoriOrientation::NONE==eTabHoriOri )
1117         rHTMLWrt.m_bLFPossible = true;
1118 
1119     if( text::HoriOrientation::NONE!=eDivHoriOri )
1120     {
1121         rHTMLWrt.DecIndentLevel();  // indent content of <CENTER>
1122         rHTMLWrt.OutNewLine();      // </CENTER> in new line
1123         OString aTag = text::HoriOrientation::CENTER == eDivHoriOri
1124                            ? OOO_STRING_SVTOOLS_HTML_center
1125                            : OOO_STRING_SVTOOLS_HTML_division;
1126         HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + aTag), false);
1127         rHTMLWrt.m_bLFPossible = true;
1128     }
1129 
1130     // move Pam behind the table
1131     rHTMLWrt.m_pCurrentPam->GetPoint()->nNode = *rNode.EndOfSectionNode();
1132 
1133     if( bPreserveForm )
1134     {
1135         rHTMLWrt.m_bPreserveForm = false;
1136         rHTMLWrt.OutForm( false );
1137     }
1138 
1139     rHTMLWrt.m_bOutTable = false;
1140 
1141     if( rHTMLWrt.GetNextNumInfo() &&
1142         !rHTMLWrt.GetNextNumInfo()->IsRestart() &&
1143         rHTMLWrt.GetNextNumInfo()->GetNumRule() ==
1144             rHTMLWrt.GetNumInfo().GetNumRule() )
1145     {
1146         // If the paragraph after the table is numbered with the same rule as the
1147         // one before, then the NumInfo of the next paragraph holds the level of
1148         // paragraph before the table. Therefore NumInfo must be fetched again
1149         // to maybe close the Num list.
1150         rHTMLWrt.ClearNextNumInfo();
1151         rHTMLWrt.FillNextNumInfo();
1152         OutHTML_NumberBulletListEnd( rHTMLWrt, *rHTMLWrt.GetNextNumInfo() );
1153     }
1154     return rWrt;
1155 }
1156 
1157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1158