/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "htmlnum.hxx" #include "wrthtml.hxx" #include #ifdef DBG_UTIL #include #include #endif #include #include #include #define MAX_DEPTH (3) using namespace ::com::sun::star; namespace { 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, tools::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, tools::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 = o3tl::narrowing(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() ) { std::unique_ptr 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 && (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 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 && (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 && iGetSttNd() ) { for( const auto& rpLine : pBox->GetTabLines() ) { if ( lcl_TableLine_HasTabBorders( rpLine, 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( const auto& rpBox : pLine->GetTabBoxes() ) { if ( lcl_TableBox_HasTabBorders( rpBox, 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(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(); // / 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(" " OOO_STRING_SVTOOLS_HTML_O_rowspan "=\"" + OString::number(nRowSpan) + "\""); } if( nColSpan > 1 ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_colspan "=\"" + OString::number(nColSpan) + "\""); } tools::Long nWidth = 0; bool bOutWidth = true; sal_uInt32 nPercentWidth = SAL_MAX_UINT32; if( m_bLayoutExport ) { if( pCell->HasPercentWidthOpt() ) { nPercentWidth = pCell->GetWidthOpt(); } else { nWidth = pCell->GetWidthOpt(); if( !nWidth ) bOutWidth = false; } } else { if( HasRelWidths() ) nPercentWidth = GetPercentWidth(nCol, nColSpan); else nWidth = GetAbsWidth( nCol, nColSpan ); } if (rWrt.mbReqIF) // ReqIF implies strict XHTML: no width for . bOutWidth = false; tools::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(" " OOO_STRING_SVTOOLS_HTML_O_width "=\""); if( nPercentWidth != SAL_MAX_UINT32 ) { sOut.append(static_cast(nPercentWidth)).append('%'); } else { sOut.append(static_cast(aPixelSz.Width())); } sOut.append("\""); } if (rWrt.mbReqIF) { // ReqIF implies strict XHTML: no height for . nHeight = 0; } if( nHeight ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"" + OString::number(aPixelSz.Height()) + "\""); } 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(" " OOO_STRING_SVTOOLS_HTML_O_valign "=\"").append(text::VertOrientation::TOP==eVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom) .append("\""); } } rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); rWrt.m_bTextAttr = false; rWrt.m_bOutOpts = true; const SvxBrushItem *pBrushItem = nullptr; if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) ) { pBrushItem = static_cast(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) pBrushItem = nullptr; } // tdf#132739 with rWrt.m_bCfgOutStyles of true bundle the brush item css // properties into the same "style" tag as the borders so there is only one // style tag rWrt.OutCSS1_TableCellBordersAndBG(*pBox->GetFrameFormat(), pBrushItem); 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(pItem)->GetValue(); bNumFormat = true; } if( SfxItemState::SET==rItemSet.GetItemState( RES_BOXATR_VALUE, false, &pItem ) ) { nValue = static_cast(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().WriteOString( sOut.makeStringAndClear() ); rWrt.m_bLFPossible = true; rWrt.IncIndentLevel(); // indent the content of ... if( pSttNd ) { HTMLSaveData aSaveData( rWrt, pSttNd->GetIndex()+1, pSttNd->EndOfSectionIndex() ); rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() ); } 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 ... if( rWrt.m_bLFPossible ) rWrt.OutNewLine(); aTag = bHead ? OOO_STRING_SVTOOLS_HTML_tableheader : OOO_STRING_SVTOOLS_HTML_tabledata; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(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(); // in new line rWrt.Strm().WriteChar( '<' ).WriteOString( OString(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(" " OOO_STRING_SVTOOLS_HTML_O_valign "=\"").append(text::VertOrientation::TOP==eRowVertOri ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom) .append("\""); rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); } rWrt.Strm().WriteChar( '>' ); rWrt.IncIndentLevel(); // indent content of ... for (const auto &rpCell : rCells) { OutTableCell(rWrt, rpCell.get(), text::VertOrientation::NONE == eRowVertOri); } rWrt.DecIndentLevel(); // indent content of ... rWrt.OutNewLine(); // in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(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].get(); for( SwWriteTableCols::size_type nCol=1; nColbRightBorder || 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(); // 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().WriteOString( sOut.makeStringAndClear() ); rWrt.OutDirection( rWrt.m_nDirection ); } // output ALIGN= if( text::HoriOrientation::RIGHT == eAlign ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\""); } else if( text::HoriOrientation::CENTER == eAlign ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_center "\""); } else if( text::HoriOrientation::LEFT == eAlign ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_left "\""); } // output WIDTH: from layout or calculated if( m_nTabWidth ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\""); if( HasRelWidths() ) sOut.append(static_cast(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(" " OOO_STRING_SVTOOLS_HTML_O_hspace "=\"" + OString::number(aPixelSpc.Width()) + "\""); } if( aPixelSpc.Height() ) { sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_vspace "=\"" + OString::number(aPixelSpc.Height()) + "\""); } } // output CELLPADDING: from layout or calculated sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellpadding "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellPadding,false)) + "\""); // output CELLSPACING: from layout or calculated sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing "=\"" + OString::number(SwHTMLWriter::ToPixel(m_nCellSpacing,false)) + "\""); rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); // output background if( pFrameFormat ) { rWrt.OutBackground( pFrameFormat->GetAttrSet(), false ); if (rWrt.m_bCfgOutStyles) rWrt.OutCSS1_TableFrameFormatOptions( *pFrameFormat ); } sOut.append('>'); rWrt.Strm().WriteOString( sOut.makeStringAndClear() ); rWrt.IncIndentLevel(); // indent content of table // output caption if( pCaption && !pCaption->isEmpty() ) { rWrt.OutNewLine(); // : If exporting via layout only when during import // some were there, otherwise always. bool bColGroups = (bColsHaveBorder && !bColsHaveBorderOnly); if( m_bColTags ) { if( bColGroups ) { rWrt.OutNewLine(); // in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) ); rWrt.IncIndentLevel(); // indent content of } for( SwWriteTableCols::size_type nCol=0; nCol in new line const SwWriteTableCol *pColumn = m_aCols[nCol].get(); 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(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 rWrt.OutNewLine(); // in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup), false ); rWrt.OutNewLine(); // in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup) ); rWrt.IncIndentLevel(); // indent content of } } if( bColGroups ) { rWrt.DecIndentLevel(); // indent content of rWrt.OutNewLine(); // in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_colgroup), false ); } } // output the lines as table lines // Output ? 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_nHeadEndRowbBottomBorder ) bTHead = false; // Output only if is outputted. bTSections |= bTHead; if( bTSections ) { rWrt.OutNewLine(); // / in new line OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag)); rWrt.IncIndentLevel(); // indent content of / } 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(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_tablerow )); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(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 / rWrt.OutNewLine(); // / in new line OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag), false); rWrt.OutNewLine(); // / 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(), OString(rWrt.GetNamespace() + aTag)); rWrt.IncIndentLevel(); // indent content of / } } if( bTSections ) { rWrt.DecIndentLevel(); // indent content of / rWrt.OutNewLine(); // / in new line OString aTag = bTHead ? OOO_STRING_SVTOOLS_HTML_thead : OOO_STRING_SVTOOLS_HTML_tbody; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(rWrt.GetNamespace() + aTag), false); } rWrt.DecIndentLevel(); // indent content of
in new line OStringBuffer sOutStr(OOO_STRING_SVTOOLS_HTML_caption); sOutStr.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") .append(bTopCaption ? OOO_STRING_SVTOOLS_HTML_VA_top : OOO_STRING_SVTOOLS_HTML_VA_bottom) .append("\""); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + sOutStr) ); HTMLOutFuncs::Out_String( rWrt.Strm(), *pCaption, rWrt.m_eDestEnc, &rWrt.m_aNonConvertableCharacters ); HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_caption), false ); } const SwWriteTableCols::size_type nCols = m_aCols.size(); // output /
rWrt.OutNewLine(); //
in new line HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(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(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 nFlyPercentWidth = 0; tools::Long nFlyWidth = 0; sal_uInt16 nFlyHSpace = 0; sal_uInt16 nFlyVSpace = 0; if( pFlyFrameFormat ) { eSurround = pFlyFrameFormat->GetSurround().GetSurround(); const SwFormatFrameSize& rFrameSize = pFlyFrameFormat->GetFrameSize(); nFlyPercentWidth = 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(); tools::Long nWidth = rFrameSize.GetSize().Width(); sal_uInt8 nPercentWidth = rFrameSize.GetWidthPercent(); sal_uInt16 nBaseWidth = o3tl::narrowing(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( nPercentWidth ) { // Without a right border the %-width is maintained. nWidth = nPercentWidth; bRelWidths = true; } else { // Without a right margin also an absolute width is maintained. // We still try to define the actual width via the layout. tools::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; [[fallthrough]]; default: // In all other case it's possible to use directly an absolute // or relative width. if( nPercentWidth ) { bRelWidths = true; nWidth = nPercentWidth; } 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(), OString(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 = nFlyPercentWidth ? nFlyPercentWidth : nFlyWidth; bRelWidths = nFlyPercentWidth > 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 // an 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
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
. 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(); //
in new line if( text::HoriOrientation::CENTER==eDivHoriOri ) HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_center) ); else { OStringLiteral sOut = OOO_STRING_SVTOOLS_HTML_division " " OOO_STRING_SVTOOLS_HTML_O_align "=\"" OOO_STRING_SVTOOLS_HTML_AL_right "\""; HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), OString(rHTMLWrt.GetNamespace() + sOut) ); } rHTMLWrt.IncIndentLevel(); // indent content of
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
rHTMLWrt.OutNewLine(); //
in new line OString aTag = text::HoriOrientation::CENTER == eDivHoriOri ? OOO_STRING_SVTOOLS_HTML_center : OOO_STRING_SVTOOLS_HTML_division; HTMLOutFuncs::Out_AsciiTag(rWrt.Strm(), OString(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_NumberBulletListEnd( rHTMLWrt, *rHTMLWrt.GetNextNumInfo() ); } return rWrt; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */