/* -*- 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 <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <com/sun/star/text/RelOrientation.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <hintids.hxx>
#include <tools/fract.hxx>
#include <svl/urihelper.hxx>
#include <vcl/svapp.hxx>
#include <sfx2/event.hxx>
#include <svtools/htmlkywd.hxx>
#include <svtools/htmlout.hxx>
#include <svtools/htmltokn.h>
#include <vcl/imap.hxx>
#include <vcl/imapobj.hxx>
#include <svtools/htmlcfg.hxx>
#include <svtools/HtmlWriter.hxx>
#include <svx/svdouno.hxx>
#include <svx/xoutbmp.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/brushitem.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <svx/svdograf.hxx>
#include <comphelper/xmlencode.hxx>
#include <fmtanchr.hxx>
#include <fmtornt.hxx>
#include <fmturl.hxx>
#include <fmtfsize.hxx>
#include <fmtclds.hxx>
#include <fmtcntnt.hxx>
#include <fmtsrnd.hxx>
#include <fmtinfmt.hxx>
#include <txtinet.hxx>
#include <frmatr.hxx>
#include <grfatr.hxx>
#include <flypos.hxx>
#include <ndgrf.hxx>
#include <doc.hxx>
#include <ndtxt.hxx>
#include <pam.hxx>
#include <swerror.h>
#include <frmfmt.hxx>
#include "wrthtml.hxx"
#include "htmlatr.hxx"
#include "htmlfly.hxx"
#include "htmlreqifreader.hxx"
using namespace css;
const HtmlFrmOpts HTML_FRMOPTS_IMG_ALL =
HtmlFrmOpts::Alt |
HtmlFrmOpts::Size |
HtmlFrmOpts::AnySize |
HtmlFrmOpts::Border |
HtmlFrmOpts::Name;
const HtmlFrmOpts HTML_FRMOPTS_IMG_CNTNR =
HTML_FRMOPTS_IMG_ALL |
HtmlFrmOpts::AbsSize;
const HtmlFrmOpts HTML_FRMOPTS_IMG =
HTML_FRMOPTS_IMG_ALL |
HtmlFrmOpts::Align |
HtmlFrmOpts::Space |
HtmlFrmOpts::BrClear;
const HtmlFrmOpts HTML_FRMOPTS_IMG_CSS1 =
HtmlFrmOpts::SAlign |
HtmlFrmOpts::SSpace;
const HtmlFrmOpts HTML_FRMOPTS_DIV =
HtmlFrmOpts::Id |
HtmlFrmOpts::SAlign |
HtmlFrmOpts::SSize |
HtmlFrmOpts::AnySize |
HtmlFrmOpts::AbsSize |
HtmlFrmOpts::SSpace |
HtmlFrmOpts::SBorder |
HtmlFrmOpts::SBackground |
HtmlFrmOpts::BrClear |
HtmlFrmOpts::Dir;
const HtmlFrmOpts HTML_FRMOPTS_MULTICOL =
HtmlFrmOpts::Id |
HtmlFrmOpts::Width |
HtmlFrmOpts::AnySize |
HtmlFrmOpts::AbsSize |
HtmlFrmOpts::Dir;
const HtmlFrmOpts HTML_FRMOPTS_MULTICOL_CSS1 =
HtmlFrmOpts::SAlign |
HtmlFrmOpts::SSize |
HtmlFrmOpts::SSpace |
HtmlFrmOpts::SBorder|
HtmlFrmOpts::SBackground;
const HtmlFrmOpts HTML_FRMOPTS_SPACER =
HtmlFrmOpts::Align |
HtmlFrmOpts::Size |
HtmlFrmOpts::AnySize |
HtmlFrmOpts::BrClear |
HtmlFrmOpts::MarginSize |
HtmlFrmOpts::AbsSize;
const HtmlFrmOpts HTML_FRMOPTS_CNTNR =
HtmlFrmOpts::SAlign |
HtmlFrmOpts::SSpace |
HtmlFrmOpts::SWidth |
HtmlFrmOpts::AnySize |
HtmlFrmOpts::AbsSize |
HtmlFrmOpts::SPixSize;
static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat );
static SwHTMLWriter& OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
bool bInCntnr );
static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat );
static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
const SwFrameFormat& rFrameFormat, bool bSpan );
static SwHTMLWriter& OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat, bool bPNGFallback );
static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFormat,
bool bInCntnr, bool bPNGFallback );
static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
const SdrObject& rSdrObj );
HTMLOutEvent const aImageEventTable[] =
{
{ OOO_STRING_SVTOOLS_HTML_O_SDonload, OOO_STRING_SVTOOLS_HTML_O_onload, SvMacroItemId::OnImageLoadDone },
{ OOO_STRING_SVTOOLS_HTML_O_SDonabort, OOO_STRING_SVTOOLS_HTML_O_onabort, SvMacroItemId::OnImageLoadCancel },
{ OOO_STRING_SVTOOLS_HTML_O_SDonerror, OOO_STRING_SVTOOLS_HTML_O_onerror, SvMacroItemId::OnImageLoadError },
{ nullptr, nullptr, SvMacroItemId::NONE }
};
HTMLOutEvent const aIMapEventTable[] =
{
{ OOO_STRING_SVTOOLS_HTML_O_SDonmouseover, OOO_STRING_SVTOOLS_HTML_O_onmouseover, SvMacroItemId::OnMouseOver },
{ OOO_STRING_SVTOOLS_HTML_O_SDonmouseout, OOO_STRING_SVTOOLS_HTML_O_onmouseout, SvMacroItemId::OnMouseOut },
{ nullptr, nullptr, SvMacroItemId::NONE }
};
SwHTMLFrameType SwHTMLWriter::GuessFrameType( const SwFrameFormat& rFrameFormat,
const SdrObject*& rpSdrObj )
{
SwHTMLFrameType eType;
if( RES_DRAWFRMFMT == rFrameFormat.Which() )
{
// use an arbitrary draw object as the default value
eType = HTML_FRMTYPE_DRAW;
const SdrObject *pObj =
SwHTMLWriter::GetMarqueeTextObj( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
if( pObj )
{
// scrolling text
rpSdrObj = pObj;
eType = HTML_FRMTYPE_MARQUEE;
}
else
{
pObj = GetHTMLControl( static_cast<const SwDrawFrameFormat &>(rFrameFormat) );
if( pObj )
{
// Form control
rpSdrObj = pObj;
eType = HTML_FRMTYPE_CONTROL;
}
}
}
else
{
// use a text frame as the default value
eType = HTML_FRMTYPE_TEXT;
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
const SwNode* pNd = m_pDoc->GetNodes()[ nStt ];
if( pNd->IsGrfNode() )
{
// graphic node
eType = HTML_FRMTYPE_GRF;
}
else if( pNd->IsOLENode() )
{
// applet, plugin, floating frame
eType = GuessOLENodeFrameType( *pNd );
}
else
{
SwNodeOffset nEnd = m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
const SwFormatCol* pFormatCol = rItemSet.GetItemIfSet( RES_COL );
if( pFormatCol && pFormatCol->GetNumCols() > 1 )
{
// frame with columns
eType = HTML_FRMTYPE_MULTICOL;
}
else if( pNd->IsTableNode() )
{
const SwTableNode *pTableNd = pNd->GetTableNode();
SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
if( nTableEnd+1 == nEnd )
{
// table
eType = HTML_FRMTYPE_TABLE;
}
else if( nTableEnd+2 == nEnd )
{
// table with caption
eType = HTML_FRMTYPE_TABLE_CAP;
}
}
else if( pNd->IsTextNode() )
{
const SwTextNode *pTextNd = pNd->GetTextNode();
bool bEmpty = false;
if( nStt==nEnd-1 && !pTextNd->Len() )
{
// empty frame? Only if no frame is
// anchored to the text or start node.
bEmpty = true;
for( auto & pHTMLPosFlyFrame : m_aHTMLPosFlyFrames )
{
SwNodeOffset nIdx = pHTMLPosFlyFrame->GetNdIndex().GetIndex();
bEmpty = (nIdx != nStt) && (nIdx != nStt-1);
if( !bEmpty || nIdx > nStt )
break;
}
}
if( bEmpty )
{
std::unique_ptr<SvxBrushItem> aBrush = rFrameFormat.makeBackgroundBrushItem();
/// background is not empty, if it has a background graphic
/// or its background color is not "no fill"/"auto fill".
if( GPOS_NONE != aBrush->GetGraphicPos() ||
aBrush->GetColor() != COL_TRANSPARENT )
{
bEmpty = false;
}
}
if( bEmpty )
{
// empty frame
eType = HTML_FRMTYPE_EMPTY;
}
else if( m_pDoc->GetNodes()[nStt+1]->IsTableNode() )
{
const SwTableNode *pTableNd =
m_pDoc->GetNodes()[nStt+1]->GetTableNode();
if( pTableNd->EndOfSectionIndex()+1 == nEnd )
{
// table with heading
eType = HTML_FRMTYPE_TABLE_CAP;
}
}
}
}
}
return eType;
}
void SwHTMLWriter::CollectFlyFrames()
{
SwPosFlyFrames aFlyPos(
m_pDoc->GetAllFlyFormats(m_bWriteAll ? nullptr : m_pCurrentPam.get(), true));
for(const SwPosFlyFrame& rItem : aFlyPos)
{
const SwFrameFormat& rFrameFormat = rItem.GetFormat();
const SdrObject *pSdrObj = nullptr;
const SwNode *pAnchorNode;
const SwContentNode *pACNd;
SwHTMLFrameType eType = GuessFrameType( rFrameFormat, pSdrObj );
AllHtmlFlags nMode;
const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
sal_Int16 eHoriRel = rFrameFormat.GetHoriOrient().GetRelationOrient();
switch( rAnchor.GetAnchorId() )
{
case RndStdIds::FLY_AT_PAGE:
case RndStdIds::FLY_AT_FLY:
nMode = getHTMLOutFramePageFlyTable(eType, m_nExportMode);
break;
case RndStdIds::FLY_AT_PARA:
// frames that are anchored to a paragraph are only placed
// before the paragraph, if the paragraph has a
// spacing.
if( text::RelOrientation::FRAME == eHoriRel &&
(pAnchorNode = rAnchor.GetAnchorNode()) != nullptr &&
(pACNd = pAnchorNode->GetContentNode()) != nullptr )
{
const SvxTextLeftMarginItem& rTextLeftMargin =
pACNd->GetAttr(RES_MARGIN_TEXTLEFT);
const SvxRightMarginItem& rRightMargin =
pACNd->GetAttr(RES_MARGIN_RIGHT);
if (rTextLeftMargin.GetTextLeft() || rRightMargin.GetRight())
{
nMode = getHTMLOutFrameParaFrameTable(eType, m_nExportMode);
break;
}
}
nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
break;
case RndStdIds::FLY_AT_CHAR:
if( text::RelOrientation::FRAME == eHoriRel || text::RelOrientation::PRINT_AREA == eHoriRel )
nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
else
nMode = getHTMLOutFrameParaOtherTable(eType, m_nExportMode);
break;
default:
nMode = getHTMLOutFrameParaPrtAreaTable(eType, m_nExportMode);
break;
}
m_aHTMLPosFlyFrames.insert( std::make_unique<SwHTMLPosFlyFrame>(rItem, pSdrObj, nMode) );
}
}
bool SwHTMLWriter::OutFlyFrame( SwNodeOffset nNdIdx, sal_Int32 nContentIdx, HtmlPosition nPos )
{
bool bFlysLeft = false; // Are there still Flys left at the current node position?
// OutFlyFrame can be called recursively. Thus, sometimes it is
// necessary to start over after a Fly was returned.
bool bRestart = true;
while( !m_aHTMLPosFlyFrames.empty() && bRestart )
{
bFlysLeft = bRestart = false;
// search for the beginning of the FlyFrames
size_t i {0};
for( ; i < m_aHTMLPosFlyFrames.size() &&
m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() < nNdIdx; i++ )
;
for( ; !bRestart && i < m_aHTMLPosFlyFrames.size() &&
m_aHTMLPosFlyFrames[i]->GetNdIndex().GetIndex() == nNdIdx; i++ )
{
SwHTMLPosFlyFrame *pPosFly = m_aHTMLPosFlyFrames[i].get();
if( ( HtmlPosition::Any == nPos ||
pPosFly->GetOutPos() == nPos ) &&
pPosFly->GetContentIndex() == nContentIdx )
{
// It is important to remove it first, because additional
// elements or the whole array could be deleted on
// deeper recursion levels.
std::unique_ptr<SwHTMLPosFlyFrame> flyHolder = m_aHTMLPosFlyFrames.erase_extract(i);
i--;
if( m_aHTMLPosFlyFrames.empty() )
{
bRestart = true; // not really, only exit the loop
}
HTMLOutFuncs::FlushToAscii(Strm()); // it was one time only; do we still need it?
OutFrameFormat( pPosFly->GetOutMode(), pPosFly->GetFormat(),
pPosFly->GetSdrObject() );
switch( pPosFly->GetOutFn() )
{
case HtmlOut::Div:
case HtmlOut::Span:
case HtmlOut::MultiCol:
case HtmlOut::TableNode:
bRestart = true; // It could become recursive here
break;
default: break;
}
}
else
{
bFlysLeft = true;
}
}
}
return bFlysLeft;
}
void SwHTMLWriter::OutFrameFormat( AllHtmlFlags nMode, const SwFrameFormat& rFrameFormat,
const SdrObject *pSdrObject )
{
HtmlContainerFlags nCntnrMode = nMode.nContainer;
HtmlOut nOutMode = nMode.nOut;
OString aContainerStr;
if( HtmlContainerFlags::NONE != nCntnrMode )
{
if (IsLFPossible() && HtmlContainerFlags::Div == nCntnrMode)
OutNewLine();
OStringBuffer sOut;
aContainerStr = (HtmlContainerFlags::Div == nCntnrMode)
? OOO_STRING_SVTOOLS_HTML_division
: OOO_STRING_SVTOOLS_HTML_span;
sOut.append("<" + GetNamespace() + aContainerStr + " "
OOO_STRING_SVTOOLS_HTML_O_class "=\""
"sd-abs-pos\"");
Strm().WriteOString( sOut );
sOut.setLength(0);
// Output a width for non-draw objects
HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_CNTNR;
// For frames with columns we can also output the background
if( HtmlOut::MultiCol == nOutMode )
nFrameFlags |= HtmlFrmOpts::SBackground|HtmlFrmOpts::SBorder;
if( IsHTMLMode( HTMLMODE_BORDER_NONE ) )
nFrameFlags |= HtmlFrmOpts::SNoBorder;
OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags, pSdrObject );
Strm().WriteChar( '>' );
if( HtmlContainerFlags::Div == nCntnrMode )
{
IncIndentLevel();
SetLFPossible(true);
}
}
switch( nOutMode )
{
case HtmlOut::TableNode: // OK
OSL_ENSURE( aContainerStr.isEmpty(), "Table: Container is not supposed to be here" );
OutHTML_FrameFormatTableNode( *this, rFrameFormat );
break;
case HtmlOut::GraphicNode: // OK
OutHTML_FrameFormatGrfNode( *this, rFrameFormat, !aContainerStr.isEmpty(), /*bPNGFallback=*/true );
break;
case HtmlOut::OleNode: // OK
OutHTML_FrameFormatOLENode( *this, rFrameFormat, !aContainerStr.isEmpty() );
break;
case HtmlOut::OleGraphic: // OK
OutHTML_FrameFormatOLENodeGrf( *this, rFrameFormat, !aContainerStr.isEmpty() );
break;
case HtmlOut::Div:
case HtmlOut::Span:
OSL_ENSURE( aContainerStr.isEmpty(), "Div: Container is not supposed to be here" );
OutHTML_FrameFormatAsDivOrSpan( *this, rFrameFormat, HtmlOut::Span==nOutMode );
break;
case HtmlOut::MultiCol: // OK
OutHTML_FrameFormatAsMulticol( *this, rFrameFormat, !aContainerStr.isEmpty() );
break;
case HtmlOut::Spacer: // OK
OSL_ENSURE( aContainerStr.isEmpty(), "Spacer: Container is not supposed to be here" );
OutHTML_FrameFormatAsSpacer( *this, rFrameFormat );
break;
case HtmlOut::Control: // OK
OutHTML_DrawFrameFormatAsControl( *this,
static_cast<const SwDrawFrameFormat &>(rFrameFormat), dynamic_cast<const SdrUnoObj&>(*pSdrObject),
!aContainerStr.isEmpty() );
break;
case HtmlOut::AMarquee:
OutHTML_FrameFormatAsMarquee( *this, rFrameFormat, *pSdrObject );
break;
case HtmlOut::Marquee:
OSL_ENSURE( aContainerStr.isEmpty(), "Marquee: Container is not supposed to be here" );
OutHTML_DrawFrameFormatAsMarquee( *this,
static_cast<const SwDrawFrameFormat &>(rFrameFormat), *pSdrObject );
break;
case HtmlOut::GraphicFrame:
OutHTML_FrameFormatAsImage( *this, rFrameFormat, /*bPNGFallback=*/true );
break;
}
if( HtmlContainerFlags::Div == nCntnrMode )
{
DecIndentLevel();
if (IsLFPossible())
OutNewLine();
HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
SetLFPossible(true);
}
else if( HtmlContainerFlags::Span == nCntnrMode )
HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
}
OString SwHTMLWriter::OutFrameFormatOptions( const SwFrameFormat &rFrameFormat,
std::u16string_view rAlternateText,
HtmlFrmOpts nFrameOpts )
{
OString sRetEndTags;
OStringBuffer sOut;
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
// Name
if( (nFrameOpts & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
!rFrameFormat.GetName().isEmpty() )
{
const char *pStr =
(nFrameOpts & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
sOut.append(OString::Concat(" ") + pStr + "=\"");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), rFrameFormat.GetName() );
sOut.append('\"');
}
// Name
if( nFrameOpts & HtmlFrmOpts::Dir )
{
SvxFrameDirection nDir = GetHTMLDirection( rItemSet );
Strm().WriteOString( sOut );
sOut.setLength(0);
OutDirection( nDir );
}
// ALT
if( (nFrameOpts & HtmlFrmOpts::Alt) && !rAlternateText.empty() )
{
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_alt "=\"");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), rAlternateText );
sOut.append('\"');
}
// ALIGN
const char *pStr = nullptr;
RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
if( (nFrameOpts & HtmlFrmOpts::Align) &&
((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) )
{
// MIB 12.3.98: Wouldn't it be more clever to left-align frames that
// are anchored to a paragraph if necessary, instead of inserting them
// as being anchored to characters?
const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
if( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
{
pStr = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
? OOO_STRING_SVTOOLS_HTML_AL_right
: OOO_STRING_SVTOOLS_HTML_AL_left;
}
}
const SwFormatVertOrient* pVertOrient;
if( (nFrameOpts & HtmlFrmOpts::Align) && !pStr &&
( !(nFrameOpts & HtmlFrmOpts::SAlign) ||
(RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
(pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
{
switch( pVertOrient->GetVertOrient() )
{
case text::VertOrientation::LINE_TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_top; break;
case text::VertOrientation::CHAR_TOP:
case text::VertOrientation::BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_texttop; break; // not possible
case text::VertOrientation::LINE_CENTER:
case text::VertOrientation::CHAR_CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break; // not possible
case text::VertOrientation::CENTER: pStr = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
case text::VertOrientation::LINE_BOTTOM:
case text::VertOrientation::CHAR_BOTTOM: pStr = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break; // not possible
case text::VertOrientation::TOP: pStr = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
case text::VertOrientation::NONE: break;
}
}
if( pStr )
{
sOut.append(OString::Concat(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"") +
pStr + "\"");
}
// HSPACE and VSPACE
Size aTwipSpc( 0, 0 );
const SvxLRSpaceItem* pLRSpaceItem;
if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
(pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
{
aTwipSpc.setWidth(
( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
}
const SvxULSpaceItem* pULSpaceItem;
if( (nFrameOpts & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
(pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
{
aTwipSpc.setHeight(
( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
}
if( (nFrameOpts & HtmlFrmOpts::Space) &&
(aTwipSpc.Width() || aTwipSpc.Height()) &&
!mbReqIF )
{
Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
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()) + "\"");
}
}
// The spacing must be considered for the size, if the corresponding flag
// is set.
if( nFrameOpts & HtmlFrmOpts::MarginSize )
{
aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
}
else
{
aTwipSpc.setWidth( 0 );
aTwipSpc.setHeight( 0 );
}
const SvxBoxItem* pBoxItem;
if( !(nFrameOpts & HtmlFrmOpts::AbsSize) &&
(pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
{
aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
}
// WIDTH and/or HEIGHT
// Output SwFrameSize::Variable/SwFrameSize::Minimum only, if ANYSIZE is set
const SwFormatFrameSize *pFSItem;
if( (nFrameOpts & HtmlFrmOpts::Size) &&
(pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE )) &&
( (nFrameOpts & HtmlFrmOpts::AnySize) ||
SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
{
sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
// Size of the object in Twips without margins
Size aTwipSz( (nPercentWidth ? 0
: pFSItem->GetWidth()-aTwipSpc.Width()),
(nPercentHeight ? 0
: pFSItem->GetHeight()-aTwipSpc.Height()) );
OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
if( aTwipSz.Width() < 0 )
aTwipSz.setWidth( 0 );
if( aTwipSz.Height() < 0 )
aTwipSz.setHeight( 0 );
Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
if( (nFrameOpts & HtmlFrmOpts::Width) &&
((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
{
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_width "=\"");
if( nPercentWidth )
sOut.append(OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%");
else
sOut.append(static_cast<sal_Int32>(aPixelSz.Width()));
sOut.append("\"");
}
if( (nFrameOpts & HtmlFrmOpts::Height) &&
((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
{
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"");
if( nPercentHeight )
sOut.append(OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%");
else
sOut.append(static_cast<sal_Int32>(aPixelSz.Height()));
sOut.append("\"");
}
}
if (!sOut.isEmpty())
{
Strm().WriteOString( sOut );
sOut.setLength(0);
}
if (!mbReqIF)
{
// Insert wrap for graphics that are anchored to a paragraph as
// <BR CLEAR=...> in the string
const SwFormatSurround* pSurround;
if( (nFrameOpts & HtmlFrmOpts::BrClear) &&
((RndStdIds::FLY_AT_PARA == rFrameFormat.GetAnchor().GetAnchorId()) ||
(RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId())) &&
(pSurround = rItemSet.GetItemIfSet( RES_SURROUND )) )
{
sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
pStr = nullptr;
css::text::WrapTextMode eSurround = pSurround->GetSurround();
bool bAnchorOnly = pSurround->IsAnchorOnly();
switch( eHoriOri )
{
case text::HoriOrientation::RIGHT:
{
switch( eSurround )
{
case css::text::WrapTextMode_NONE:
case css::text::WrapTextMode_RIGHT:
pStr = OOO_STRING_SVTOOLS_HTML_AL_right;
break;
case css::text::WrapTextMode_LEFT:
case css::text::WrapTextMode_PARALLEL:
if( bAnchorOnly )
m_bClearRight = true;
break;
default:
;
}
}
break;
default:
// If a frame is centered, it gets left aligned. This
// should be taken into account here, too.
{
switch( eSurround )
{
case css::text::WrapTextMode_NONE:
case css::text::WrapTextMode_LEFT:
pStr = OOO_STRING_SVTOOLS_HTML_AL_left;
break;
case css::text::WrapTextMode_RIGHT:
case css::text::WrapTextMode_PARALLEL:
if( bAnchorOnly )
m_bClearLeft = true;
break;
default:
;
}
}
break;
}
if( pStr )
{
sOut.append("<" OOO_STRING_SVTOOLS_HTML_linebreak
" " OOO_STRING_SVTOOLS_HTML_O_clear
"=\"" + OString::Concat(pStr) + "\">");
sRetEndTags = sOut.makeStringAndClear();
}
}
}
return sRetEndTags;
}
void SwHTMLWriter::writeFrameFormatOptions(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat, const OUString& rAlternateText, HtmlFrmOpts nFrameOptions)
{
bool bReplacement = (nFrameOptions & HtmlFrmOpts::Replacement) || mbReqIF;
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
// Name
if( (nFrameOptions & (HtmlFrmOpts::Id|HtmlFrmOpts::Name)) &&
!rFrameFormat.GetName().isEmpty() && !bReplacement)
{
const char* pAttributeName = (nFrameOptions & HtmlFrmOpts::Id) ? OOO_STRING_SVTOOLS_HTML_O_id : OOO_STRING_SVTOOLS_HTML_O_name;
aHtml.attribute(pAttributeName, rFrameFormat.GetName());
}
// Name
if (nFrameOptions & HtmlFrmOpts::Dir)
{
SvxFrameDirection nCurrentDirection = GetHTMLDirection(rItemSet);
OString sDirection = convertDirection(nCurrentDirection);
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_dir, sDirection);
}
// alt
if( (nFrameOptions & HtmlFrmOpts::Alt) && !rAlternateText.isEmpty() && !bReplacement )
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_alt, rAlternateText);
}
// align
std::string_view pAlignString;
RndStdIds eAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
if( (nFrameOptions & HtmlFrmOpts::Align) &&
((RndStdIds::FLY_AT_PARA == eAnchorId) || (RndStdIds::FLY_AT_CHAR == eAnchorId)) && !bReplacement)
{
const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
if( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
{
pAlignString = text::HoriOrientation::RIGHT == rHoriOri.GetHoriOrient()
? std::string_view(OOO_STRING_SVTOOLS_HTML_AL_right)
: std::string_view(OOO_STRING_SVTOOLS_HTML_AL_left);
}
}
const SwFormatVertOrient* pVertOrient;
if( (nFrameOptions & HtmlFrmOpts::Align) && pAlignString.empty() &&
( !(nFrameOptions & HtmlFrmOpts::SAlign) ||
(RndStdIds::FLY_AS_CHAR == eAnchorId) ) &&
(pVertOrient = rItemSet.GetItemIfSet( RES_VERT_ORIENT )) )
{
switch( pVertOrient->GetVertOrient() )
{
case text::VertOrientation::LINE_TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_top; break;
case text::VertOrientation::CHAR_TOP:
case text::VertOrientation::BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_texttop; break;
case text::VertOrientation::LINE_CENTER:
case text::VertOrientation::CHAR_CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absmiddle; break;
case text::VertOrientation::CENTER: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
case text::VertOrientation::LINE_BOTTOM:
case text::VertOrientation::CHAR_BOTTOM: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_absbottom; break;
case text::VertOrientation::TOP: pAlignString = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
case text::VertOrientation::NONE: break;
}
}
if (!pAlignString.empty() && !bReplacement)
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_align, pAlignString);
}
// hspace and vspace
Size aTwipSpc( 0, 0 );
const SvxLRSpaceItem* pLRSpaceItem;
if( (nFrameOptions & (HtmlFrmOpts::Space | HtmlFrmOpts::MarginSize)) &&
(pLRSpaceItem = rItemSet.GetItemIfSet( RES_LR_SPACE )) )
{
aTwipSpc.setWidth(
( pLRSpaceItem->GetLeft() + pLRSpaceItem->GetRight() ) / 2 );
m_nDfltLeftMargin = m_nDfltRightMargin = aTwipSpc.Width();
}
const SvxULSpaceItem* pULSpaceItem;
if( (nFrameOptions & (HtmlFrmOpts::Space|HtmlFrmOpts::MarginSize)) &&
(pULSpaceItem = rItemSet.GetItemIfSet( RES_UL_SPACE )) )
{
aTwipSpc.setHeight(
( pULSpaceItem->GetUpper() + pULSpaceItem->GetLower() ) / 2 );
m_nDfltTopMargin = m_nDfltBottomMargin = o3tl::narrowing<sal_uInt16>(aTwipSpc.Height());
}
if( (nFrameOptions & HtmlFrmOpts::Space) &&
(aTwipSpc.Width() || aTwipSpc.Height()) &&
!mbReqIF )
{
Size aPixelSpc = SwHTMLWriter::ToPixel(aTwipSpc);
if (aPixelSpc.Width())
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_hspace, static_cast<sal_Int32>(aPixelSpc.Width()));
}
if (aPixelSpc.Height())
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_vspace, static_cast<sal_Int32>(aPixelSpc.Height()));
}
}
// The spacing must be considered for the size, if the corresponding flag
// is set.
if( nFrameOptions & HtmlFrmOpts::MarginSize )
{
aTwipSpc.setWidth( aTwipSpc.Width() * -2 );
aTwipSpc.setHeight( aTwipSpc.Height() * -2 );
}
else
{
aTwipSpc.setWidth( 0 );
aTwipSpc.setHeight( 0 );
}
const SvxBoxItem* pBoxItem;
if( !(nFrameOptions & HtmlFrmOpts::AbsSize) &&
(pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
{
aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT ) );
aTwipSpc.AdjustWidth(pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT ) );
aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::TOP ) );
aTwipSpc.AdjustHeight(pBoxItem->CalcLineSpace( SvxBoxItemLine::BOTTOM ) );
}
// "width" and/or "height"
// Only output SwFrameSize::Variable/SwFrameSize::Minimum if ANYSIZE is set
std::optional<SwFormatFrameSize> aFrameSize;
const SwFormatFrameSize* pFSItem = rItemSet.GetItemIfSet( RES_FRM_SIZE );
const SdrObject* pObject;
if (!pFSItem && (pObject = rFrameFormat.FindSdrObject()))
{
// Write size for Draw shapes as well.
const tools::Rectangle& rSnapRect = pObject->GetSnapRect();
aFrameSize.emplace();
aFrameSize->SetWidthSizeType(SwFrameSize::Fixed);
aFrameSize->SetWidth(rSnapRect.getOpenWidth());
aFrameSize->SetHeightSizeType(SwFrameSize::Fixed);
aFrameSize->SetHeight(rSnapRect.getOpenHeight());
pFSItem = &*aFrameSize;
}
if( (nFrameOptions & HtmlFrmOpts::Size) &&
pFSItem &&
( (nFrameOptions & HtmlFrmOpts::AnySize) ||
SwFrameSize::Fixed == pFSItem->GetHeightSizeType()) )
{
sal_uInt8 nPercentWidth = pFSItem->GetWidthPercent();
sal_uInt8 nPercentHeight = pFSItem->GetHeightPercent();
// Size of the object in Twips without margins
Size aTwipSz( (nPercentWidth && nPercentWidth != 255 ? 0
: pFSItem->GetWidth()-aTwipSpc.Width()),
(nPercentHeight && nPercentHeight != 255 ? 0
: pFSItem->GetHeight()-aTwipSpc.Height()) );
OSL_ENSURE( aTwipSz.Width() >= 0 && aTwipSz.Height() >= 0, "Frame size minus spacing < 0!!!???" );
if( aTwipSz.Width() < 0 )
aTwipSz.setWidth( 0 );
if( aTwipSz.Height() < 0 )
aTwipSz.setHeight( 0 );
Size aPixelSz(SwHTMLWriter::ToPixel(aTwipSz));
if( (nFrameOptions & HtmlFrmOpts::Width) &&
((nPercentWidth && nPercentWidth!=255) || aPixelSz.Width()) )
{
OString sWidth;
if (nPercentWidth)
{
if (nPercentWidth == 255)
{
if (nPercentHeight)
{
sWidth = "auto"_ostr;
}
else
{
sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
}
}
else
{
sWidth = OString::number(static_cast<sal_Int32>(nPercentWidth)) + "%";
}
}
else
sWidth = OString::number(static_cast<sal_Int32>(aPixelSz.Width()));
if (!mbXHTML || sWidth != "auto")
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_width, sWidth);
}
}
if( (nFrameOptions & HtmlFrmOpts::Height) &&
((nPercentHeight && nPercentHeight!=255) || aPixelSz.Height()) )
{
OString sHeight;
if (nPercentHeight)
{
if (nPercentHeight == 255)
{
if (nPercentWidth)
{
sHeight = "auto"_ostr;
}
else
{
sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
}
}
else
{
sHeight = OString::number(static_cast<sal_Int32>(nPercentHeight)) + "%";
}
}
else
sHeight = OString::number(static_cast<sal_Int32>(aPixelSz.Height()));
if (!mbXHTML || sHeight != "auto")
{
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_height, sHeight);
}
}
}
}
void SwHTMLWriter::writeFrameSurroundTag(HtmlWriter& aHtml, const SwFrameFormat& rFrameFormat,
HtmlFrmOpts nFrameOptions)
{
if (mbReqIF)
return;
// Insert wrap for graphics that are anchored to a paragraph as
// <BR CLEAR=...> in the string
if( !(nFrameOptions & HtmlFrmOpts::BrClear) )
return;
RndStdIds nAnchorId = rFrameFormat.GetAnchor().GetAnchorId();
if (RndStdIds::FLY_AT_PARA != nAnchorId && RndStdIds::FLY_AT_CHAR != nAnchorId)
return;
const SwFormatSurround* pSurround = rFrameFormat.GetAttrSet().GetItemIfSet(RES_SURROUND);
if (!pSurround)
return;
std::string_view pSurroundString;
sal_Int16 eHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
css::text::WrapTextMode eSurround = pSurround->GetSurround();
bool bAnchorOnly = pSurround->IsAnchorOnly();
switch( eHoriOri )
{
case text::HoriOrientation::RIGHT:
{
switch( eSurround )
{
case css::text::WrapTextMode_NONE:
case css::text::WrapTextMode_RIGHT:
pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_right;
break;
case css::text::WrapTextMode_LEFT:
case css::text::WrapTextMode_PARALLEL:
if( bAnchorOnly )
m_bClearRight = true;
break;
default:
;
}
}
break;
default:
// If a frame is centered, it gets left aligned. This
// should be taken into account here, too.
{
switch( eSurround )
{
case css::text::WrapTextMode_NONE:
case css::text::WrapTextMode_LEFT:
pSurroundString = OOO_STRING_SVTOOLS_HTML_AL_left;
break;
case css::text::WrapTextMode_RIGHT:
case css::text::WrapTextMode_PARALLEL:
if( bAnchorOnly )
m_bClearLeft = true;
break;
default:
break;
}
}
break;
}
if (!pSurroundString.empty())
{
aHtml.start(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
aHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, pSurroundString);
aHtml.end();
}
}
namespace
{
OUString lclWriteOutImap(SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, const SwFrameFormat& rFrameFormat,
const Size& rRealSize, const ImageMap* pAltImgMap, const SwFormatURL*& pURLItem)
{
OUString aIMapName;
// Only consider the URL attribute if no ImageMap was supplied
// write ImageMap
const ImageMap* pIMap = pAltImgMap;
if( !pIMap )
{
pURLItem = rItemSet.GetItemIfSet(RES_URL);
if (pURLItem)
pIMap = pURLItem->GetMap();
}
if (pIMap && !rWrt.mbReqIF)
{
// make the name unique
aIMapName = pIMap->GetName();
OUString aNameBase;
if (!aIMapName.isEmpty())
aNameBase = aIMapName;
else
{
aNameBase = OOO_STRING_SVTOOLS_HTML_map;
aIMapName = aNameBase + OUString::number(rWrt.m_nImgMapCnt);
}
bool bFound;
do
{
bFound = false;
for (const OUString & rImgMapName : rWrt.m_aImgMapNames)
{
// TODO: Unicode: Comparison is case insensitive for ASCII
// characters only now!
if (aIMapName.equalsIgnoreAsciiCase(rImgMapName))
{
bFound = true;
break;
}
}
if (bFound)
{
rWrt.m_nImgMapCnt++;
aIMapName = aNameBase + OUString::number( rWrt.m_nImgMapCnt );
}
} while (bFound);
bool bScale = false;
Fraction aScaleX(1, 1);
Fraction aScaleY(1, 1);
const SwFormatFrameSize& rFrameSize = rFrameFormat.GetFrameSize();
const SvxBoxItem& rBox = rFrameFormat.GetBox();
if (!rFrameSize.GetWidthPercent() && rRealSize.Width())
{
SwTwips nWidth = rFrameSize.GetWidth();
nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
OSL_ENSURE( nWidth > 0, "Are there any graphics that are 0 twip wide!?" );
if (nWidth <= 0) // should not happen
nWidth = 1;
if (rRealSize.Width() != nWidth)
{
aScaleX = Fraction(nWidth, rRealSize.Width());
bScale = true;
}
}
if (!rFrameSize.GetHeightPercent() && rRealSize.Height())
{
SwTwips nHeight = rFrameSize.GetHeight();
nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) + rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM);
OSL_ENSURE( nHeight > 0, "Are there any graphics that are 0 twip high!?" );
if (nHeight <= 0)
nHeight = 1;
if (rRealSize.Height() != nHeight)
{
aScaleY = Fraction(nHeight, rRealSize.Height());
bScale = true;
}
}
rWrt.m_aImgMapNames.push_back(aIMapName);
OString aIndMap, aIndArea;
const char *pIndArea = nullptr, *pIndMap = nullptr;
if (rWrt.IsLFPossible())
{
rWrt.OutNewLine( true );
aIndMap = rWrt.GetIndentString();
aIndArea = rWrt.GetIndentString(1);
pIndArea = aIndArea.getStr();
pIndMap = aIndMap.getStr();
}
if (bScale)
{
ImageMap aScaledIMap(*pIMap);
aScaledIMap.Scale(aScaleX, aScaleY);
HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), aScaledIMap, aIMapName,
aIMapEventTable,
rWrt.m_bCfgStarBasic,
SAL_NEWLINE_STRING, pIndArea, pIndMap );
}
else
{
HTMLOutFuncs::Out_ImageMap( rWrt.Strm(), rWrt.GetBaseURL(), *pIMap, aIMapName,
aIMapEventTable,
rWrt.m_bCfgStarBasic,
SAL_NEWLINE_STRING, pIndArea, pIndMap );
}
}
return aIMapName;
}
OUString getFrameFormatText(const SwFrameFormat& rFrameFormat)
{
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
const SwNodeIndex* pSttIx = rFlyContent.GetContentIdx();
if (!pSttIx)
return {};
const SwNodeOffset nStt = pSttIx->GetIndex();
const auto& nodes = rFrameFormat.GetDoc()->GetNodes();
const SwNodeOffset nEnd = nodes[nStt]->EndOfSectionIndex();
OUStringBuffer result;
for (SwNodeOffset i = nStt + 1; i < nEnd; ++i)
{
if (const auto* pTextNd = nodes[i]->GetTextNode())
{
if (!result.isEmpty())
result.append("\n");
result.append(comphelper::string::encodeForXml(pTextNd->GetExpandText(
nullptr, 0, -1, true, true, false,
ExpandMode::ExpandFields | ExpandMode::HideInvisible | ExpandMode::HideDeletions
| ExpandMode::HideFieldmarkCommands)));
}
}
return result.makeStringAndClear();
}
}
SwHTMLWriter& OutHTML_ImageStart( HtmlWriter& rHtml, SwHTMLWriter& rWrt, const SwFrameFormat &rFrameFormat,
const OUString& rGraphicURL,
Graphic const & rGraphic, const OUString& rAlternateText,
const Size &rRealSize, HtmlFrmOpts nFrameOpts,
const char *pMarkType,
const ImageMap *pAltImgMap,
const OUString& rMimeType,
bool bOwn)
{
// <object data="..."> instead of <img src="...">
bool bReplacement = (nFrameOpts & HtmlFrmOpts::Replacement) || rWrt.mbReqIF;
if (rWrt.mbSkipImages)
return rWrt;
// if necessary, temporarily close an open attribute
if( !rWrt.m_aINetFormats.empty() )
{
SwFormatINetFormat* pINetFormat = rWrt.m_aINetFormats.back();
OutHTML_INetFormat( rWrt, *pINetFormat, false );
}
OUString aGraphicURL( rGraphicURL );
if (!rWrt.mbEmbedImages)
aGraphicURL = rWrt.normalizeURL(aGraphicURL, bOwn);
const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
const SwFormatURL* pURLItem = nullptr;
OUString aIMapName = lclWriteOutImap(rWrt, rItemSet, rFrameFormat, rRealSize, pAltImgMap, pURLItem);
// put img into new line
if (rWrt.IsLFPossible())
rWrt.OutNewLine( true );
// <a name=...></a>...<img ...>
if( pMarkType && !rFrameFormat.GetName().isEmpty() )
{
rWrt.OutImplicitMark( rFrameFormat.GetName(), pMarkType );
}
// URL -> <a>...<img ... >...</a>
const SvxMacroItem *pMacItem = rItemSet.GetItemIfSet(RES_FRMMACRO);
if (pURLItem || pMacItem || (rWrt.mbReqIF && pAltImgMap))
{
OUString aMapURL;
OUString aName;
OUString aTarget;
if(pURLItem)
{
aMapURL = pURLItem->GetURL();
aName = pURLItem->GetName();
aTarget = pURLItem->GetTargetFrameName();
}
else if (rWrt.mbReqIF && pAltImgMap)
{
// Get first non-empty map element
for (size_t i = 0; i < pAltImgMap->GetIMapObjectCount(); ++i)
{
if (auto* pIMapObject = pAltImgMap->GetIMapObject(i))
{
aMapURL = pIMapObject->GetURL();
aName = pIMapObject->GetName();
aTarget = pIMapObject->GetTarget();
if (!aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty())
break;
}
}
}
bool bEvents = pMacItem && !pMacItem->GetMacroTable().empty();
if( !aMapURL.isEmpty() || !aName.isEmpty() || !aTarget.isEmpty() || bEvents )
{
rHtml.start(OOO_STRING_SVTOOLS_HTML_anchor ""_ostr);
// Output "href" element if a link or macro exists
if( !aMapURL.isEmpty() || bEvents )
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_href, rWrt.convertHyperlinkHRefValue(aMapURL));
}
if( !aName.isEmpty() )
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_name, aName);
}
if( !aTarget.isEmpty() )
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_target, aTarget);
}
if( pMacItem )
{
const SvxMacroTableDtor& rMacTable = pMacItem->GetMacroTable();
if (!rMacTable.empty())
{
HtmlWriterHelper::applyEvents(rHtml, rMacTable, aAnchorEventTable, rWrt.m_bCfgStarBasic);
}
}
}
}
// <font color = ...>...<img ... >...</font>
sal_uInt16 nBorderWidth = 0;
const SvxBoxItem* pBoxItem;
if( (nFrameOpts & HtmlFrmOpts::Border) &&
(pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
{
Size aTwipBorder( 0, 0 );
const ::editeng::SvxBorderLine *pColBorderLine = nullptr;
const ::editeng::SvxBorderLine *pBorderLine = pBoxItem->GetLeft();
if( pBorderLine )
{
pColBorderLine = pBorderLine;
aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
}
pBorderLine = pBoxItem->GetRight();
if( pBorderLine )
{
pColBorderLine = pBorderLine;
aTwipBorder.AdjustWidth(pBorderLine->GetOutWidth() );
}
pBorderLine = pBoxItem->GetTop();
if( pBorderLine )
{
pColBorderLine = pBorderLine;
aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
}
pBorderLine = pBoxItem->GetBottom();
if( pBorderLine )
{
pColBorderLine = pBorderLine;
aTwipBorder.AdjustHeight(pBorderLine->GetOutWidth() );
}
aTwipBorder.setWidth( aTwipBorder.Width() / 2 );
aTwipBorder.setHeight( aTwipBorder.Height() / 2 );
if( (aTwipBorder.Width() || aTwipBorder.Height()) &&
Application::GetDefaultDevice() )
{
Size aPixelBorder = SwHTMLWriter::ToPixel(aTwipBorder);
if( aPixelBorder.Width() )
aPixelBorder.setHeight( 0 );
nBorderWidth =
o3tl::narrowing<sal_uInt16>(aPixelBorder.Width() + aPixelBorder.Height());
}
if( pColBorderLine )
{
rHtml.start(OOO_STRING_SVTOOLS_HTML_font ""_ostr);
HtmlWriterHelper::applyColor(rHtml, OOO_STRING_SVTOOLS_HTML_O_color, pColBorderLine->GetColor());
}
}
OString aTag(OOO_STRING_SVTOOLS_HTML_image ""_ostr);
if (bReplacement)
// Write replacement graphic of OLE object as <object>.
aTag = OOO_STRING_SVTOOLS_HTML_object ""_ostr;
rHtml.start(aTag);
if(rWrt.mbEmbedImages)
{
OUString aGraphicInBase64;
if (XOutBitmap::GraphicToBase64(rGraphic, aGraphicInBase64))
{
OString sBuffer(OString::Concat(OOO_STRING_SVTOOLS_HTML_O_data)
+ ":"
+ OUStringToOString(aGraphicInBase64, RTL_TEXTENCODING_UTF8));
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_src, sBuffer);
}
else
rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
}
else
{
OString sBuffer(OUStringToOString(aGraphicURL, RTL_TEXTENCODING_UTF8));
OString aAttribute(OOO_STRING_SVTOOLS_HTML_O_src ""_ostr);
if (bReplacement)
aAttribute = OOO_STRING_SVTOOLS_HTML_O_data ""_ostr;
rHtml.attribute(aAttribute, sBuffer);
}
if (bReplacement)
{
// Handle XHTML type attribute for OLE replacement images.
if (!rMimeType.isEmpty())
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_type, rMimeType);
}
// Events
if (const SvxMacroItem* pMacroItem = rItemSet.GetItemIfSet(RES_FRMMACRO))
{
const SvxMacroTableDtor& rMacTable = pMacroItem->GetMacroTable();
if (!rMacTable.empty())
{
HtmlWriterHelper::applyEvents(rHtml, rMacTable, aImageEventTable, rWrt.m_bCfgStarBasic);
}
}
// alt, align, width, height, hspace, vspace
rWrt.writeFrameFormatOptions(rHtml, rFrameFormat, rAlternateText, nFrameOpts);
if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) )
rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameOpts );
if ((nFrameOpts & HtmlFrmOpts::Border) && !bReplacement)
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_border, nBorderWidth);
}
if( pURLItem && pURLItem->IsServerMap() )
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_ismap);
}
if( !aIMapName.isEmpty() )
{
rHtml.attribute(OOO_STRING_SVTOOLS_HTML_O_usemap, "#" + aIMapName);
}
rWrt.writeFrameSurroundTag(rHtml, rFrameFormat, nFrameOpts);
if (bReplacement)
{
OUString aAltText = rAlternateText;
// In ReqIF mode, output text from the frame instead
if (rWrt.mbReqIF)
if (OUString aFrameText = getFrameFormatText(rFrameFormat); !aFrameText.isEmpty())
aAltText = aFrameText;
// XHTML object replacement image's alternate text doesn't use the
// "alt" attribute.
if (aAltText.isEmpty())
// Empty alternate text is not valid.
rHtml.characters(" ");
else
rHtml.characters(aAltText.toUtf8());
}
return rWrt;
}
SwHTMLWriter& OutHTML_ImageEnd( HtmlWriter& rHtml, SwHTMLWriter& rWrt )
{
rHtml.flushStack();
if( !rWrt.m_aINetFormats.empty() )
{
// There is still an attribute on the stack that has to be reopened
SwFormatINetFormat *pINetFormat = rWrt.m_aINetFormats.back();
OutHTML_INetFormat( rWrt, *pINetFormat, true );
}
return rWrt;
}
SwHTMLWriter& OutHTML_BulletImage( SwHTMLWriter& rWrt,
const char *pTag,
const SvxBrushItem* pBrush,
const OUString &rGraphicURL)
{
OUString aGraphicInBase64;
OUString aLink;
bool bOwn = false;
if( pBrush )
{
aLink = pBrush->GetGraphicLink();
if(rWrt.mbEmbedImages || aLink.isEmpty())
{
const Graphic* pGrf = pBrush->GetGraphic();
if( pGrf )
{
if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
{
rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
}
}
}
else
{
if( rWrt.m_bCfgCpyLinkedGrfs )
{
bOwn = rWrt.CopyLocalFileToINet(aLink);
}
}
}
else if(!rWrt.mbEmbedImages)
{
aLink = rGraphicURL;
}
if(!aLink.isEmpty())
{
aLink = rWrt.normalizeURL(aLink, bOwn);
}
OStringBuffer sOut;
if( pTag )
sOut.append(OString::Concat("<") + pTag);
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
if(!aLink.isEmpty())
{
sOut.append(OOO_STRING_SVTOOLS_HTML_O_src "=\"");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( rWrt.Strm(), aLink );
}
else
{
sOut.append("list-style-image: url("
OOO_STRING_SVTOOLS_HTML_O_data ":");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( rWrt.Strm(), aGraphicInBase64 );
sOut.append(");");
}
sOut.append('\"');
if (pTag)
sOut.append('>');
rWrt.Strm().WriteOString( sOut );
return rWrt;
}
static SwHTMLWriter& OutHTML_FrameFormatTableNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
{
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
SwNodeOffset nEnd = rWrt.m_pDoc->GetNodes()[nStt-1]->EndOfSectionIndex();
OUString aCaption;
bool bTopCaption = false;
// Not const, because GetTable won't be const sometime later
SwNode *pNd = rWrt.m_pDoc->GetNodes()[ nStt ];
SwTableNode *pTableNd = pNd->GetTableNode();
const SwTextNode *pTextNd = pNd->GetTextNode();
if( !pTableNd && pTextNd )
{
// Table with heading
bTopCaption = true;
pTableNd = rWrt.m_pDoc->GetNodes()[nStt+1]->GetTableNode();
}
OSL_ENSURE( pTableNd, "Frame does not contain a table" );
if( pTableNd )
{
SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex();
OSL_ENSURE( nTableEnd == nEnd - 1 ||
(nTableEnd == nEnd - 2 && !bTopCaption),
"Invalid frame content for a table" );
if( nTableEnd == nEnd - 2 )
pTextNd = rWrt.m_pDoc->GetNodes()[nTableEnd+1]->GetTextNode();
}
if( pTextNd )
aCaption = pTextNd->GetText();
if( pTableNd )
{
HTMLSaveData aSaveData( rWrt, pTableNd->GetIndex()+1,
pTableNd->EndOfSectionIndex(),
true, &rFrameFormat );
rWrt.m_bOutFlyFrame = true;
OutHTML_SwTableNode( rWrt, *pTableNd, &rFrameFormat, &aCaption,
bTopCaption );
}
return rWrt;
}
static SwHTMLWriter & OutHTML_FrameFormatAsMulticol( SwHTMLWriter& rWrt,
const SwFrameFormat& rFrameFormat,
bool bInCntnr )
{
rWrt.ChangeParaToken( HtmlTokenId::NONE );
// Close the current <DL>!
rWrt.OutAndSetDefList( 0 );
// output as Multicol
if (rWrt.IsLFPossible())
rWrt.OutNewLine();
OStringBuffer sOut("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol);
const SwFormatCol& rFormatCol = rFrameFormat.GetCol();
// output the number of columns as COLS
sal_uInt16 nCols = rFormatCol.GetNumCols();
if( nCols )
{
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cols
"=\"" + OString::number(nCols) + "\"");
}
// the Gutter width (minimum value) as GUTTER
sal_uInt16 nGutter = rFormatCol.GetGutterWidth( true );
if( nGutter!=USHRT_MAX )
{
nGutter = SwHTMLWriter::ToPixel(nGutter);
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_gutter
"=\"" + OString::number(nGutter) + "\"");
}
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
// WIDTH
HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_MULTICOL;
if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
nFrameFlags |= HTML_FRMOPTS_MULTICOL_CSS1;
rWrt.OutFrameFormatOptions(rFrameFormat, u"", nFrameFlags);
if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
rWrt.Strm().WriteChar( '>' );
rWrt.SetLFPossible(true);
rWrt.IncIndentLevel(); // indent the content of Multicol
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
assert(pSttNd && "Where is the start node");
{
// in a block, so that the old state can be restored in time
// before the end
HTMLSaveData aSaveData( rWrt, nStt+1,
pSttNd->EndOfSectionIndex(),
true, &rFrameFormat );
rWrt.m_bOutFlyFrame = true;
rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
}
rWrt.DecIndentLevel(); // indent the content of Multicol;
if (rWrt.IsLFPossible())
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_multicol), false );
rWrt.SetLFPossible(true);
return rWrt;
}
static SwHTMLWriter& OutHTML_FrameFormatAsSpacer( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat )
{
// if possible, output a line break before the graphic
if (rWrt.IsLFPossible())
rWrt.OutNewLine( true );
OString sOut =
"<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_spacer " "
OOO_STRING_SVTOOLS_HTML_O_type "=\""
OOO_STRING_SVTOOLS_HTML_SPTYPE_block "\"";
rWrt.Strm().WriteOString( sOut );
// ALIGN, WIDTH, HEIGHT
OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, u"", HTML_FRMOPTS_SPACER);
rWrt.Strm().WriteChar( '>' );
if( !aEndTags.isEmpty() )
rWrt.Strm().WriteOString( aEndTags );
return rWrt;
}
static SwHTMLWriter& OutHTML_FrameFormatAsDivOrSpan( SwHTMLWriter& rWrt,
const SwFrameFormat& rFrameFormat, bool bSpan)
{
OString aTag;
if( !bSpan )
{
rWrt.ChangeParaToken( HtmlTokenId::NONE );
// Close the current <DL>!
rWrt.OutAndSetDefList( 0 );
aTag = OOO_STRING_SVTOOLS_HTML_division ""_ostr;
}
else
aTag = OOO_STRING_SVTOOLS_HTML_span ""_ostr;
// output as DIV
if (rWrt.IsLFPossible())
rWrt.OutNewLine();
OStringBuffer sOut("<" + rWrt.GetNamespace() + aTag);
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HtmlFrmOpts nFrameFlags = HTML_FRMOPTS_DIV;
if( rWrt.IsHTMLMode( HTMLMODE_BORDER_NONE ) )
nFrameFlags |= HtmlFrmOpts::SNoBorder;
OString aEndTags = rWrt.OutFrameFormatOptions(rFrameFormat, u"", nFrameFlags);
rWrt.OutCSS1_FrameFormatOptions( rFrameFormat, nFrameFlags );
rWrt.Strm().WriteChar( '>' );
rWrt.IncIndentLevel(); // indent the content
rWrt.SetLFPossible(true);
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
// Output frame-anchored frames that are anchored to the start node
rWrt.OutFlyFrame( nStt, 0, HtmlPosition::Any );
const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
assert(pSttNd && "Where is the start node");
{
// in a block, so that the old state can be restored in time
// before the end
HTMLSaveData aSaveData( rWrt, nStt+1,
pSttNd->EndOfSectionIndex(),
true, &rFrameFormat );
rWrt.m_bOutFlyFrame = true;
rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
}
rWrt.DecIndentLevel(); // indent the content of Multicol;
if (rWrt.IsLFPossible())
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aTag), false );
if( !aEndTags.isEmpty() )
rWrt.Strm().WriteOString( aEndTags );
return rWrt;
}
/// Starts the OLE version of an image in the ReqIF + OLE case.
static void OutHTML_ImageOLEStart(SwHTMLWriter& rWrt, const Graphic& rGraphic,
const SwFrameFormat& rFrameFormat)
{
if (!rWrt.mbReqIF || !rWrt.m_bExportImagesAsOLE)
return;
// Write the original image as an RTF fragment.
OUString aFileName;
if (rWrt.GetOrigFileName())
aFileName = *rWrt.GetOrigFileName();
INetURLObject aURL(aFileName);
OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_"
+ OUString::number(rGraphic.GetChecksum(), 16);
aURL.setBase(aName);
aURL.setExtension(u"ole");
aFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
SvFileStream aOutStream(aFileName, StreamMode::WRITE);
if (!SwReqIfReader::WrapGraphicInRtf(rGraphic, rFrameFormat, aOutStream))
SAL_WARN("sw.html", "SwReqIfReader::WrapGraphicInRtf() failed");
// Refer to this data.
aFileName = rWrt.normalizeURL(aFileName, true);
rWrt.Strm().WriteOString(
Concat2View("<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object));
rWrt.Strm().WriteOString(Concat2View(" data=\"" + aFileName.toUtf8() + "\""));
rWrt.Strm().WriteOString(" type=\"text/rtf\"");
rWrt.Strm().WriteOString(">");
rWrt.OutNewLine();
}
/// Ends the OLE version of an image in the ReqIF + OLE case.
static void OutHTML_ImageOLEEnd(SwHTMLWriter& rWrt)
{
if (rWrt.mbReqIF && rWrt.m_bExportImagesAsOLE)
{
rWrt.Strm().WriteOString(
Concat2View("</" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_object ">"));
}
}
static SwHTMLWriter & OutHTML_FrameFormatAsImage( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat, bool bPNGFallback)
{
bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
if (rWrt.mbSkipImages)
return rWrt;
ImageMap aIMap;
std::optional<Size> aDPI;
if (rWrt.m_nShapeDPI.has_value())
{
aDPI.emplace(*rWrt.m_nShapeDPI, *rWrt.m_nShapeDPI);
}
Graphic aGraphic( const_cast<SwFrameFormat &>(rFrameFormat).MakeGraphic( &aIMap, /*nMaximumQuadraticPixels=*/2100000, aDPI ) );
if (rWrt.mbReqIF)
{
// ImageMap doesn't seem to be allowed in reqif.
if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(rFrameFormat.FindSdrObject()))
{
aGraphic = pGrafObj->GetGraphic();
}
else
{
// We only have a bitmap, write that as PNG without any fallback.
bWritePNGFallback = false;
}
}
Size aSz( 0, 0 );
OUString GraphicURL;
OUString aMimeType(u"image/jpeg"_ustr);
if(!rWrt.mbEmbedImages)
{
if( rWrt.GetOrigFileName() )
GraphicURL = *rWrt.GetOrigFileName();
OUString aFilterName(u"JPG"_ustr);
XOutFlags nFlags = XOutFlags::UseGifIfPossible | XOutFlags::UseNativeIfPossible;
if (rWrt.mbReqIF && !bWritePNGFallback)
{
// Writing image without fallback PNG in ReqIF mode: force PNG output.
aFilterName = "PNG";
nFlags = XOutFlags::NONE;
aMimeType = "image/png";
}
else if (rWrt.mbReqIF)
{
// Original format is wanted, don't force JPG.
aFilterName.clear();
aMimeType.clear();
}
if( aGraphic.GetType() == GraphicType::NONE ||
XOutBitmap::WriteGraphic( aGraphic, GraphicURL,
aFilterName,
nFlags ) != ERRCODE_NONE )
{
// empty or incorrect, because there is nothing to output
rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
return rWrt;
}
GraphicURL = URIHelper::SmartRel2Abs(
INetURLObject(rWrt.GetBaseURL()), GraphicURL,
URIHelper::GetMaybeFileHdl() );
}
uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
if (xGraphic.is() && aMimeType.isEmpty())
xGraphic->getPropertyValue(u"MimeType"_ustr) >>= aMimeType;
OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, GraphicURL, aGraphic, rFrameFormat.GetName(), aSz,
HtmlFrmOpts::GenImgMask, "frame",
aIMap.GetIMapObjectCount() ? &aIMap : nullptr, aMimeType, true);
GfxLink aLink = aGraphic.GetGfxLink();
if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
{
OutHTML_FrameFormatAsImage( rWrt, rFrameFormat, /*bPNGFallback=*/false);
}
OutHTML_ImageEnd(aHtml, rWrt);
OutHTML_ImageOLEEnd(rWrt);
return rWrt;
}
static SwHTMLWriter& OutHTML_FrameFormatGrfNode( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
bool bInCntnr, bool bPNGFallback )
{
bool bWritePNGFallback = rWrt.mbReqIF && !rWrt.m_bExportImagesAsOLE && bPNGFallback;
if (rWrt.mbSkipImages)
return rWrt;
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex()+1;
SwGrfNode *pGrfNd = rWrt.m_pDoc->GetNodes()[ nStt ]->GetGrfNode();
OSL_ENSURE( pGrfNd, "Grf node expected" );
if( !pGrfNd )
return rWrt;
HtmlFrmOpts nFrameFlags = bInCntnr ? HTML_FRMOPTS_IMG_CNTNR : HTML_FRMOPTS_IMG;
if( rWrt.IsHTMLMode( HTMLMODE_ABS_POS_FLY ) && !bInCntnr )
nFrameFlags |= HTML_FRMOPTS_IMG_CSS1;
Graphic aGraphic = pGrfNd->GetGraphic();
if (aGraphic.GetType() == GraphicType::GdiMetafile)
{
// We only have a metafile, write that as PNG without any fallback.
bWritePNGFallback = false;
}
OUString aGraphicURL;
OUString aMimeType;
bool bOwn = false;
if(!rWrt.mbEmbedImages)
{
const SwMirrorGrf& rMirror = pGrfNd->GetSwAttrSet().GetMirrorGrf();
if( !pGrfNd->IsLinkedFile() || MirrorGraph::Dont != rMirror.GetValue() )
{
// create a (mirrored) jpeg file
if( rWrt.GetOrigFileName() )
aGraphicURL = *rWrt.GetOrigFileName();
else
aGraphicURL = rWrt.GetBaseURL();
pGrfNd->GetGrf( true );
XOutFlags nFlags = XOutFlags::UseGifIfSensible |
XOutFlags::UseNativeIfPossible;
switch( rMirror.GetValue() )
{
case MirrorGraph::Vertical: nFlags = XOutFlags::MirrorHorz; break;
case MirrorGraph::Horizontal: nFlags = XOutFlags::MirrorVert; break;
case MirrorGraph::Both:
nFlags = XOutFlags::MirrorVert | XOutFlags::MirrorHorz;
break;
default: break;
}
const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
Size aMM100Size = o3tl::convert( rSize.GetSize(),
o3tl::Length::twip, o3tl::Length::mm100 );
OUString aFilterName;
if (rWrt.mbReqIF)
{
// In ReqIF mode, do not try to write GIF for other image types
nFlags &= ~XOutFlags::UseGifIfSensible;
if (!bWritePNGFallback)
{
// Writing image without fallback PNG in ReqIF mode: force PNG
// output.
// But don't force it when writing the original format and we'll write PNG inside
// that.
aFilterName = "PNG";
nFlags &= ~XOutFlags::UseNativeIfPossible;
}
}
const Graphic& rGraphic = pGrfNd->GetGrf();
// So that Graphic::IsTransparent() can report true.
if (!rGraphic.isAvailable())
const_cast<Graphic&>(rGraphic).makeAvailable();
if (rWrt.mbReqIF && bWritePNGFallback)
{
// ReqIF: force native data if possible.
const std::shared_ptr<VectorGraphicData>& pVectorGraphicData = rGraphic.getVectorGraphicData();
if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Svg)
{
aFilterName = "svg";
}
else if (rGraphic.GetGfxLink().IsEMF())
{
aFilterName = "emf";
}
else if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Wmf)
{
aFilterName = "wmf";
}
else if (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativeTif)
{
aFilterName = "tif";
}
}
ErrCode nErr = XOutBitmap::WriteGraphic( rGraphic, aGraphicURL,
aFilterName, nFlags, &aMM100Size, nullptr, &aMimeType );
if( nErr )
{
rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
return rWrt;
}
aGraphicURL = URIHelper::SmartRel2Abs(
INetURLObject(rWrt.GetBaseURL()), aGraphicURL,
URIHelper::GetMaybeFileHdl() );
bOwn = true;
}
else
{
pGrfNd->GetFileFilterNms( &aGraphicURL, nullptr );
if( rWrt.m_bCfgCpyLinkedGrfs )
bOwn = rWrt.CopyLocalFileToINet(aGraphicURL);
}
}
uno::Reference<beans::XPropertySet> xGraphic(aGraphic.GetXGraphic(), uno::UNO_QUERY);
if (xGraphic.is() && aMimeType.isEmpty())
xGraphic->getPropertyValue(u"MimeType"_ustr) >>= aMimeType;
OutHTML_ImageOLEStart(rWrt, aGraphic, rFrameFormat);
HtmlWriter aHtml(rWrt.Strm(), rWrt.maNamespace);
OutHTML_ImageStart( aHtml, rWrt, rFrameFormat, aGraphicURL, aGraphic, pGrfNd->GetTitle(),
pGrfNd->GetTwipSize(), nFrameFlags, "graphic", nullptr, aMimeType, bOwn );
GfxLink aLink = aGraphic.GetGfxLink();
if (bWritePNGFallback && aLink.GetType() != GfxLinkType::NativePng)
{
// Not OLE mode, outer format is not PNG: write inner PNG.
OutHTML_FrameFormatGrfNode( rWrt, rFrameFormat,
bInCntnr, /*bPNGFallback=*/false );
}
OutHTML_ImageEnd(aHtml, rWrt);
OutHTML_ImageOLEEnd(rWrt);
return rWrt;
}
static SwHTMLWriter& OutHTML_FrameFormatAsMarquee( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
const SdrObject& rSdrObj )
{
// get the edit engine attributes of the object as SW attributes and
// sort them as Hints
const SfxItemSet& rFormatItemSet = rFrameFormat.GetAttrSet();
SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( *rFormatItemSet.GetPool() );
SwHTMLWriter::GetEEAttrsFromDrwObj( aItemSet, &rSdrObj );
bool bCfgOutStylesOld = rWrt.m_bCfgOutStyles;
rWrt.m_bCfgOutStyles = false;
rWrt.m_bTextAttr = true;
rWrt.m_bTagOn = true;
Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
rWrt.m_bTextAttr = false;
OutHTML_DrawFrameFormatAsMarquee( rWrt,
static_cast<const SwDrawFrameFormat &>(rFrameFormat),
rSdrObj );
rWrt.m_bTextAttr = true;
rWrt.m_bTagOn = false;
Out_SfxItemSet( aHTMLAttrFnTab, rWrt, aItemSet, false );
rWrt.m_bTextAttr = false;
rWrt.m_bCfgOutStyles = bCfgOutStylesOld;
return rWrt;
}
SwHTMLWriter& OutHTML_HeaderFooter( SwHTMLWriter& rWrt, const SwFrameFormat& rFrameFormat,
bool bHeader )
{
// output as Multicol
rWrt.OutNewLine();
OStringBuffer sOut;
sOut.append(OOO_STRING_SVTOOLS_HTML_division " "
OOO_STRING_SVTOOLS_HTML_O_title "=\"")
.append( bHeader ? "header" : "footer" ).append("\"");
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + sOut) );
rWrt.IncIndentLevel(); // indent the content of Multicol;
// Piece a spacer for the spacing together. Because the
// <DL> or </DL> always produces a space between paragraphs, it is
// subtracted if necessary.
const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace();
sal_uInt16 nSize = bHeader ? rULSpace.GetLower() : rULSpace.GetUpper();
rWrt.m_nHeaderFooterSpace = nSize;
OString aSpacer;
if( rWrt.IsHTMLMode(HTMLMODE_VERT_SPACER) &&
nSize > HTML_PARSPACE )
{
nSize -= HTML_PARSPACE;
nSize = SwHTMLWriter::ToPixel(nSize);
aSpacer = OOO_STRING_SVTOOLS_HTML_spacer
" " OOO_STRING_SVTOOLS_HTML_O_type
"=\"" OOO_STRING_SVTOOLS_HTML_SPTYPE_vertical "\""
" " OOO_STRING_SVTOOLS_HTML_O_size
"=\"" + OString::number(nSize) + "\"";
}
const SwFormatContent& rFlyContent = rFrameFormat.GetContent();
SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
const SwStartNode* pSttNd = rWrt.m_pDoc->GetNodes()[nStt]->GetStartNode();
assert(pSttNd && "Where is the start node");
if( !bHeader && !aSpacer.isEmpty() )
{
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
}
{
// in a block, so that the old state can be restored in time
// before the end. pFlyFormat doesn't need to be set here, because
// PageDesc attributes cannot occur here
HTMLSaveData aSaveData( rWrt, nStt+1,
pSttNd->EndOfSectionIndex() );
if( bHeader )
rWrt.m_bOutHeader = true;
else
rWrt.m_bOutFooter = true;
rWrt.Out_SwDoc( rWrt.m_pCurrentPam.get() );
}
if( bHeader && !aSpacer.isEmpty() )
{
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + aSpacer) );
}
rWrt.DecIndentLevel(); // indent the content of Multicol;
rWrt.OutNewLine();
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
rWrt.m_nHeaderFooterSpace = 0;
return rWrt;
}
void SwHTMLWriter::AddLinkTarget( std::u16string_view aURL )
{
if( aURL.empty() || aURL[0] != '#' )
return;
// There might be a '|' as delimiter (if the link has been inserted
// freshly) or a '%7c' or a '%7C' if the document has been saved and
// loaded already.
sal_Int32 nPos = aURL.size();
bool bFound = false, bEncoded = false;
while( !bFound && nPos > 0 )
{
sal_Unicode c = aURL[ --nPos ];
switch( c )
{
case cMarkSeparator:
bFound = true;
break;
case '%':
bFound = (aURL.size() - nPos) >=3 && aURL[ nPos+1 ] == '7';
if(bFound)
{
c = aURL[ nPos+2 ];
bFound = (c == 'C' || c == 'c');
}
if( bFound )
bEncoded = true;
}
}
if( !bFound || nPos < 2 ) // at least "#a|..."
return;
aURL = aURL.substr( 1 );
// nPos-1+1/3 (-1 because of Erase)
OUString sCmp = OUString(aURL.substr(bEncoded ? nPos+2 : nPos)).replaceAll(" ","");
if( sCmp.isEmpty() )
return;
sCmp = sCmp.toAsciiLowerCase();
if( sCmp == "region" ||
sCmp == "frame" ||
sCmp == "graphic" ||
sCmp == "ole" ||
sCmp == "table" )
{
// Just remember it in a sorted array
OUString aURL2(aURL);
if( bEncoded )
{
aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
}
m_aImplicitMarks.insert( aURL2 );
}
else if( sCmp == "outline" )
{
// Here, we need position and name. That's why we sort a
// sal_uInt16 and a string array ourselves.
OUString aOutline( aURL.substr( 0, nPos-1 ) );
SwPosition aPos( *m_pCurrentPam->GetPoint() );
if( m_pDoc->GotoOutline( aPos, aOutline ) )
{
SwNodeOffset nIdx = aPos.GetNodeIndex();
decltype(m_aOutlineMarkPoss)::size_type nIns=0;
while( nIns < m_aOutlineMarkPoss.size() &&
m_aOutlineMarkPoss[nIns] < nIdx )
nIns++;
m_aOutlineMarkPoss.insert( m_aOutlineMarkPoss.begin()+nIns, nIdx );
OUString aURL2(aURL);
if( bEncoded )
{
aURL2 = aURL2.replaceAt( nPos - 1, 3, rtl::OUStringChar(cMarkSeparator) );
}
m_aOutlineMarks.insert( m_aOutlineMarks.begin()+nIns, aURL2 );
}
}
}
void SwHTMLWriter::CollectLinkTargets()
{
m_pDoc->ForEachINetFormat(
[this] (const SwFormatINetFormat& rINetFormat) -> bool
{
AddLinkTarget( rINetFormat.GetValue() );
return true;
});
m_pDoc->ForEachFormatURL(
[this] (const SwFormatURL& rURL) -> bool
{
AddLinkTarget( rURL.GetURL() );
const ImageMap *pIMap = rURL.GetMap();
if( pIMap )
{
for( size_t i=0; i<pIMap->GetIMapObjectCount(); ++i )
{
const IMapObject* pObj = pIMap->GetIMapObject( i );
if( pObj )
{
AddLinkTarget( pObj->GetURL() );
}
}
}
return true;
});
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'OutFrameFormatOptions' is required to be utilized.