android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / html / htmltabw.cxx
blob8461758aa87bbe9a4c1494343ab966b091366958
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
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>
55 #define MAX_DEPTH (3)
57 using namespace ::com::sun::star;
59 namespace {
61 class SwHTMLWrtTable : public SwWriteTable
63 static void Pixelize( sal_uInt16& rValue );
64 void PixelizeBorders();
66 /// Writes a single table cell.
67 ///
68 /// bCellRowSpan decides if the cell's row span should be written or not.
69 void OutTableCell( SwHTMLWriter& rWrt, const SwWriteTableCell *pCell,
70 bool bOutVAlign,
71 bool bCellRowSpan ) const;
73 /// Writes a single table row.
74 ///
75 /// rSkipRows decides if the next N rows should be skipped or written.
76 void OutTableCells( SwHTMLWriter& rWrt,
77 const SwWriteTableCells& rCells,
78 const SvxBrushItem *pBrushItem,
79 sal_uInt16& rSkipRows ) const;
81 virtual bool ShouldExpandSub( const SwTableBox *pBox,
82 bool bExpandedBefore, sal_uInt16 nDepth ) const override;
84 static bool HasTabBackground( const SwTableLine& rLine,
85 bool bTop, bool bBottom, bool bLeft, bool bRight );
86 static bool HasTabBackground( const SwTableBox& rBox,
87 bool bTop, bool bBottom, bool bLeft, bool bRight );
89 public:
90 SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth, sal_uInt32 nBWidth,
91 bool bRel, sal_uInt16 nLeftSub, sal_uInt16 nRightSub,
92 sal_uInt16 nNumOfRowsToRepeat );
93 explicit SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo );
95 void Write( SwHTMLWriter& rWrt, sal_Int16 eAlign=text::HoriOrientation::NONE,
96 bool bTHead=false, const SwFrameFormat *pFrameFormat=nullptr,
97 const OUString *pCaption=nullptr, bool bTopCaption=false,
98 sal_uInt16 nHSpace=0, sal_uInt16 nVSpace=0 ) const;
103 SwHTMLWrtTable::SwHTMLWrtTable( const SwTableLines& rLines, tools::Long nWidth,
104 sal_uInt32 nBWidth, bool bRel,
105 sal_uInt16 nLSub, sal_uInt16 nRSub,
106 sal_uInt16 nNumOfRowsToRepeat )
107 : SwWriteTable(nullptr, rLines, nWidth, nBWidth, bRel, MAX_DEPTH, nLSub, nRSub, nNumOfRowsToRepeat)
109 PixelizeBorders();
112 SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo )
113 : SwWriteTable(nullptr, pLayoutInfo)
115 // Adjust some Twip values to pixel limits
116 if( m_bCollectBorderWidth )
117 PixelizeBorders();
120 void SwHTMLWrtTable::Pixelize( sal_uInt16& rValue )
122 if( rValue )
124 rValue = o3tl::convert(SwHTMLWriter::ToPixel(rValue), o3tl::Length::px, o3tl::Length::twip);
128 void SwHTMLWrtTable::PixelizeBorders()
130 Pixelize( m_nBorder );
131 Pixelize( m_nCellSpacing );
132 Pixelize( m_nCellPadding );
135 bool SwHTMLWrtTable::HasTabBackground( const SwTableBox& rBox,
136 bool bTop, bool bBottom, bool bLeft, bool bRight )
138 OSL_ENSURE( bTop || bBottom || bLeft || bRight,
139 "HasTabBackground: cannot be called" );
141 bool bRet = false;
142 if( rBox.GetSttNd() )
144 std::unique_ptr<SvxBrushItem> aBrushItem =
145 rBox.GetFrameFormat()->makeBackgroundBrushItem();
147 /// The table box has a background, if its background color is not "no fill"/
148 /// "auto fill" or it has a background graphic.
149 bRet = aBrushItem->GetColor() != COL_TRANSPARENT ||
150 !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic();
152 else
154 const SwTableLines& rLines = rBox.GetTabLines();
155 const SwTableLines::size_type nCount = rLines.size();
156 bool bLeftRight = bLeft || bRight;
157 for( SwTableLines::size_type i=0; !bRet && i<nCount; ++i )
159 bool bT = bTop && 0 == i;
160 bool bB = bBottom && nCount-1 == i;
161 if( bT || bB || bLeftRight )
162 bRet = HasTabBackground( *rLines[i], bT, bB, bLeft, bRight);
166 return bRet;
169 bool SwHTMLWrtTable::HasTabBackground( const SwTableLine& rLine,
170 bool bTop, bool bBottom, bool bLeft, bool bRight )
172 OSL_ENSURE( bTop || bBottom || bLeft || bRight,
173 "HasTabBackground: cannot be called" );
175 std::unique_ptr<SvxBrushItem> aBrushItem = rLine.GetFrameFormat()->makeBackgroundBrushItem();
176 /// The table line has a background, if its background color is not "no fill"/
177 /// "auto fill" or it has a background graphic.
178 bool bRet = aBrushItem->GetColor() != COL_TRANSPARENT ||
179 !aBrushItem->GetGraphicLink().isEmpty() || aBrushItem->GetGraphic();
181 if( !bRet )
183 const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
184 const SwTableBoxes::size_type nCount = rBoxes.size();
185 bool bTopBottom = bTop || bBottom;
186 for( SwTableBoxes::size_type i=0; !bRet && i<nCount; ++i )
188 bool bL = bLeft && 0 == i;
189 bool bR = bRight && nCount-1 == i;
190 if( bTopBottom || bL || bR )
191 bRet = HasTabBackground( *rBoxes[i], bTop, bBottom, bL, bR );
195 return bRet;
198 static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders );
200 static bool lcl_TableBox_HasTabBorders( const SwTableBox* pBox, bool *pBorders )
202 if( *pBorders )
203 return false;
205 if( !pBox->GetSttNd() )
207 for( const auto& rpLine : pBox->GetTabLines() )
209 if ( lcl_TableLine_HasTabBorders( rpLine, pBorders ) )
210 break;
213 else
215 const SvxBoxItem& rBoxItem =
216 pBox->GetFrameFormat()->GetFormatAttr( RES_BOX );
218 *pBorders = rBoxItem.GetTop() || rBoxItem.GetBottom() ||
219 rBoxItem.GetLeft() || rBoxItem.GetRight();
222 return !*pBorders;
225 static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders )
227 if( *pBorders )
228 return false;
230 for( const auto& rpBox : pLine->GetTabBoxes() )
232 if ( lcl_TableBox_HasTabBorders( rpBox, pBorders ) )
233 break;
235 return !*pBorders;
238 bool SwHTMLWrtTable::ShouldExpandSub( const SwTableBox *pBox,
239 bool bExpandedBefore,
240 sal_uInt16 nDepth ) const
242 bool bExpand = !pBox->GetSttNd() && nDepth>0;
243 if( bExpand && bExpandedBefore )
245 // MIB 30.6.97: If a box was already expanded, another one is only
246 // expanded when it has a border.
247 bool bBorders = false;
248 lcl_TableBox_HasTabBorders( pBox, &bBorders );
249 if( !bBorders )
250 bBorders = HasTabBackground( *pBox, true, true, true, true );
251 bExpand = bBorders;
254 return bExpand;
257 // Write a box as single cell
258 void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt,
259 const SwWriteTableCell *pCell,
260 bool bOutVAlign,
261 bool bCellRowSpan ) const
263 const SwTableBox *pBox = pCell->GetBox();
264 sal_uInt16 nRow = pCell->GetRow();
265 sal_uInt16 nCol = pCell->GetCol();
266 sal_uInt16 nRowSpan = pCell->GetRowSpan();
267 sal_uInt16 nColSpan = pCell->GetColSpan();
269 if ( !nRowSpan )
270 return;
272 const SwStartNode* pSttNd = pBox->GetSttNd();
273 bool bHead = false;
274 if( pSttNd )
276 SwNodeOffset nNdPos = pSttNd->GetIndex()+1;
278 // determine the type of cell (TD/TH)
279 SwNode* pNd;
280 while( !( pNd = rWrt.m_pDoc->GetNodes()[nNdPos])->IsEndNode() )
282 if( pNd->IsTextNode() )
284 // The only paragraphs relevant for the distinction are those
285 // where the style is one of the two table related styles
286 // or inherits from one of these.
287 const SwFormat *pFormat = &static_cast<SwTextNode*>(pNd)->GetAnyFormatColl();
288 sal_uInt16 nPoolId = pFormat->GetPoolFormatId();
289 while( !pFormat->IsDefault() &&
290 RES_POOLCOLL_TABLE_HDLN!=nPoolId &&
291 RES_POOLCOLL_TABLE!=nPoolId )
293 pFormat = pFormat->DerivedFrom();
294 nPoolId = pFormat->GetPoolFormatId();
297 if( !pFormat->IsDefault() )
299 bHead = (RES_POOLCOLL_TABLE_HDLN==nPoolId);
300 break;
303 nNdPos++;
307 rWrt.OutNewLine(); // <TH>/<TD> in new line
308 OStringBuffer sOut("<");
309 OString aTag(bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata);
310 sOut.append(rWrt.GetNamespace() + aTag);
312 // output ROW- and COLSPAN
313 if (nRowSpan > 1 && bCellRowSpan)
315 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_rowspan
316 "=\"" + OString::number(nRowSpan) + "\"");
318 if( nColSpan > 1 )
320 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan
321 "=\"" + OString::number(nColSpan) + "\"");
324 tools::Long nWidth = 0;
325 bool bOutWidth = true;
326 sal_uInt32 nPercentWidth = SAL_MAX_UINT32;
328 if( m_bLayoutExport )
330 if( pCell->HasPercentWidthOpt() )
332 nPercentWidth = pCell->GetWidthOpt();
334 else
336 nWidth = pCell->GetWidthOpt();
337 if( !nWidth )
338 bOutWidth = false;
341 else
343 if( HasRelWidths() )
344 nPercentWidth = GetPercentWidth(nCol, nColSpan);
345 else
346 nWidth = GetAbsWidth( nCol, nColSpan );
349 if (rWrt.mbReqIF)
350 // ReqIF implies strict XHTML: no width for <td>.
351 bOutWidth = false;
353 tools::Long nHeight = pCell->GetHeight() > 0
354 ? GetAbsHeight( pCell->GetHeight(), nRow, nRowSpan )
355 : 0;
356 Size aPixelSz(SwHTMLWriter::ToPixel(nWidth), SwHTMLWriter::ToPixel(nHeight));
358 // output WIDTH: from layout or calculated
359 if( bOutWidth )
361 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
362 if( nPercentWidth != SAL_MAX_UINT32 )
364 sOut.append(OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%");
366 else
368 sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
370 sOut.append("\"");
373 if (rWrt.mbReqIF)
375 // ReqIF implies strict XHTML: no height for <td>.
376 nHeight = 0;
379 if( nHeight )
381 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height
382 "=\"" + OString::number(aPixelSz.Height()) + "\"");
385 const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet();
387 // ALIGN is only outputted at the paragraphs from now on
389 // output VALIGN
390 if( bOutVAlign )
392 sal_Int16 eVertOri = pCell->GetVertOri();
393 if( text::VertOrientation::TOP==eVertOri || text::VertOrientation::BOTTOM==eVertOri )
395 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
396 "=\"").append(text::VertOrientation::TOP==eVertOri ?
397 OOO_STRING_SVTOOLS_HTML_VA_top :
398 OOO_STRING_SVTOOLS_HTML_VA_bottom)
399 .append("\"");
403 rWrt.Strm().WriteOString( sOut );
404 sOut.setLength(0);
406 rWrt.m_bTextAttr = false;
407 rWrt.m_bOutOpts = true;
408 const SvxBrushItem *pBrushItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false );
409 if( !pBrushItem )
410 pBrushItem = pCell->GetBackground();
412 if( pBrushItem )
414 // output background
415 if (!rWrt.mbReqIF)
416 // Avoid non-CSS version in the ReqIF case.
417 rWrt.OutBackground( pBrushItem, false );
419 if (!rWrt.m_bCfgOutStyles)
420 pBrushItem = nullptr;
423 // tdf#132739 with rWrt.m_bCfgOutStyles of true bundle the brush item css
424 // properties into the same "style" tag as the borders so there is only one
425 // style tag
426 rWrt.OutCSS1_TableCellBordersAndBG(*pBox->GetFrameFormat(), pBrushItem);
428 sal_uInt32 nNumFormat = 0;
429 double nValue = 0.0;
430 bool bNumFormat = false, bValue = false;
431 if( const SwTableBoxNumFormat* pItem = rItemSet.GetItemIfSet( RES_BOXATR_FORMAT, false ) )
433 nNumFormat = pItem->GetValue();
434 bNumFormat = true;
436 if( const SwTableBoxValue* pItem = rItemSet.GetItemIfSet( RES_BOXATR_VALUE, false ) )
438 nValue = pItem->GetValue();
439 bValue = true;
440 if( !bNumFormat )
441 nNumFormat = pBox->GetFrameFormat()->GetTableBoxNumFormat().GetValue();
444 if ((bNumFormat || bValue) && !rWrt.mbXHTML)
446 sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValue, nValue,
447 nNumFormat, *rWrt.m_pDoc->GetNumberFormatter()));
449 sOut.append('>');
450 rWrt.Strm().WriteOString( sOut );
451 sOut.setLength(0);
452 rWrt.m_bLFPossible = true;
454 rWrt.IncIndentLevel(); // indent the content of <TD>...</TD>
456 if( pSttNd )
458 HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1,
459 pSttNd->EndOfSectionIndex() );
460 rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
462 else
464 sal_uInt16 nTWidth;
465 sal_uInt32 nBWidth;
466 sal_uInt16 nLSub, nRSub;
467 if( HasRelWidths() )
469 nTWidth = 100;
470 nBWidth = GetRawWidth( nCol, nColSpan );
471 nLSub = 0;
472 nRSub = 0;
474 else
476 nTWidth = GetAbsWidth( nCol, nColSpan );
477 nBWidth = nTWidth;
478 nLSub = GetLeftSpace( nCol );
479 nRSub = GetRightSpace( nCol, nColSpan );
482 SwHTMLWrtTable aTableWrt( pBox->GetTabLines(), nTWidth,
483 nBWidth, HasRelWidths(), nLSub, nRSub, /*nNumOfRowsToRepeat*/0 );
484 aTableWrt.Write( rWrt );
487 rWrt.DecIndentLevel(); // indent the content of <TD>...</TD>
489 if( rWrt.m_bLFPossible )
490 rWrt.OutNewLine();
491 aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata;
492 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
493 rWrt.m_bLFPossible = true;
496 // output a line as lines
497 void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt,
498 const SwWriteTableCells& rCells,
499 const SvxBrushItem *pBrushItem,
500 sal_uInt16& rSkipRows ) const
502 // If the line contains more the one cell and all cells have the same
503 // alignment, then output the VALIGN at the line instead of the cell.
504 sal_Int16 eRowVertOri = text::VertOrientation::NONE;
505 if( rCells.size() > 1 )
507 for (SwWriteTableCells::size_type nCell = 0; nCell < rCells.size(); ++nCell)
509 sal_Int16 eCellVertOri = rCells[nCell]->GetVertOri();
510 if( 0==nCell )
512 eRowVertOri = eCellVertOri;
514 else if( eRowVertOri != eCellVertOri )
516 eRowVertOri = text::VertOrientation::NONE;
517 break;
522 rWrt.OutNewLine(); // <TR> in new line
523 rWrt.Strm().WriteChar( '<' ).WriteOString( Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow) );
524 if( pBrushItem )
526 if (!rWrt.mbXHTML)
528 rWrt.OutBackground(pBrushItem, false);
531 rWrt.m_bTextAttr = false;
532 rWrt.m_bOutOpts = true;
533 if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
534 OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
537 if( text::VertOrientation::TOP==eRowVertOri || text::VertOrientation::BOTTOM==eRowVertOri )
539 OStringBuffer sOut;
540 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_valign
541 "=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
542 .append("\"");
543 rWrt.Strm().WriteOString( sOut );
544 sOut.setLength(0);
547 rWrt.Strm().WriteChar( '>' );
549 rWrt.IncIndentLevel(); // indent content of <TR>...</TR>
551 bool bCellRowSpan = true;
552 if (!rCells.empty() && rCells[0]->GetRowSpan() > 1)
554 // Skip the rowspan attrs of <td> elements if they are the same for every cell of this row.
555 bCellRowSpan = std::adjacent_find(rCells.begin(), rCells.end(),
556 [](const std::unique_ptr<SwWriteTableCell>& pA,
557 const std::unique_ptr<SwWriteTableCell>& pB)
558 { return pA->GetRowSpan() != pB->GetRowSpan(); })
559 != rCells.end();
560 if (!bCellRowSpan)
562 // If no rowspan is written, then skip rows which would only contain covered cells, but
563 // not the current row.
564 rSkipRows = rCells[0]->GetRowSpan() - 1;
568 for (const auto &rpCell : rCells)
570 OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri, bCellRowSpan);
573 rWrt.DecIndentLevel(); // indent content of <TR>...</TR>
575 rWrt.OutNewLine(); // </TR> in new line
576 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow), false );
579 void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
580 bool bTHead, const SwFrameFormat *pFrameFormat,
581 const OUString *pCaption, bool bTopCaption,
582 sal_uInt16 nHSpace, sal_uInt16 nVSpace ) const
584 // determine value of RULES
585 bool bRowsHaveBorder = false;
586 bool bRowsHaveBorderOnly = true;
587 SwWriteTableRow *pRow = m_aRows[0].get();
588 for( SwWriteTableRows::size_type nRow=1; nRow < m_aRows.size(); ++nRow )
590 SwWriteTableRow *pNextRow = m_aRows[nRow].get();
591 bool bBorder = ( pRow->m_bBottomBorder || pNextRow->m_bTopBorder );
592 bRowsHaveBorder |= bBorder;
593 bRowsHaveBorderOnly &= bBorder;
595 sal_uInt16 nBorder2 = pRow->m_bBottomBorder ? pRow->m_nBottomBorder : USHRT_MAX;
596 if( pNextRow->m_bTopBorder && pNextRow->m_nTopBorder < nBorder2 )
597 nBorder2 = pNextRow->m_nTopBorder;
599 pRow->m_bBottomBorder = bBorder;
600 pRow->m_nBottomBorder = nBorder2;
602 pNextRow->m_bTopBorder = bBorder;
603 pNextRow->m_nTopBorder = nBorder2;
605 pRow = pNextRow;
608 bool bColsHaveBorder = false;
609 bool bColsHaveBorderOnly = true;
610 SwWriteTableCol *pCol = m_aCols[0].get();
611 for( SwWriteTableCols::size_type nCol=1; nCol<m_aCols.size(); ++nCol )
613 SwWriteTableCol *pNextCol = m_aCols[nCol].get();
614 bool bBorder = ( pCol->m_bRightBorder || pNextCol->m_bLeftBorder );
615 bColsHaveBorder |= bBorder;
616 bColsHaveBorderOnly &= bBorder;
617 pCol->m_bRightBorder = bBorder;
618 pNextCol->m_bLeftBorder = bBorder;
619 pCol = pNextCol;
622 // close previous numbering, etc
623 rWrt.ChangeParaToken( HtmlTokenId::NONE );
625 if( rWrt.m_bLFPossible )
626 rWrt.OutNewLine(); // <TABLE> in new line
627 OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table);
629 const SvxFrameDirection nOldDirection = rWrt.m_nDirection;
630 if( pFrameFormat )
631 rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
632 if( rWrt.m_bOutFlyFrame || nOldDirection != rWrt.m_nDirection )
634 rWrt.Strm().WriteOString( sOut );
635 sOut.setLength(0);
636 rWrt.OutDirection( rWrt.m_nDirection );
639 // output ALIGN=
640 if( text::HoriOrientation::RIGHT == eAlign )
642 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
643 "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"");
645 else if( text::HoriOrientation::CENTER == eAlign )
647 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
648 "=\"" OOO_STRING_SVTOOLS_HTML_AL_center "\"");
650 else if( text::HoriOrientation::LEFT == eAlign )
652 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
653 "=\"" OOO_STRING_SVTOOLS_HTML_AL_left "\"");
656 // output WIDTH: from layout or calculated
657 if( m_nTabWidth )
659 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
660 if( HasRelWidths() )
661 sOut.append(OString::number(static_cast<sal_Int32>(m_nTabWidth)) + "%");
662 else
664 sal_Int32 nPixWidth = SwHTMLWriter::ToPixel(m_nTabWidth);
665 sOut.append(nPixWidth);
667 sOut.append("\"");
670 if( (nHSpace || nVSpace) && !rWrt.mbReqIF)
672 if (auto nPixHSpace = SwHTMLWriter::ToPixel(nHSpace))
674 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
675 "=\"" + OString::number(nPixHSpace) + "\"");
678 if (auto nPixVSpace = SwHTMLWriter::ToPixel(nVSpace))
680 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
681 "=\"" + OString::number(nPixVSpace) + "\"");
685 // output CELLPADDING: from layout or calculated
686 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellpadding
687 "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellPadding)) + "\"");
689 // output CELLSPACING: from layout or calculated
690 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
691 "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellSpacing)) + "\"");
693 rWrt.Strm().WriteOString( sOut );
694 sOut.setLength(0);
696 // output background
697 if( pFrameFormat )
699 if (!rWrt.mbXHTML)
701 rWrt.OutBackground(pFrameFormat->GetAttrSet(), false);
704 if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
706 rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
710 sOut.append('>');
711 rWrt.Strm().WriteOString( sOut );
712 sOut.setLength(0);
714 rWrt.IncIndentLevel(); // indent content of table
716 // output caption
717 if( pCaption && !pCaption->isEmpty() )
719 rWrt.OutNewLine(); // <CAPTION> in new line
720 OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption);
721 sOutStr.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"")
722 .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
723 .append("\"");
724 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOutStr) );
725 HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption );
726 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption), false );
729 const SwWriteTableCols::size_type nCols = m_aCols.size();
731 // output <COLGRP>/<COL>: If exporting via layout only when during import
732 // some were there, otherwise always.
733 bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly);
734 if( m_bColTags )
736 if( bColGroups )
738 rWrt.OutNewLine(); // <COLGRP> in new line
739 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
741 rWrt.IncIndentLevel(); // indent content of <COLGRP>
744 for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol )
746 rWrt.OutNewLine(); // </COL> in new line
748 const SwWriteTableCol *pColumn = m_aCols[nCol].get();
750 HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
751 html.prettyPrint(false); // We add newlines ourself
752 html.start(OOO_STRING_SVTOOLS_HTML_col);
754 sal_uInt32 nWidth;
755 bool bRel;
756 if( m_bLayoutExport )
758 bRel = pColumn->HasRelWidthOpt();
759 nWidth = pColumn->GetWidthOpt();
761 else
763 bRel = HasRelWidths();
764 nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1);
767 if( bRel )
768 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, Concat2View(OString::number(nWidth) + "*"));
769 else
770 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth)));
771 html.end();
773 if( bColGroups && pColumn->m_bRightBorder && nCol<nCols-1 )
775 rWrt.DecIndentLevel(); // indent content of <COLGRP>
776 rWrt.OutNewLine(); // </COLGRP> in new line
777 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
778 false );
779 rWrt.OutNewLine(); // <COLGRP> in new line
780 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
781 rWrt.IncIndentLevel(); // indent content of <COLGRP>
784 if( bColGroups )
786 rWrt.DecIndentLevel(); // indent content of <COLGRP>
788 rWrt.OutNewLine(); // </COLGRP> in new line
789 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
790 false );
794 // output the lines as table lines
796 // Output <TBODY>?
797 bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly);
798 bool bTBody = bTSections;
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]->m_bBottomBorder )
805 bTHead = false;
807 // Output <TBODY> only if <THEAD> is outputted.
808 bTSections |= bTHead;
810 if( bTSections )
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(), Concat2View(rWrt.GetNamespace() + aTag));
816 rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
819 sal_uInt16 nSkipRows = 0;
820 for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow )
822 const SwWriteTableRow *pRow2 = m_aRows[nRow].get();
824 if (nSkipRows == 0)
826 OutTableCells(rWrt, pRow2->GetCells(), pRow2->GetBackground(), nSkipRows);
828 else
830 --nSkipRows;
832 if( ( (bTHead && nRow==m_nHeadEndRow) ||
833 (bTBody && pRow2->m_bBottomBorder) ) &&
834 nRow < m_aRows.size()-1 )
836 rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
837 rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
838 OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
839 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
840 rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
842 if( bTHead && nRow==m_nHeadEndRow )
843 bTHead = false;
845 aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
846 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag));
847 rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
851 if( bTSections )
853 rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
855 rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
856 OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
857 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
860 rWrt.DecIndentLevel(); // indent content of <TABLE>
862 rWrt.OutNewLine(); // </TABLE> in new line
863 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table), false );
865 rWrt.m_nDirection = nOldDirection;
868 SwHTMLWriter& OutHTML_SwTableNode( SwHTMLWriter& rWrt, SwTableNode & rNode,
869 const SwFrameFormat *pFlyFrameFormat,
870 const OUString *pCaption, bool bTopCaption )
873 SwTable& rTable = rNode.GetTable();
875 rWrt.m_bOutTable = true;
877 // The horizontal alignment of the frame (if exists) has priority.
878 // NONE means that no horizontal alignment was outputted.
879 sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE;
880 css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
881 sal_uInt8 nFlyPercentWidth = 0;
882 tools::Long nFlyWidth = 0;
883 sal_uInt16 nFlyHSpace = 0;
884 sal_uInt16 nFlyVSpace = 0;
885 if( pFlyFrameFormat )
887 eSurround = pFlyFrameFormat->GetSurround().GetSurround();
888 const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize();
889 nFlyPercentWidth = rFrameSize.GetWidthPercent();
890 nFlyWidth = rFrameSize.GetSize().Width();
892 eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient();
893 if( text::HoriOrientation::NONE == eFlyHoriOri )
894 eFlyHoriOri = text::HoriOrientation::LEFT;
896 const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace();
897 nFlyHSpace = static_cast< sal_uInt16 >((rLRSpace.GetLeft() + rLRSpace.GetRight()) / 2);
899 const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace();
900 nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2;
903 // maybe open a FORM
904 bool bPreserveForm = false;
905 if( !rWrt.m_bPreserveForm )
907 rWrt.OutForm( true, &rNode );
908 bPreserveForm = rWrt.mxFormComps.is();
909 rWrt.m_bPreserveForm = bPreserveForm;
912 SwFrameFormat *pFormat = rTable.GetFrameFormat();
914 const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
915 tools::Long nWidth = rFrameSize.GetSize().Width();
916 sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent();
917 sal_uInt16 nBaseWidth = o3tl::narrowing<sal_uInt16>(nWidth);
919 sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient();
921 // text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths
922 sal_uInt16 nNewDefListLvl = 0;
923 bool bRelWidths = false;
924 bool bCheckDefList = false;
925 switch( eTabHoriOri )
927 case text::HoriOrientation::FULL:
928 // Tables with automatic alignment become tables with 100% width.
929 bRelWidths = true;
930 nWidth = 100;
931 eTabHoriOri = text::HoriOrientation::LEFT;
932 break;
933 case text::HoriOrientation::NONE:
935 const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
936 if( aLRItem.GetRight() )
938 // The table width is defined on the basis of the left and
939 // right margin. Therefore we try to define the actual
940 // width of the table. If that's not possible we transform
941 // it to a table with width 100%.
942 nWidth = pFormat->FindLayoutRect(true).Width();
943 if( !nWidth )
945 bRelWidths = true;
946 nWidth = 100;
950 else if( nPercentWidth )
952 // Without a right border the %-width is maintained.
953 nWidth = nPercentWidth;
954 bRelWidths = true;
956 else
958 // Without a right margin also an absolute width is maintained.
959 // We still try to define the actual width via the layout.
960 tools::Long nRealWidth = pFormat->FindLayoutRect(true).Width();
961 if( nRealWidth )
962 nWidth = nRealWidth;
964 bCheckDefList = true;
966 break;
967 case text::HoriOrientation::LEFT_AND_WIDTH:
968 eTabHoriOri = text::HoriOrientation::LEFT;
969 bCheckDefList = true;
970 [[fallthrough]];
971 default:
972 // In all other case it's possible to use directly an absolute
973 // or relative width.
974 if( nPercentWidth )
976 bRelWidths = true;
977 nWidth = nPercentWidth;
979 break;
982 // In ReqIF case, do not emulate indentation with fake description list
983 if( bCheckDefList && !rWrt.mbReqIF )
985 OSL_ENSURE( !rWrt.GetNumInfo().GetNumRule() ||
986 rWrt.GetNextNumInfo(),
987 "NumInfo for next paragraph is missing!" );
988 const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
989 if( aLRItem.GetLeft() > 0 && rWrt.m_nDefListMargin > 0 &&
990 ( !rWrt.GetNumInfo().GetNumRule() ||
991 ( rWrt.GetNextNumInfo() &&
992 (rWrt.GetNumInfo().GetNumRule() != rWrt.GetNextNumInfo()->GetNumRule() ||
993 rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo())) ) ) )
995 // If the paragraph before the table is not numbered or the
996 // paragraph after the table starts with a new numbering or with
997 // a different rule, we can maintain the indentation with a DL.
998 // Otherwise we keep the indentation of the numbering.
999 nNewDefListLvl = static_cast< sal_uInt16 >(
1000 (aLRItem.GetLeft() + (rWrt.m_nDefListMargin/2)) /
1001 rWrt.m_nDefListMargin );
1005 if( !pFlyFrameFormat && !rWrt.mbReqIF && nNewDefListLvl != rWrt.m_nDefListLvl )
1006 rWrt.OutAndSetDefList( nNewDefListLvl );
1008 // eFlyHoriOri and eTabHoriOri now only contain the values of
1009 // LEFT/CENTER and RIGHT!
1010 if( eFlyHoriOri!=text::HoriOrientation::NONE )
1012 eTabHoriOri = eFlyHoriOri;
1013 // MIB 4.7.97: If the table has a relative width, then the width is
1014 // adjusted to the width of the frame, therefore we export its width.
1015 // If fixed width, the table width is relevant. Whoever puts tables with
1016 // relative width <100% into frames is to blame when the result looks bad.
1017 if( bRelWidths )
1019 nWidth = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth;
1020 bRelWidths = nFlyPercentWidth > 0;
1024 sal_Int16 eDivHoriOri = text::HoriOrientation::NONE;
1025 switch( eTabHoriOri )
1027 case text::HoriOrientation::LEFT:
1028 // If a left-aligned table has no right sided flow, then we don't need
1029 // an ALIGN=LEFT in the table.
1030 if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT )
1031 eTabHoriOri = text::HoriOrientation::NONE;
1032 break;
1033 case text::HoriOrientation::RIGHT:
1034 // Something like that also applies to right-aligned tables,
1035 // here we use a <DIV ALIGN=RIGHT> instead.
1036 if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT )
1038 eDivHoriOri = text::HoriOrientation::RIGHT;
1039 eTabHoriOri = text::HoriOrientation::NONE;
1041 break;
1042 case text::HoriOrientation::CENTER:
1043 // Almost nobody understands ALIGN=CENTER, therefore we abstain
1044 // from it and use a <CENTER>.
1045 eDivHoriOri = text::HoriOrientation::CENTER;
1046 eTabHoriOri = text::HoriOrientation::NONE;
1047 break;
1048 default:
1051 if( text::HoriOrientation::NONE==eTabHoriOri )
1052 nFlyHSpace = nFlyVSpace = 0;
1054 if( !pFormat->GetName().isEmpty() )
1055 rWrt.OutImplicitMark( pFormat->GetName(), "table" );
1057 if( text::HoriOrientation::NONE!=eDivHoriOri )
1059 if( rWrt.m_bLFPossible )
1060 rWrt.OutNewLine(); // <CENTER> in new line
1061 if( text::HoriOrientation::CENTER==eDivHoriOri )
1063 if (!rWrt.mbXHTML)
1065 // Not XHTML's css center: start <center>.
1066 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center) );
1069 else
1071 if (rWrt.mbReqIF)
1073 // In ReqIF, div cannot have an 'align' attribute. For now, use 'style' only
1074 // for ReqIF; maybe it makes sense to use it in both cases?
1075 static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
1076 " style=\"display: flex; flex-direction: column; align-items: flex-end\"";
1077 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
1079 else
1081 static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
1082 " " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"";
1083 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
1086 rWrt.IncIndentLevel(); // indent content of <CENTER>
1087 rWrt.m_bLFPossible = true;
1090 // If the table isn't in a frame, then you always can output a LF.
1091 if( text::HoriOrientation::NONE==eTabHoriOri )
1092 rWrt.m_bLFPossible = true;
1094 const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout();
1096 #ifdef DBG_UTIL
1098 SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
1099 if ( pSh && pSh->GetViewOptions()->IsTest1() )
1100 pLayout = nullptr;
1102 #endif
1104 if( pLayout && pLayout->IsExportable() )
1106 SwHTMLWrtTable aTableWrt( pLayout );
1107 aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1108 pFormat, pCaption, bTopCaption,
1109 nFlyHSpace, nFlyVSpace );
1111 else
1113 SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth,
1114 nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() );
1115 aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1116 pFormat, pCaption, bTopCaption,
1117 nFlyHSpace, nFlyVSpace );
1120 // If the table wasn't in a frame, then you always can output a LF.
1121 if( text::HoriOrientation::NONE==eTabHoriOri )
1122 rWrt.m_bLFPossible = true;
1124 if( text::HoriOrientation::NONE!=eDivHoriOri )
1126 rWrt.DecIndentLevel(); // indent content of <CENTER>
1127 rWrt.OutNewLine(); // </CENTER> in new line
1128 OString aTag = text::HoriOrientation::CENTER == eDivHoriOri
1129 ? OOO_STRING_SVTOOLS_HTML_center
1130 : OOO_STRING_SVTOOLS_HTML_division;
1131 if (!rWrt.mbXHTML || eDivHoriOri != text::HoriOrientation::CENTER)
1133 // Not XHTML's css center: end <center>.
1134 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
1136 rWrt.m_bLFPossible = true;
1139 // move Pam behind the table
1140 rWrt.m_pCurrentPam->GetPoint()->Assign( *rNode.EndOfSectionNode() );
1142 if( bPreserveForm )
1144 rWrt.m_bPreserveForm = false;
1145 rWrt.OutForm( false );
1148 rWrt.m_bOutTable = false;
1150 if( rWrt.GetNextNumInfo() &&
1151 rWrt.GetNextNumInfo()->GetNumRule() == rWrt.GetNumInfo().GetNumRule() &&
1152 !rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo()) )
1154 // If the paragraph after the table is numbered with the same rule as the
1155 // one before, then the NumInfo of the next paragraph holds the level of
1156 // paragraph before the table. Therefore NumInfo must be fetched again
1157 // to maybe close the Num list.
1158 rWrt.ClearNextNumInfo();
1159 rWrt.FillNextNumInfo();
1160 OutHTML_NumberBulletListEnd( rWrt, *rWrt.GetNextNumInfo() );
1162 return rWrt;
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */