tdf#35361 Add a Quick Look plugins for .od* files on macOS
[LibreOffice.git] / sw / source / filter / html / htmltabw.cxx
blobc1ef09d2922d4842a69bdab26e1561b3f4fd71f4
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.SetLFPossible(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.IsLFPossible())
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.SetLFPossible(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 assert(m_aRows.begin() != m_aRows.end());
588 for (auto row = m_aRows.begin(), next = std::next(row); next < m_aRows.end(); ++row, ++next)
590 SwWriteTableRow* pRow = row->get();
591 SwWriteTableRow* pNextRow = next->get();
592 bool bBorder = ( pRow->HasBottomBorder() || pNextRow->HasTopBorder() );
593 bRowsHaveBorder |= bBorder;
594 bRowsHaveBorderOnly &= bBorder;
596 pRow->SetBottomBorder(bBorder);
597 pNextRow->SetTopBorder(bBorder);
600 bool bColsHaveBorder = false;
601 bool bColsHaveBorderOnly = true;
602 assert(m_aCols.begin() != m_aCols.end());
603 for (auto col = m_aCols.begin(), next = std::next(col); next < m_aCols.end(); ++col, ++next)
605 SwWriteTableCol* pCol = col->get();
606 SwWriteTableCol* pNextCol = next->get();
607 bool bBorder = ( pCol->m_bRightBorder || pNextCol->m_bLeftBorder );
608 bColsHaveBorder |= bBorder;
609 bColsHaveBorderOnly &= bBorder;
610 pCol->m_bRightBorder = bBorder;
611 pNextCol->m_bLeftBorder = bBorder;
614 // close previous numbering, etc
615 rWrt.ChangeParaToken( HtmlTokenId::NONE );
617 if (rWrt.IsLFPossible())
618 rWrt.OutNewLine(); // <TABLE> in new line
619 OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table);
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 )
626 rWrt.Strm().WriteOString( sOut );
627 sOut.setLength(0);
628 rWrt.OutDirection( rWrt.m_nDirection );
631 // output ALIGN=
632 if( text::HoriOrientation::RIGHT == eAlign )
634 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
635 "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"");
637 else if( text::HoriOrientation::CENTER == eAlign )
639 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
640 "=\"" OOO_STRING_SVTOOLS_HTML_AL_center "\"");
642 else if( text::HoriOrientation::LEFT == eAlign )
644 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
645 "=\"" OOO_STRING_SVTOOLS_HTML_AL_left "\"");
648 // output WIDTH: from layout or calculated
649 if( m_nTabWidth )
651 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
652 if( HasRelWidths() )
653 sOut.append(OString::number(static_cast<sal_Int32>(m_nTabWidth)) + "%");
654 else
656 sal_Int32 nPixWidth = SwHTMLWriter::ToPixel(m_nTabWidth);
657 sOut.append(nPixWidth);
659 sOut.append("\"");
662 if( (nHSpace || nVSpace) && !rWrt.mbReqIF)
664 if (auto nPixHSpace = SwHTMLWriter::ToPixel(nHSpace))
666 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_hspace
667 "=\"" + OString::number(nPixHSpace) + "\"");
670 if (auto nPixVSpace = SwHTMLWriter::ToPixel(nVSpace))
672 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace
673 "=\"" + OString::number(nPixVSpace) + "\"");
677 // output CELLPADDING: from layout or calculated
678 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellpadding
679 "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellPadding)) + "\"");
681 // output CELLSPACING: from layout or calculated
682 sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
683 "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellSpacing)) + "\"");
685 rWrt.Strm().WriteOString( sOut );
686 sOut.setLength(0);
688 // output background
689 if( pFrameFormat )
691 if (!rWrt.mbXHTML)
693 rWrt.OutBackground(pFrameFormat->GetAttrSet(), false);
696 if (rWrt.m_bCfgOutStyles || rWrt.mbXHTML)
698 rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
702 sOut.append('>');
703 rWrt.Strm().WriteOString( sOut );
704 sOut.setLength(0);
706 rWrt.IncIndentLevel(); // indent content of table
708 // output caption
709 if( pCaption && !pCaption->isEmpty() )
711 rWrt.OutNewLine(); // <CAPTION> in new line
712 OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption);
713 sOutStr.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"")
714 .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
715 .append("\"");
716 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOutStr) );
717 HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption );
718 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption), false );
721 // output <COLGRP>/<COL>: If exporting via layout only when during import
722 // some were there, otherwise always.
723 bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly);
724 if( m_bColTags )
726 if( bColGroups )
728 rWrt.OutNewLine(); // <COLGRP> in new line
729 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
731 rWrt.IncIndentLevel(); // indent content of <COLGRP>
734 const SwWriteTableCols::size_type nCols = m_aCols.size();
735 for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol )
737 rWrt.OutNewLine(); // </COL> in new line
739 const SwWriteTableCol *pColumn = m_aCols[nCol].get();
741 HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
742 html.prettyPrint(false); // We add newlines ourself
743 html.start(OOO_STRING_SVTOOLS_HTML_col ""_ostr);
745 sal_uInt32 nWidth;
746 bool bRel;
747 if( m_bLayoutExport )
749 bRel = pColumn->HasRelWidthOpt();
750 nWidth = pColumn->GetWidthOpt();
752 else
754 bRel = HasRelWidths();
755 nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1);
758 if( bRel )
759 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, Concat2View(OString::number(nWidth) + "*"));
760 else
761 html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth)));
762 html.end();
764 if( bColGroups && pColumn->m_bRightBorder && nCol<nCols-1 )
766 rWrt.DecIndentLevel(); // indent content of <COLGRP>
767 rWrt.OutNewLine(); // </COLGRP> in new line
768 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
769 false );
770 rWrt.OutNewLine(); // <COLGRP> in new line
771 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) );
772 rWrt.IncIndentLevel(); // indent content of <COLGRP>
775 if( bColGroups )
777 rWrt.DecIndentLevel(); // indent content of <COLGRP>
779 rWrt.OutNewLine(); // </COLGRP> in new line
780 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup),
781 false );
785 // output the lines as table lines
787 // Output <TBODY>?
788 bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly);
789 bool bTBody = bTSections;
791 // If sections must be outputted, then a THEAD around the first line only
792 // can be outputted if there is a line below the cell.
793 if( bTHead &&
794 (bTSections || bColGroups) &&
795 m_nHeadEndRow<m_aRows.size()-1 && !m_aRows[m_nHeadEndRow]->HasBottomBorder() )
796 bTHead = false;
798 // Output <TBODY> only if <THEAD> is outputted.
799 bTSections |= bTHead;
801 if( bTSections )
803 rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
804 OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
805 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag));
807 rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
810 sal_uInt16 nSkipRows = 0;
811 for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow )
813 const SwWriteTableRow *pRow = m_aRows[nRow].get();
815 if (nSkipRows == 0)
817 OutTableCells(rWrt, pRow->GetCells(), pRow->GetBackground(), nSkipRows);
819 else
821 --nSkipRows;
823 if( ( (bTHead && nRow==m_nHeadEndRow) ||
824 (bTBody && pRow->HasBottomBorder()) ) &&
825 nRow < m_aRows.size()-1 )
827 rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
828 rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
829 OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
830 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
831 rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
833 if( bTHead && nRow==m_nHeadEndRow )
834 bTHead = false;
836 aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
837 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag));
838 rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
842 if( bTSections )
844 rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
846 rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
847 OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
848 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
851 rWrt.DecIndentLevel(); // indent content of <TABLE>
853 rWrt.OutNewLine(); // </TABLE> in new line
854 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table), false );
856 rWrt.m_nDirection = nOldDirection;
859 SwHTMLWriter& OutHTML_SwTableNode( SwHTMLWriter& rWrt, SwTableNode & rNode,
860 const SwFrameFormat *pFlyFrameFormat,
861 const OUString *pCaption, bool bTopCaption )
864 SwTable& rTable = rNode.GetTable();
866 rWrt.m_bOutTable = true;
868 // The horizontal alignment of the frame (if exists) has priority.
869 // NONE means that no horizontal alignment was outputted.
870 sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE;
871 css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
872 sal_uInt8 nFlyPercentWidth = 0;
873 tools::Long nFlyWidth = 0;
874 sal_uInt16 nFlyHSpace = 0;
875 sal_uInt16 nFlyVSpace = 0;
876 if( pFlyFrameFormat )
878 eSurround = pFlyFrameFormat->GetSurround().GetSurround();
879 const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize();
880 nFlyPercentWidth = rFrameSize.GetWidthPercent();
881 nFlyWidth = rFrameSize.GetSize().Width();
883 eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient();
884 if( text::HoriOrientation::NONE == eFlyHoriOri )
885 eFlyHoriOri = text::HoriOrientation::LEFT;
887 const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace();
888 nFlyHSpace
889 = static_cast<sal_uInt16>((rLRSpace.ResolveLeft({}) + rLRSpace.ResolveRight({})) / 2);
891 const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace();
892 nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2;
895 // maybe open a FORM
896 bool bPreserveForm = false;
897 if( !rWrt.m_bPreserveForm )
899 rWrt.OutForm( true, &rNode );
900 bPreserveForm = rWrt.mxFormComps.is();
901 rWrt.m_bPreserveForm = bPreserveForm;
904 SwFrameFormat *pFormat = rTable.GetFrameFormat();
906 const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
907 tools::Long nWidth = rFrameSize.GetSize().Width();
908 sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent();
909 sal_uInt16 nBaseWidth = o3tl::narrowing<sal_uInt16>(nWidth);
911 sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient();
913 // text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths
914 sal_uInt16 nNewDefListLvl = 0;
915 bool bRelWidths = false;
916 bool bCheckDefList = false;
917 switch( eTabHoriOri )
919 case text::HoriOrientation::FULL:
920 // Tables with automatic alignment become tables with 100% width.
921 bRelWidths = true;
922 nWidth = 100;
923 eTabHoriOri = text::HoriOrientation::LEFT;
924 break;
925 case text::HoriOrientation::NONE:
927 const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
928 if (aLRItem.ResolveRight({}))
930 // The table width is defined on the basis of the left and
931 // right margin. Therefore we try to define the actual
932 // width of the table. If that's not possible we transform
933 // it to a table with width 100%.
934 nWidth = pFormat->FindLayoutRect(true).Width();
935 if( !nWidth )
937 bRelWidths = true;
938 nWidth = 100;
942 else if( nPercentWidth )
944 // Without a right border the %-width is maintained.
945 nWidth = nPercentWidth;
946 bRelWidths = true;
948 else
950 // Without a right margin also an absolute width is maintained.
951 // We still try to define the actual width via the layout.
952 tools::Long nRealWidth = pFormat->FindLayoutRect(true).Width();
953 if( nRealWidth )
954 nWidth = nRealWidth;
956 bCheckDefList = true;
958 break;
959 case text::HoriOrientation::LEFT_AND_WIDTH:
960 eTabHoriOri = text::HoriOrientation::LEFT;
961 bCheckDefList = true;
962 [[fallthrough]];
963 default:
964 // In all other case it's possible to use directly an absolute
965 // or relative width.
966 if( nPercentWidth )
968 bRelWidths = true;
969 nWidth = nPercentWidth;
971 break;
974 // In ReqIF case, do not emulate indentation with fake description list
975 if( bCheckDefList && !rWrt.mbReqIF )
977 OSL_ENSURE( !rWrt.GetNumInfo().GetNumRule() ||
978 rWrt.GetNextNumInfo(),
979 "NumInfo for next paragraph is missing!" );
980 const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
981 if (aLRItem.ResolveLeft({}) > 0 && rWrt.m_nDefListMargin > 0
982 && (!rWrt.GetNumInfo().GetNumRule()
983 || (rWrt.GetNextNumInfo()
984 && (rWrt.GetNumInfo().GetNumRule() != rWrt.GetNextNumInfo()->GetNumRule()
985 || rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo())))))
987 // If the paragraph before the table is not numbered or the
988 // paragraph after the table starts with a new numbering or with
989 // a different rule, we can maintain the indentation with a DL.
990 // Otherwise we keep the indentation of the numbering.
991 nNewDefListLvl = static_cast<sal_uInt16>(
992 (aLRItem.ResolveLeft({}) + (rWrt.m_nDefListMargin / 2)) / rWrt.m_nDefListMargin);
996 if( !pFlyFrameFormat && !rWrt.mbReqIF && nNewDefListLvl != rWrt.m_nDefListLvl )
997 rWrt.OutAndSetDefList( nNewDefListLvl );
999 // eFlyHoriOri and eTabHoriOri now only contain the values of
1000 // LEFT/CENTER and RIGHT!
1001 if( eFlyHoriOri!=text::HoriOrientation::NONE )
1003 eTabHoriOri = eFlyHoriOri;
1004 // MIB 4.7.97: If the table has a relative width, then the width is
1005 // adjusted to the width of the frame, therefore we export its width.
1006 // If fixed width, the table width is relevant. Whoever puts tables with
1007 // relative width <100% into frames is to blame when the result looks bad.
1008 if( bRelWidths )
1010 nWidth = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth;
1011 bRelWidths = nFlyPercentWidth > 0;
1015 sal_Int16 eDivHoriOri = text::HoriOrientation::NONE;
1016 switch( eTabHoriOri )
1018 case text::HoriOrientation::LEFT:
1019 // If a left-aligned table has no right sided flow, then we don't need
1020 // an ALIGN=LEFT in the table.
1021 if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT )
1022 eTabHoriOri = text::HoriOrientation::NONE;
1023 break;
1024 case text::HoriOrientation::RIGHT:
1025 // Something like that also applies to right-aligned tables,
1026 // here we use a <DIV ALIGN=RIGHT> instead.
1027 if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT )
1029 eDivHoriOri = text::HoriOrientation::RIGHT;
1030 eTabHoriOri = text::HoriOrientation::NONE;
1032 break;
1033 case text::HoriOrientation::CENTER:
1034 // Almost nobody understands ALIGN=CENTER, therefore we abstain
1035 // from it and use a <CENTER>.
1036 eDivHoriOri = text::HoriOrientation::CENTER;
1037 eTabHoriOri = text::HoriOrientation::NONE;
1038 break;
1039 default:
1042 if( text::HoriOrientation::NONE==eTabHoriOri )
1043 nFlyHSpace = nFlyVSpace = 0;
1045 if( !pFormat->GetName().isEmpty() )
1046 rWrt.OutImplicitMark( pFormat->GetName(), "table" );
1048 if( text::HoriOrientation::NONE!=eDivHoriOri )
1050 if (rWrt.IsLFPossible())
1051 rWrt.OutNewLine(); // <CENTER> in new line
1052 if( text::HoriOrientation::CENTER==eDivHoriOri )
1054 if (!rWrt.mbXHTML)
1056 // Not XHTML's css center: start <center>.
1057 HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center) );
1060 else
1062 if (rWrt.mbReqIF)
1064 // In ReqIF, div cannot have an 'align' attribute. For now, use 'style' only
1065 // for ReqIF; maybe it makes sense to use it in both cases?
1066 static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
1067 " style=\"display: flex; flex-direction: column; align-items: flex-end\"";
1068 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
1070 else
1072 static constexpr char sOut[] = OOO_STRING_SVTOOLS_HTML_division
1073 " " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\"";
1074 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut));
1077 rWrt.IncIndentLevel(); // indent content of <CENTER>
1078 rWrt.SetLFPossible(true);
1081 // If the table isn't in a frame, then you always can output a LF.
1082 if( text::HoriOrientation::NONE==eTabHoriOri )
1083 rWrt.SetLFPossible(true);
1085 const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout();
1087 #ifdef DBG_UTIL
1089 SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
1090 if ( pSh && pSh->GetViewOptions()->IsTest1() )
1091 pLayout = nullptr;
1093 #endif
1095 if( pLayout && pLayout->IsExportable() )
1097 SwHTMLWrtTable aTableWrt( pLayout );
1098 aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1099 pFormat, pCaption, bTopCaption,
1100 nFlyHSpace, nFlyVSpace );
1102 else
1104 SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth,
1105 nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() );
1106 aTableWrt.Write( rWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
1107 pFormat, pCaption, bTopCaption,
1108 nFlyHSpace, nFlyVSpace );
1111 // If the table wasn't in a frame, then you always can output a LF.
1112 if( text::HoriOrientation::NONE==eTabHoriOri )
1113 rWrt.SetLFPossible(true);
1115 if( text::HoriOrientation::NONE!=eDivHoriOri )
1117 rWrt.DecIndentLevel(); // indent content of <CENTER>
1118 rWrt.OutNewLine(); // </CENTER> in new line
1119 OString aTag = text::HoriOrientation::CENTER == eDivHoriOri
1120 ? OOO_STRING_SVTOOLS_HTML_center
1121 : OOO_STRING_SVTOOLS_HTML_division;
1122 if (!rWrt.mbXHTML || eDivHoriOri != text::HoriOrientation::CENTER)
1124 // Not XHTML's css center: end <center>.
1125 HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false);
1127 rWrt.SetLFPossible(true);
1130 // move Pam behind the table
1131 rWrt.m_pCurrentPam->GetPoint()->Assign( *rNode.EndOfSectionNode() );
1133 if( bPreserveForm )
1135 rWrt.m_bPreserveForm = false;
1136 rWrt.OutForm( false );
1139 rWrt.m_bOutTable = false;
1141 if( rWrt.GetNextNumInfo() &&
1142 rWrt.GetNextNumInfo()->GetNumRule() == rWrt.GetNumInfo().GetNumRule() &&
1143 !rWrt.GetNextNumInfo()->IsRestart(rWrt.GetNumInfo()) )
1145 // If the paragraph after the table is numbered with the same rule as the
1146 // one before, then the NumInfo of the next paragraph holds the level of
1147 // paragraph before the table. Therefore NumInfo must be fetched again
1148 // to maybe close the Num list.
1149 rWrt.ClearNextNumInfo();
1150 rWrt.FillNextNumInfo();
1151 OutHTML_NumberBulletListEnd( rWrt, *rWrt.GetNextNumInfo() );
1153 return rWrt;
1156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */