/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <hintids.hxx>
#include <vcl/svapp.hxx>
#include <svtools/htmlout.hxx>
#include <svtools/htmltokn.h>
#include <svtools/htmlkywd.hxx>
#include <vcl/wrkwin.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/boxitem.hxx>
#include <fmtornt.hxx>
#include <frmfmt.hxx>
#include <fmtfsize.hxx>
#include <fmtsrnd.hxx>
#include <frmatr.hxx>
#include <doc.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <pam.hxx>
#include <ndtxt.hxx>
#include <swrect.hxx>
#include <cellatr.hxx>
#include <poolfmt.hxx>
#include <swtable.hxx>
#include <htmltbl.hxx>
#include "htmlnum.hxx"
#include "wrthtml.hxx"
#include <wrtswtbl.hxx>
#ifdef DBG_UTIL
#include <viewsh.hxx>
#include <viewopt.hxx>
#endif
#include <rtl/strbuf.hxx>
#include <sal/types.h>
#define MAX_DEPTH (3)
using namespace ::com::sun::star;
class SwHTMLWrtTable : public SwWriteTable
{
static void Pixelize( sal_uInt16& rValue );
void PixelizeBorders();
void OutTableCell( SwHTMLWriter& rWrt, const SwWriteTableCell *pCell,
bool bOutVAlign ) const;
void OutTableCells( SwHTMLWriter& rWrt,
const SwWriteTableCells& rCells,
const SvxBrushItem *pBrushItem ) const;
virtual bool ShouldExpandSub( const SwTableBox *pBox,
bool bExpandedBefore, sal_uInt16 nDepth ) const override;
static bool HasTabBackground( const SwTableLine& rLine,
bool bTop, bool bBottom, bool bLeft, bool bRight );
static bool HasTabBackground( const SwTableBox& rBox,
bool bTop, bool bBottom, bool bLeft, bool bRight );
public:
SwHTMLWrtTable( const SwTableLines& rLines, long nWidth, sal_uInt32 nBWidth,
bool bRel, sal_uInt16 nLeftSub, sal_uInt16 nRightSub,
sal_uInt16 nNumOfRowsToRepeat );
explicit SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo );
void Write( SwHTMLWriter& rWrt, sal_Int16 eAlign=text::HoriOrientation::NONE,
bool bTHead=false, const SwFrameFormat *pFrameFormat=nullptr,
const OUString *pCaption=nullptr, bool bTopCaption=false,
sal_uInt16 nHSpace=0, sal_uInt16 nVSpace=0 ) const;
};
SwHTMLWrtTable::SwHTMLWrtTable( const SwTableLines& rLines, long nWidth,
sal_uInt32 nBWidth, bool bRel,
sal_uInt16 nLSub, sal_uInt16 nRSub,
sal_uInt16 nNumOfRowsToRepeat )
: SwWriteTable(nullptr, rLines, nWidth, nBWidth, bRel, MAX_DEPTH, nLSub, nRSub, nNumOfRowsToRepeat)
{
PixelizeBorders();
}
SwHTMLWrtTable::SwHTMLWrtTable( const SwHTMLTableLayout *pLayoutInfo )
: SwWriteTable(nullptr, pLayoutInfo)
{
// Adjust some Twip values to pixel limits
if( m_bCollectBorderWidth )
PixelizeBorders();
}
void SwHTMLWrtTable::Pixelize( sal_uInt16& rValue )
{
if( rValue && Application::GetDefaultDevice() )
{
Size aSz( rValue, 0 );
aSz = Application::GetDefaultDevice()->LogicToPixel( aSz, MapMode(MapUnit::MapTwip) );
if( !aSz.Width() )
aSz.setWidth( 1 );
aSz = Application::GetDefaultDevice()->PixelToLogic( aSz, MapMode(MapUnit::MapTwip) );
rValue = static_cast<sal_uInt16>(aSz.Width());
}
}
void SwHTMLWrtTable::PixelizeBorders()
{
Pixelize( m_nBorder );
Pixelize( m_nCellSpacing );
Pixelize( m_nCellPadding );
}
bool SwHTMLWrtTable::HasTabBackground( const SwTableBox& rBox,
bool bTop, bool bBottom, bool bLeft, bool bRight )
{
OSL_ENSURE( bTop || bBottom || bLeft || bRight,
"HasTabBackground: cannot be called" );
bool bRet = false;
if( rBox.GetSttNd() )
{
SvxBrushItem aBrushItem =
rBox.GetFrameFormat()->makeBackgroundBrushItem();
/// The table box has a background, if its background color is not "no fill"/
/// "auto fill" or it has a background graphic.
bRet = aBrushItem.GetColor() != COL_TRANSPARENT ||
!aBrushItem.GetGraphicLink().isEmpty() || aBrushItem.GetGraphic();
}
else
{
const SwTableLines& rLines = rBox.GetTabLines();
const SwTableLines::size_type nCount = rLines.size();
bool bLeftRight = bLeft || bRight;
for( SwTableLines::size_type i=0; !bRet && i<nCount; ++i )
{
bool bT = bTop && 0 == i;
bool bB = bBottom && nCount-1 == i;
if( bT || bB || bLeftRight )
bRet = HasTabBackground( *rLines[i], bT, bB, bLeft, bRight);
}
}
return bRet;
}
bool SwHTMLWrtTable::HasTabBackground( const SwTableLine& rLine,
bool bTop, bool bBottom, bool bLeft, bool bRight )
{
OSL_ENSURE( bTop || bBottom || bLeft || bRight,
"HasTabBackground: cannot be called" );
SvxBrushItem aBrushItem = rLine.GetFrameFormat()->makeBackgroundBrushItem();
/// The table line has a background, if its background color is not "no fill"/
/// "auto fill" or it has a background graphic.
bool bRet = aBrushItem.GetColor() != COL_TRANSPARENT ||
!aBrushItem.GetGraphicLink().isEmpty() || aBrushItem.GetGraphic();
if( !bRet )
{
const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
const SwTableBoxes::size_type nCount = rBoxes.size();
bool bTopBottom = bTop || bBottom;
for( SwTableBoxes::size_type i=0; !bRet && i<nCount; ++i )
{
bool bL = bLeft && 0 == i;
bool bR = bRight && nCount-1 == i;
if( bTopBottom || bL || bR )
bRet = HasTabBackground( *rBoxes[i], bTop, bBottom, bL, bR );
}
}
return bRet;
}
static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders );
static bool lcl_TableBox_HasTabBorders( const SwTableBox* pBox, bool *pBorders )
{
if( *pBorders )
return false;
if( !pBox->GetSttNd() )
{
for( SwTableLines::const_iterator it = pBox->GetTabLines().begin();
it != pBox->GetTabLines().end(); ++it)
{
if ( lcl_TableLine_HasTabBorders( *it, pBorders ) )
break;
}
}
else
{
const SvxBoxItem& rBoxItem =
pBox->GetFrameFormat()->GetFormatAttr( RES_BOX );
*pBorders = rBoxItem.GetTop() || rBoxItem.GetBottom() ||
rBoxItem.GetLeft() || rBoxItem.GetRight();
}
return !*pBorders;
}
static bool lcl_TableLine_HasTabBorders( const SwTableLine* pLine, bool *pBorders )
{
if( *pBorders )
return false;
for( SwTableBoxes::const_iterator it = pLine->GetTabBoxes().begin();
it != pLine->GetTabBoxes().end(); ++it)
{
if ( lcl_TableBox_HasTabBorders( *it, pBorders ) )
break;
}
return !*pBorders;
}
bool SwHTMLWrtTable::ShouldExpandSub( const SwTableBox *pBox,
bool bExpandedBefore,
sal_uInt16 nDepth ) const
{
bool bExpand = !pBox->GetSttNd() && nDepth>0;
if( bExpand && bExpandedBefore )
{
// MIB 30.6.97: If a box was already expanded, another one is only
// expanded when it has a border.
bool bBorders = false;
lcl_TableBox_HasTabBorders( pBox, &bBorders );
if( !bBorders )
bBorders = HasTabBackground( *pBox, true, true, true, true );
bExpand = bBorders;
}
return bExpand;
}
// Write a box as single cell
void SwHTMLWrtTable::OutTableCell( SwHTMLWriter& rWrt,
const SwWriteTableCell *pCell,
bool bOutVAlign ) const
{
const SwTableBox *pBox = pCell->GetBox();
sal_uInt16 nRow = pCell->GetRow();
sal_uInt16 nCol = pCell->GetCol();
sal_uInt16 nRowSpan = pCell->GetRowSpan();
sal_uInt16 nColSpan = pCell->GetColSpan();
if ( !nRowSpan )
return;
const SwStartNode* pSttNd = pBox->GetSttNd();
bool bHead = false;
if( pSttNd )
{
sal_uLong nNdPos = pSttNd->GetIndex()+1;
// determine the type of cell (TD/TH)
SwNode* pNd;
while( !( pNd = rWrt.m_pDoc->GetNodes()[nNdPos])->IsEndNode() )
{
if( pNd->IsTextNode() )
{
// The only paragraphs relevant for the distinction are those
// where the style is one of the two table related styles
// or inherits from one of these.
const SwFormat *pFormat = &static_cast<SwTextNode*>(pNd)->GetAnyFormatColl();
sal_uInt16 nPoolId = pFormat->GetPoolFormatId();
while( !pFormat->IsDefault() &&
RES_POOLCOLL_TABLE_HDLN!=nPoolId &&
RES_POOLCOLL_TABLE!=nPoolId )
{
pFormat = pFormat->DerivedFrom();
nPoolId = pFormat->GetPoolFormatId();
}
if( !pFormat->IsDefault() )
{
bHead = (RES_POOLCOLL_TABLE_HDLN==nPoolId);
break;
}
}
nNdPos++;
}
}
rWrt.OutNewLine(); // <TH>/<TD> in new line
OStringBuffer sOut;
sOut.append('<');
OString aTag(bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata);
sOut.append(rWrt.GetNamespace() + aTag);
// output ROW- and COLSPAN
if( nRowSpan>1 )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_rowspan).
append("=\"").append(static_cast<sal_Int32>(nRowSpan)).append("\"");
}
if( nColSpan > 1 )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_colspan).
append("=\"").append(static_cast<sal_Int32>(nColSpan)).append("\"");
}
long nWidth = 0;
bool bOutWidth = true;
sal_uInt32 nPrcWidth = SAL_MAX_UINT32;
if( m_bLayoutExport )
{
if( pCell->HasPrcWidthOpt() )
{
nPrcWidth = pCell->GetWidthOpt();
}
else
{
nWidth = pCell->GetWidthOpt();
if( !nWidth )
bOutWidth = false;
}
}
else
{
if( HasRelWidths() )
nPrcWidth = GetPrcWidth(nCol, nColSpan);
else
nWidth = GetAbsWidth( nCol, nColSpan );
}
if (rWrt.mbReqIF)
// ReqIF implies strict XHTML: no width for <td>.
bOutWidth = false;
long nHeight = pCell->GetHeight() > 0
? GetAbsHeight( pCell->GetHeight(), nRow, nRowSpan )
: 0;
Size aPixelSz( nWidth, nHeight );
// output WIDTH (Argh: only for Netscape)
if( (aPixelSz.Width() || aPixelSz.Height()) && Application::GetDefaultDevice() )
{
Size aOldSz( aPixelSz );
aPixelSz = Application::GetDefaultDevice()->LogicToPixel( aPixelSz,
MapMode(MapUnit::MapTwip) );
if( aOldSz.Width() && !aPixelSz.Width() )
aPixelSz.setWidth( 1 );
if( aOldSz.Height() && !aPixelSz.Height() )
aPixelSz.setHeight( 1 );
}
// output WIDTH: from layout or calculated
if( bOutWidth )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width).
append("=\"");
if( nPrcWidth != SAL_MAX_UINT32 )
{
sOut.append(static_cast<sal_Int32>(nPrcWidth)).append('%');
}
else
{
sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
}
sOut.append("\"");
}
if( nHeight )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_height)
.append("=\"").append(static_cast<sal_Int32>(aPixelSz.Height())).append("\"");
}
const SfxItemSet& rItemSet = pBox->GetFrameFormat()->GetAttrSet();
const SfxPoolItem *pItem;
// ALIGN is only outputted at the paragraphs from now on
// output VALIGN
if( bOutVAlign )
{
sal_Int16 eVertOri = pCell->GetVertOri();
if( text::VertOrientation::TOP==eVertOri || text::VertOrientation::BOTTOM==eVertOri )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign)
.append("=\"").append(text::VertOrientation::TOP==eVertOri ?
OOO_STRING_SVTOOLS_HTML_VA_top :
OOO_STRING_SVTOOLS_HTML_VA_bottom)
.append("\"");
}
}
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
rWrt.m_bTextAttr = false;
rWrt.m_bOutOpts = true;
const SvxBrushItem *pBrushItem = nullptr;
if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
{
pBrushItem = static_cast<const SvxBrushItem *>(pItem);
}
if( !pBrushItem )
pBrushItem = pCell->GetBackground();
if( pBrushItem )
{
// output background
if (!rWrt.mbReqIF)
// Avoid non-CSS version in the ReqIF case.
rWrt.OutBackground( pBrushItem, false );
if( rWrt.m_bCfgOutStyles )
OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
}
rWrt.OutCSS1_TableCellBorderHack(*pBox->GetFrameFormat());
sal_uInt32 nNumFormat = 0;
double nValue = 0.0;
bool bNumFormat = false, bValue = false;
if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_FORMAT, false, &pItem ) )
{
nNumFormat = static_cast<const SwTableBoxNumFormat *>(pItem)->GetValue();
bNumFormat = true;
}
if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_VALUE, false, &pItem ) )
{
nValue = static_cast<const SwTableBoxValue *>(pItem)->GetValue();
bValue = true;
if( !bNumFormat )
nNumFormat = pBox->GetFrameFormat()->GetTableBoxNumFormat().GetValue();
}
if( bNumFormat || bValue )
{
sOut.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValue, nValue,
nNumFormat, *rWrt.m_pDoc->GetNumberFormatter(), rWrt.m_eDestEnc,
&rWrt.m_aNonConvertableCharacters));
}
sOut.append('>');
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
rWrt.m_bLFPossible = true;
rWrt.IncIndentLevel(); // indent the content of <TD>...</TD>
if( pSttNd )
{
HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1,
pSttNd->EndOfSectionIndex() );
rWrt.Out_SwDoc( rWrt.m_pCurrentPam );
}
else
{
sal_uInt16 nTWidth;
sal_uInt32 nBWidth;
sal_uInt16 nLSub, nRSub;
if( HasRelWidths() )
{
nTWidth = 100;
nBWidth = GetRawWidth( nCol, nColSpan );
nLSub = 0;
nRSub = 0;
}
else
{
nTWidth = GetAbsWidth( nCol, nColSpan );
nBWidth = nTWidth;
nLSub = GetLeftSpace( nCol );
nRSub = GetRightSpace( nCol, nColSpan );
}
SwHTMLWrtTable aTableWrt( pBox->GetTabLines(), nTWidth,
nBWidth, HasRelWidths(), nLSub, nRSub, /*nNumOfRowsToRepeat*/0 );
aTableWrt.Write( rWrt );
}
rWrt.DecIndentLevel(); // indent the content of <TD>...</TD>
if( rWrt.m_bLFPossible )
rWrt.OutNewLine();
aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false);
rWrt.m_bLFPossible = true;
}
// output a line as lines
void SwHTMLWrtTable::OutTableCells( SwHTMLWriter& rWrt,
const SwWriteTableCells& rCells,
const SvxBrushItem *pBrushItem ) const
{
// If the line contains more the one cell and all cells have the same
// alignment, then output the VALIGN at the line instead of the cell.
sal_Int16 eRowVertOri = text::VertOrientation::NONE;
if( rCells.size() > 1 )
{
for (SwWriteTableCells::size_type nCell = 0; nCell < rCells.size(); ++nCell)
{
sal_Int16 eCellVertOri = rCells[nCell]->GetVertOri();
if( 0==nCell )
{
eRowVertOri = eCellVertOri;
}
else if( eRowVertOri != eCellVertOri )
{
eRowVertOri = text::VertOrientation::NONE;
break;
}
}
}
rWrt.OutNewLine(); // <TR> in new line
rWrt.Strm().WriteChar( '<' ).WriteOString( rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow );
if( pBrushItem )
{
rWrt.OutBackground( pBrushItem, false );
rWrt.m_bTextAttr = false;
rWrt.m_bOutOpts = true;
if( rWrt.m_bCfgOutStyles )
OutCSS1_TableBGStyleOpt( rWrt, *pBrushItem );
}
if( text::VertOrientation::TOP==eRowVertOri || text::VertOrientation::BOTTOM==eRowVertOri )
{
OStringBuffer sOut;
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign)
.append("=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
.append("\"");
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
}
rWrt.Strm().WriteChar( '>' );
rWrt.IncIndentLevel(); // indent content of <TR>...</TR>
for (const auto &rpCell : rCells)
{
OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri);
}
rWrt.DecIndentLevel(); // indent content of <TR>...</TR>
rWrt.OutNewLine(); // </TR> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow, false );
}
void SwHTMLWrtTable::Write( SwHTMLWriter& rWrt, sal_Int16 eAlign,
bool bTHead, const SwFrameFormat *pFrameFormat,
const OUString *pCaption, bool bTopCaption,
sal_uInt16 nHSpace, sal_uInt16 nVSpace ) const
{
// determine value of RULES
bool bRowsHaveBorder = false;
bool bRowsHaveBorderOnly = true;
SwWriteTableRow *pRow = m_aRows[0].get();
for( SwWriteTableRows::size_type nRow=1; nRow < m_aRows.size(); ++nRow )
{
SwWriteTableRow *pNextRow = m_aRows[nRow].get();
bool bBorder = ( pRow->bBottomBorder || pNextRow->bTopBorder );
bRowsHaveBorder |= bBorder;
bRowsHaveBorderOnly &= bBorder;
sal_uInt16 nBorder2 = pRow->bBottomBorder ? pRow->nBottomBorder : USHRT_MAX;
if( pNextRow->bTopBorder && pNextRow->nTopBorder < nBorder2 )
nBorder2 = pNextRow->nTopBorder;
pRow->bBottomBorder = bBorder;
pRow->nBottomBorder = nBorder2;
pNextRow->bTopBorder = bBorder;
pNextRow->nTopBorder = nBorder2;
pRow = pNextRow;
}
bool bColsHaveBorder = false;
bool bColsHaveBorderOnly = true;
SwWriteTableCol *pCol = m_aCols[0];
for( SwWriteTableCols::size_type nCol=1; nCol<m_aCols.size(); ++nCol )
{
SwWriteTableCol *pNextCol = m_aCols[nCol];
bool bBorder = ( pCol->bRightBorder || pNextCol->bLeftBorder );
bColsHaveBorder |= bBorder;
bColsHaveBorderOnly &= bBorder;
pCol->bRightBorder = bBorder;
pNextCol->bLeftBorder = bBorder;
pCol = pNextCol;
}
// close previous numbering, etc
rWrt.ChangeParaToken( HtmlTokenId::NONE );
if( rWrt.m_bLFPossible )
rWrt.OutNewLine(); // <TABLE> in new line
OStringBuffer sOut;
sOut.append('<').append(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table);
const SvxFrameDirection nOldDirection = rWrt.m_nDirection;
if( pFrameFormat )
rWrt.m_nDirection = rWrt.GetHTMLDirection( pFrameFormat->GetAttrSet() );
if( rWrt.m_bOutFlyFrame || nOldDirection != rWrt.m_nDirection )
{
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
rWrt.OutDirection( rWrt.m_nDirection );
}
// output ALIGN=
if( text::HoriOrientation::RIGHT == eAlign )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).
append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_right).append("\"");
}
else if( text::HoriOrientation::CENTER == eAlign )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).
append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_center).append("\"");
}
else if( text::HoriOrientation::LEFT == eAlign )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).
append("=\"").append(OOO_STRING_SVTOOLS_HTML_AL_left).append("\"");
}
// output WIDTH: from layout or calculated
if( m_nTabWidth )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width).
append("=\"");
if( HasRelWidths() )
sOut.append(static_cast<sal_Int32>(m_nTabWidth)).append('%');
else if( Application::GetDefaultDevice() )
{
sal_Int32 nPixWidth = Application::GetDefaultDevice()->LogicToPixel(
Size(m_nTabWidth,0), MapMode(MapUnit::MapTwip) ).Width();
if( !nPixWidth )
nPixWidth = 1;
sOut.append(nPixWidth);
}
else
{
OSL_ENSURE( Application::GetDefaultDevice(), "no Application-Window!?" );
sOut.append("100%");
}
sOut.append("\"");
}
if( (nHSpace || nVSpace) && Application::GetDefaultDevice())
{
Size aPixelSpc =
Application::GetDefaultDevice()->LogicToPixel( Size(nHSpace,nVSpace),
MapMode(MapUnit::MapTwip) );
if( !aPixelSpc.Width() && nHSpace )
aPixelSpc.setWidth( 1 );
if( !aPixelSpc.Height() && nVSpace )
aPixelSpc.setHeight( 1 );
if( aPixelSpc.Width() )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_hspace).
append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Width())).append("\"");
}
if( aPixelSpc.Height() )
{
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_vspace).
append("=\"").append(static_cast<sal_Int32>(aPixelSpc.Height())).append("\"");
}
}
// output CELLPADDING: from layout or calculated
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_cellpadding).
append("=\"").append(static_cast<sal_Int32>(SwHTMLWriter::ToPixel(m_nCellPadding,false))).append("\"");
// output CELLSPACING: from layout or calculated
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_cellspacing).
append("=\"").append(static_cast<sal_Int32>(SwHTMLWriter::ToPixel(m_nCellSpacing,false))).append("\"");
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
// output background
if( pFrameFormat )
{
rWrt.OutBackground( pFrameFormat->GetAttrSet(), false );
if( rWrt.m_bCfgOutStyles && pFrameFormat )
rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat );
}
sOut.append('>');
rWrt.Strm().WriteCharPtr( sOut.makeStringAndClear().getStr() );
rWrt.IncIndentLevel(); // indent content of table
// output caption
if( pCaption && !pCaption->isEmpty() )
{
rWrt.OutNewLine(); // <CAPTION> in new line
OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption);
sOutStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).append("=\"")
.append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom)
.append("\"");
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + sOutStr.getStr() );
HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption, rWrt.m_eDestEnc, &rWrt.m_aNonConvertableCharacters );
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption, false );
}
const SwWriteTableCols::size_type nCols = m_aCols.size();
// output <COLGRP>/<COL>: If exporting via layout only when during import
// some were there, otherwise always.
bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly);
if( m_bColTags )
{
if( bColGroups )
{
rWrt.OutNewLine(); // <COLGRP> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup );
rWrt.IncIndentLevel(); // indent content of <COLGRP>
}
for( SwWriteTableCols::size_type nCol=0; nCol<nCols; ++nCol )
{
rWrt.OutNewLine(); // </COL> in new line
const SwWriteTableCol *pColumn = m_aCols[nCol];
HtmlWriter html(rWrt.Strm(), rWrt.maNamespace);
html.start(OOO_STRING_SVTOOLS_HTML_col);
sal_uInt32 nWidth;
bool bRel;
if( m_bLayoutExport )
{
bRel = pColumn->HasRelWidthOpt();
nWidth = pColumn->GetWidthOpt();
}
else
{
bRel = HasRelWidths();
nWidth = bRel ? GetRelWidth(nCol,1) : GetAbsWidth(nCol,1);
}
if( bRel )
html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(nWidth) + "*");
else
html.attribute(OOO_STRING_SVTOOLS_HTML_O_width, OString::number(SwHTMLWriter::ToPixel(nWidth,false)));
html.end();
if( bColGroups && pColumn->bRightBorder && nCol<nCols-1 )
{
rWrt.DecIndentLevel(); // indent content of <COLGRP>
rWrt.OutNewLine(); // </COLGRP> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup,
false );
rWrt.OutNewLine(); // <COLGRP> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup );
rWrt.IncIndentLevel(); // indent content of <COLGRP>
}
}
if( bColGroups )
{
rWrt.DecIndentLevel(); // indent content of <COLGRP>
rWrt.OutNewLine(); // </COLGRP> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup,
false );
}
}
// output the lines as table lines
// Output <TBODY>?
bool bTSections = (bRowsHaveBorder && !bRowsHaveBorderOnly);
bool bTBody = bTSections;
// If sections must be outputted, then a THEAD around the first line only
// can be outputted if there is a line below the cell.
if( bTHead &&
(bTSections || bColGroups) &&
m_nHeadEndRow<m_aRows.size()-1 && !m_aRows[m_nHeadEndRow]->bBottomBorder )
bTHead = false;
// Output <TBODY> only if <THEAD> is outputted.
bTSections |= bTHead;
if( bTSections )
{
rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag);
rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
}
for( SwWriteTableRows::size_type nRow = 0; nRow < m_aRows.size(); ++nRow )
{
const SwWriteTableRow *pRow2 = m_aRows[nRow].get();
OutTableCells( rWrt, pRow2->GetCells(), pRow2->GetBackground() );
if( !m_nCellSpacing && nRow < m_aRows.size()-1 && pRow2->bBottomBorder &&
pRow2->nBottomBorder > DEF_LINE_WIDTH_1 )
{
for( auto nCnt = (pRow2->nBottomBorder / DEF_LINE_WIDTH_1) - 1; nCnt; --nCnt )
{
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow );
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow,
false );
}
}
if( ( (bTHead && nRow==m_nHeadEndRow) ||
(bTBody && pRow2->bBottomBorder) ) &&
nRow < m_aRows.size()-1 )
{
rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false);
rWrt.OutNewLine(); // <THEAD>/<TDATA> in new line
if( bTHead && nRow==m_nHeadEndRow )
bTHead = false;
aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag);
rWrt.IncIndentLevel(); // indent content of <THEAD>/<TDATA>
}
}
if( bTSections )
{
rWrt.DecIndentLevel(); // indent content of <THEAD>/<TDATA>
rWrt.OutNewLine(); // </THEAD>/</TDATA> in new line
OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rWrt.GetNamespace() + aTag, false);
}
rWrt.DecIndentLevel(); // indent content of <TABLE>
rWrt.OutNewLine(); // </TABLE> in new line
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_table, false );
rWrt.m_nDirection = nOldDirection;
}
Writer& OutHTML_SwTableNode( Writer& rWrt, SwTableNode & rNode,
const SwFrameFormat *pFlyFrameFormat,
const OUString *pCaption, bool bTopCaption )
{
SwTable& rTable = rNode.GetTable();
SwHTMLWriter & rHTMLWrt = static_cast<SwHTMLWriter&>(rWrt);
rHTMLWrt.m_bOutTable = true;
// The horizontal alignment of the frame (if exists) has priority.
// NONE means that no horizontal alignment was outputted.
sal_Int16 eFlyHoriOri = text::HoriOrientation::NONE;
css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
sal_uInt8 nFlyPrcWidth = 0;
long nFlyWidth = 0;
sal_uInt16 nFlyHSpace = 0;
sal_uInt16 nFlyVSpace = 0;
if( pFlyFrameFormat )
{
eSurround = pFlyFrameFormat->GetSurround().GetSurround();
const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize();
nFlyPrcWidth = rFrameSize.GetWidthPercent();
nFlyWidth = rFrameSize.GetSize().Width();
eFlyHoriOri = pFlyFrameFormat->GetHoriOrient().GetHoriOrient();
if( text::HoriOrientation::NONE == eFlyHoriOri )
eFlyHoriOri = text::HoriOrientation::LEFT;
const SvxLRSpaceItem& rLRSpace = pFlyFrameFormat->GetLRSpace();
nFlyHSpace = static_cast< sal_uInt16 >((rLRSpace.GetLeft() + rLRSpace.GetRight()) / 2);
const SvxULSpaceItem& rULSpace = pFlyFrameFormat->GetULSpace();
nFlyVSpace = (rULSpace.GetUpper() + rULSpace.GetLower()) / 2;
}
// maybe open a FORM
bool bPreserveForm = false;
if( !rHTMLWrt.m_bPreserveForm )
{
rHTMLWrt.OutForm( true, &rNode );
bPreserveForm = rHTMLWrt.mxFormComps.is();
rHTMLWrt.m_bPreserveForm = bPreserveForm;
}
SwFrameFormat *pFormat = rTable.GetFrameFormat();
const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
long nWidth = rFrameSize.GetSize().Width();
sal_uInt8 nPrcWidth = rFrameSize.GetWidthPercent();
sal_uInt16 nBaseWidth = static_cast<sal_uInt16>(nWidth);
sal_Int16 eTabHoriOri = pFormat->GetHoriOrient().GetHoriOrient();
// text::HoriOrientation::NONE and text::HoriOrientation::FULL tables need relative widths
sal_uInt16 nNewDefListLvl = 0;
bool bRelWidths = false;
bool bCheckDefList = false;
switch( eTabHoriOri )
{
case text::HoriOrientation::FULL:
// Tables with automatic alignment become tables with 100% width.
bRelWidths = true;
nWidth = 100;
eTabHoriOri = text::HoriOrientation::LEFT;
break;
case text::HoriOrientation::NONE:
{
const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
if( aLRItem.GetRight() )
{
// The table width is defined on the basis of the left and
// right margin. Therefore we try to define the actual
// width of the table. If that's not possible we transform
// it to a table with width 100%.
nWidth = pFormat->FindLayoutRect(true).Width();
if( !nWidth )
{
bRelWidths = true;
nWidth = 100;
}
}
else if( nPrcWidth )
{
// Without a right border the %-width is maintained.
nWidth = nPrcWidth;
bRelWidths = true;
}
else
{
// Without a right margin also an absolute width is maintained.
// We still try to define the actual width via the layout.
long nRealWidth = pFormat->FindLayoutRect(true).Width();
if( nRealWidth )
nWidth = nRealWidth;
}
bCheckDefList = true;
}
break;
case text::HoriOrientation::LEFT_AND_WIDTH:
eTabHoriOri = text::HoriOrientation::LEFT;
bCheckDefList = true;
SAL_FALLTHROUGH;
default:
// In all other case it's possible to use directly an absolute
// or relative width.
if( nPrcWidth )
{
bRelWidths = true;
nWidth = nPrcWidth;
}
break;
}
if( bCheckDefList )
{
OSL_ENSURE( !rHTMLWrt.GetNumInfo().GetNumRule() ||
rHTMLWrt.GetNextNumInfo(),
"NumInfo for next paragraph is missing!" );
const SvxLRSpaceItem& aLRItem = pFormat->GetLRSpace();
if( aLRItem.GetLeft() > 0 && rHTMLWrt.m_nDefListMargin > 0 &&
( !rHTMLWrt.GetNumInfo().GetNumRule() ||
( rHTMLWrt.GetNextNumInfo() &&
(rHTMLWrt.GetNextNumInfo()->IsRestart() ||
rHTMLWrt.GetNumInfo().GetNumRule() !=
rHTMLWrt.GetNextNumInfo()->GetNumRule()) ) ) )
{
// If the paragraph before the table is not numbered or the
// paragraph after the table starts with a new numbering or with
// a different rule, we can maintain the indentation with a DL.
// Otherwise we keep the indentation of the numbering.
nNewDefListLvl = static_cast< sal_uInt16 >(
(aLRItem.GetLeft() + (rHTMLWrt.m_nDefListMargin/2)) /
rHTMLWrt.m_nDefListMargin );
}
}
if( !pFlyFrameFormat && nNewDefListLvl != rHTMLWrt.m_nDefListLvl )
rHTMLWrt.OutAndSetDefList( nNewDefListLvl );
if( nNewDefListLvl )
{
if( rHTMLWrt.m_bLFPossible )
rHTMLWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_dd );
}
// eFlyHoriOri and eTabHoriOri now only contain the values of
// LEFT/CENTER and RIGHT!
if( eFlyHoriOri!=text::HoriOrientation::NONE )
{
eTabHoriOri = eFlyHoriOri;
// MIB 4.7.97: If the table has a relative width, then the width is
// adjusted to the width of the frame, therefore we export its width.
// If fixed width, the table width is relevant. Whoever puts tables with
// relative width <100% into frames is to blame when the result looks bad.
if( bRelWidths )
{
nWidth = nFlyPrcWidth ? nFlyPrcWidth : nFlyWidth;
bRelWidths = nFlyPrcWidth > 0;
}
}
sal_Int16 eDivHoriOri = text::HoriOrientation::NONE;
switch( eTabHoriOri )
{
case text::HoriOrientation::LEFT:
// If a left-aligned table has no right sided flow, then we don't need
// a ALIGN=LEFT in the table.
if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_LEFT )
eTabHoriOri = text::HoriOrientation::NONE;
break;
case text::HoriOrientation::RIGHT:
// Something like that also applies to right-aligned tables,
// here we use a <DIV ALIGN=RIGHT> instead.
if( eSurround==css::text::WrapTextMode_NONE || eSurround==css::text::WrapTextMode_RIGHT )
{
eDivHoriOri = text::HoriOrientation::RIGHT;
eTabHoriOri = text::HoriOrientation::NONE;
}
break;
case text::HoriOrientation::CENTER:
// Almost nobody understands ALIGN=CENTER, therefore we abstain
// from it and use a <CENTER>.
eDivHoriOri = text::HoriOrientation::CENTER;
eTabHoriOri = text::HoriOrientation::NONE;
break;
default:
;
}
if( text::HoriOrientation::NONE==eTabHoriOri )
nFlyHSpace = nFlyVSpace = 0;
if( !pFormat->GetName().isEmpty() )
rHTMLWrt.OutImplicitMark( pFormat->GetName(), "table" );
if( text::HoriOrientation::NONE!=eDivHoriOri )
{
if( rHTMLWrt.m_bLFPossible )
rHTMLWrt.OutNewLine(); // <CENTER> in new line
if( text::HoriOrientation::CENTER==eDivHoriOri )
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center );
else
{
OStringBuffer sOut(OOO_STRING_SVTOOLS_HTML_division);
sOut.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_align).append("=\"")
.append(OOO_STRING_SVTOOLS_HTML_AL_right).append("\"");
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), rHTMLWrt.GetNamespace() + sOut.getStr() );
}
rHTMLWrt.IncIndentLevel(); // indent content of <CENTER>
rHTMLWrt.m_bLFPossible = true;
}
// If the table isn't in a frame, then you always can output a LF.
if( text::HoriOrientation::NONE==eTabHoriOri )
rHTMLWrt.m_bLFPossible = true;
const SwHTMLTableLayout *pLayout = rTable.GetHTMLTableLayout();
#ifdef DBG_UTIL
{
SwViewShell *pSh = rWrt.m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
if ( pSh && pSh->GetViewOptions()->IsTest1() )
pLayout = nullptr;
}
#endif
if( pLayout && pLayout->IsExportable() )
{
SwHTMLWrtTable aTableWrt( pLayout );
aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
pFormat, pCaption, bTopCaption,
nFlyHSpace, nFlyVSpace );
}
else
{
SwHTMLWrtTable aTableWrt( rTable.GetTabLines(), nWidth,
nBaseWidth, bRelWidths, 0, 0, rTable.GetRowsToRepeat() );
aTableWrt.Write( rHTMLWrt, eTabHoriOri, rTable.GetRowsToRepeat() > 0,
pFormat, pCaption, bTopCaption,
nFlyHSpace, nFlyVSpace );
}
// If the table wasn't in a frame, then you always can output a LF.
if( text::HoriOrientation::NONE==eTabHoriOri )
rHTMLWrt.m_bLFPossible = true;
if( text::HoriOrientation::NONE!=eDivHoriOri )
{
rHTMLWrt.DecIndentLevel(); // indent content of <CENTER>
rHTMLWrt.OutNewLine(); // </CENTER> in new line
OString aTag = text::HoriOrientation::CENTER == eDivHoriOri
? OOO_STRING_SVTOOLS_HTML_center
: OOO_STRING_SVTOOLS_HTML_division;
HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), rHTMLWrt.GetNamespace() + aTag, false);
rHTMLWrt.m_bLFPossible = true;
}
// move Pam behind the table
rHTMLWrt.m_pCurrentPam->GetPoint()->nNode = *rNode.EndOfSectionNode();
if( bPreserveForm )
{
rHTMLWrt.m_bPreserveForm = false;
rHTMLWrt.OutForm( false );
}
rHTMLWrt.m_bOutTable = false;
if( rHTMLWrt.GetNextNumInfo() &&
!rHTMLWrt.GetNextNumInfo()->IsRestart() &&
rHTMLWrt.GetNextNumInfo()->GetNumRule() ==
rHTMLWrt.GetNumInfo().GetNumRule() )
{
// If the paragraph after the table is numbered with the same rule as the
// one before, then the NumInfo of the next paragraph holds the level of
// paragraph before the table. Therefore NumInfo must be fetched again
// to maybe close the Num list.
rHTMLWrt.ClearNextNumInfo();
rHTMLWrt.FillNextNumInfo();
OutHTML_NumBulListEnd( rHTMLWrt, *rHTMLWrt.GetNextNumInfo() );
}
return rWrt;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: pFrameFormat.