1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
32 #include <fmtfsize.hxx>
33 #include <fmtsrnd.hxx>
36 #include <IDocumentLayoutAccess.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>
49 #include <viewopt.hxx>
51 #include <rtl/strbuf.hxx>
52 #include <sal/types.h>
53 #include <osl/diagnose.h>
57 using namespace ::com::sun::star
;
61 class SwHTMLWrtTable
: public SwWriteTable
63 static void Pixelize( sal_uInt16
& rValue
);
64 void PixelizeBorders();
66 /// Writes a single table cell.
68 /// bCellRowSpan decides if the cell's row span should be written or not.
69 void OutTableCell( SwHTMLWriter
& rWrt
, const SwWriteTableCell
*pCell
,
71 bool bCellRowSpan
) const;
73 /// Writes a single table row.
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
);
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
)
112 SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout
*pLayoutInfo
)
113 : SwWriteTable(nullptr, pLayoutInfo
)
115 // Adjust some Twip values to pixel limits
116 if( m_bCollectBorderWidth
)
120 void SwHTMLWrtTable::Pixelize( sal_uInt16
& 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" );
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();
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
);
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();
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
);
198 static bool lcl_TableLine_HasTabBorders( const SwTableLine
* pLine
, bool *pBorders
);
200 static bool lcl_TableBox_HasTabBorders( const SwTableBox
* pBox
, bool *pBorders
)
205 if( !pBox
->GetSttNd() )
207 for( const auto& rpLine
: pBox
->GetTabLines() )
209 if ( lcl_TableLine_HasTabBorders( rpLine
, pBorders
) )
215 const SvxBoxItem
& rBoxItem
=
216 pBox
->GetFrameFormat()->GetFormatAttr( RES_BOX
);
218 *pBorders
= rBoxItem
.GetTop() || rBoxItem
.GetBottom() ||
219 rBoxItem
.GetLeft() || rBoxItem
.GetRight();
225 static bool lcl_TableLine_HasTabBorders( const SwTableLine
* pLine
, bool *pBorders
)
230 for( const auto& rpBox
: pLine
->GetTabBoxes() )
232 if ( lcl_TableBox_HasTabBorders( rpBox
, 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
);
250 bBorders
= HasTabBackground( *pBox
, true, true, true, true );
257 // Write a box as single cell
258 void SwHTMLWrtTable::OutTableCell( SwHTMLWriter
& rWrt
,
259 const SwWriteTableCell
*pCell
,
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();
272 const SwStartNode
* pSttNd
= pBox
->GetSttNd();
276 SwNodeOffset nNdPos
= pSttNd
->GetIndex()+1;
278 // determine the type of cell (TD/TH)
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
);
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
) + "\"");
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();
336 nWidth
= pCell
->GetWidthOpt();
344 nPercentWidth
= GetPercentWidth(nCol
, nColSpan
);
346 nWidth
= GetAbsWidth( nCol
, nColSpan
);
350 // ReqIF implies strict XHTML: no width for <td>.
353 tools::Long nHeight
= pCell
->GetHeight() > 0
354 ? GetAbsHeight( pCell
->GetHeight(), nRow
, nRowSpan
)
356 Size
aPixelSz(SwHTMLWriter::ToPixel(nWidth
), SwHTMLWriter::ToPixel(nHeight
));
358 // output WIDTH: from layout or calculated
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
)) + "%");
368 sOut
.append(static_cast<sal_Int32
>(aPixelSz
.Width()));
375 // ReqIF implies strict XHTML: no height for <td>.
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
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
)
403 rWrt
.Strm().WriteOString( sOut
);
406 rWrt
.m_bTextAttr
= false;
407 rWrt
.m_bOutOpts
= true;
408 const SvxBrushItem
*pBrushItem
= rItemSet
.GetItemIfSet( RES_BACKGROUND
, false );
410 pBrushItem
= pCell
->GetBackground();
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
426 rWrt
.OutCSS1_TableCellBordersAndBG(*pBox
->GetFrameFormat(), pBrushItem
);
428 sal_uInt32 nNumFormat
= 0;
430 bool bNumFormat
= false, bValue
= false;
431 if( const SwTableBoxNumFormat
* pItem
= rItemSet
.GetItemIfSet( RES_BOXATR_FORMAT
, false ) )
433 nNumFormat
= pItem
->GetValue();
436 if( const SwTableBoxValue
* pItem
= rItemSet
.GetItemIfSet( RES_BOXATR_VALUE
, false ) )
438 nValue
= pItem
->GetValue();
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()));
450 rWrt
.Strm().WriteOString( sOut
);
452 rWrt
.m_bLFPossible
= true;
454 rWrt
.IncIndentLevel(); // indent the content of <TD>...</TD>
458 HTMLSaveData
aSaveData( rWrt
, pSttNd
->GetIndex()+1,
459 pSttNd
->EndOfSectionIndex() );
460 rWrt
.Out_SwDoc( rWrt
.m_pCurrentPam
.get() );
466 sal_uInt16 nLSub
, nRSub
;
470 nBWidth
= GetRawWidth( nCol
, nColSpan
);
476 nTWidth
= GetAbsWidth( nCol
, nColSpan
);
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
)
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();
512 eRowVertOri
= eCellVertOri
;
514 else if( eRowVertOri
!= eCellVertOri
)
516 eRowVertOri
= text::VertOrientation::NONE
;
522 rWrt
.OutNewLine(); // <TR> in new line
523 rWrt
.Strm().WriteChar( '<' ).WriteOString( Concat2View(rWrt
.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow
) );
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
)
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
)
543 rWrt
.Strm().WriteOString( sOut
);
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(); })
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
;
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
;
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
;
631 rWrt
.m_nDirection
= rWrt
.GetHTMLDirection( pFrameFormat
->GetAttrSet() );
632 if( rWrt
.m_bOutFlyFrame
|| nOldDirection
!= rWrt
.m_nDirection
)
634 rWrt
.Strm().WriteOString( sOut
);
636 rWrt
.OutDirection( rWrt
.m_nDirection
);
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
659 sOut
.append(" " OOO_STRING_SVTOOLS_HTML_O_width
"=\"");
661 sOut
.append(OString::number(static_cast<sal_Int32
>(m_nTabWidth
)) + "%");
664 sal_Int32 nPixWidth
= SwHTMLWriter::ToPixel(m_nTabWidth
);
665 sOut
.append(nPixWidth
);
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
);
701 rWrt
.OutBackground(pFrameFormat
->GetAttrSet(), false);
704 if (rWrt
.m_bCfgOutStyles
|| rWrt
.mbXHTML
)
706 rWrt
.OutCSS1_TableFrameFormatOptions( *pFrameFormat
);
711 rWrt
.Strm().WriteOString( sOut
);
714 rWrt
.IncIndentLevel(); // indent content of table
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
)
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
);
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
);
756 if( m_bLayoutExport
)
758 bRel
= pColumn
->HasRelWidthOpt();
759 nWidth
= pColumn
->GetWidthOpt();
763 bRel
= HasRelWidths();
764 nWidth
= bRel
? GetRelWidth(nCol
,1) : GetAbsWidth(nCol
,1);
768 html
.attribute(OOO_STRING_SVTOOLS_HTML_O_width
, Concat2View(OString::number(nWidth
) + "*"));
770 html
.attribute(OOO_STRING_SVTOOLS_HTML_O_width
, OString::number(SwHTMLWriter::ToPixel(nWidth
)));
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
),
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>
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
),
794 // output the lines as table lines
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.
803 (bTSections
|| bColGroups
) &&
804 m_nHeadEndRow
<m_aRows
.size()-1 && !m_aRows
[m_nHeadEndRow
]->m_bBottomBorder
)
807 // Output <TBODY> only if <THEAD> is outputted.
808 bTSections
|= bTHead
;
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();
826 OutTableCells(rWrt
, pRow2
->GetCells(), pRow2
->GetBackground(), 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
)
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>
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;
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.
931 eTabHoriOri
= text::HoriOrientation::LEFT
;
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();
950 else if( nPercentWidth
)
952 // Without a right border the %-width is maintained.
953 nWidth
= nPercentWidth
;
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();
964 bCheckDefList
= true;
967 case text::HoriOrientation::LEFT_AND_WIDTH
:
968 eTabHoriOri
= text::HoriOrientation::LEFT
;
969 bCheckDefList
= true;
972 // In all other case it's possible to use directly an absolute
973 // or relative width.
977 nWidth
= nPercentWidth
;
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.
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
;
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
;
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
;
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
)
1065 // Not XHTML's css center: start <center>.
1066 HTMLOutFuncs::Out_AsciiTag( rWrt
.Strm(), Concat2View(rWrt
.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center
) );
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
));
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();
1098 SwViewShell
*pSh
= rWrt
.m_pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell();
1099 if ( pSh
&& pSh
->GetViewOptions()->IsTest1() )
1104 if( pLayout
&& pLayout
->IsExportable() )
1106 SwHTMLWrtTable
aTableWrt( pLayout
);
1107 aTableWrt
.Write( rWrt
, eTabHoriOri
, rTable
.GetRowsToRepeat() > 0,
1108 pFormat
, pCaption
, bTopCaption
,
1109 nFlyHSpace
, nFlyVSpace
);
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() );
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() );
1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */