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