/* -*- 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 <sal/config.h>
#include <string_view>
#include <osl/diagnose.h>
#include <svtools/htmlout.hxx>
#include <svtools/htmlkywd.hxx>
#include <svtools/HtmlWriter.hxx>
#include <rtl/strbuf.hxx>
#include <ndindex.hxx>
#include <fmtftn.hxx>
#include <txtftn.hxx>
#include <ftninfo.hxx>
#include <doc.hxx>
#include <ndtxt.hxx>
#include <charfmt.hxx>
#include "swhtml.hxx"
#include "wrthtml.hxx"
static sal_Int32 lcl_html_getNextPart( OUString& rPart, std::u16string_view aContent,
sal_Int32 nPos )
{
rPart.clear();
sal_Int32 nLen = aContent.size();
if( nPos >= nLen )
{
nPos = -1;
}
else
{
bool bQuoted = false, bDone = false;
for( ; nPos < nLen && !bDone; nPos++ )
{
sal_Unicode c = aContent[nPos];
switch( c )
{
case '\\':
if( bQuoted )
rPart += OUStringChar( c );
bQuoted = !bQuoted;
break;
case ';':
if( bQuoted )
rPart += OUStringChar( c );
else
bDone = true;
bQuoted = false;
break;
default:
rPart += OUStringChar( c );
bQuoted = false;
break;
}
}
}
return nPos;
}
static sal_Int32 lcl_html_getEndNoteInfo( SwEndNoteInfo& rInfo,
std::u16string_view aContent,
bool bEndNote )
{
sal_Int32 nStrPos = 0;
for( int nPart = 0; nPart < 4; ++nPart )
{
OUString aPart;
if( -1 != nStrPos )
nStrPos = lcl_html_getNextPart( aPart, aContent, nStrPos );
switch( nPart )
{
case 0:
rInfo.m_aFormat.SetNumberingType( bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC );
if( !aPart.isEmpty() )
rInfo.m_aFormat.SetNumberingType(SwHTMLParser::GetNumType( aPart,
rInfo.m_aFormat.GetNumberingType() ));
break;
case 1:
rInfo.m_nFootnoteOffset = aPart.isEmpty() ? 0 : o3tl::narrowing<sal_uInt16>(aPart.toInt32());
break;
case 2:
rInfo.SetPrefix( aPart );
break;
case 3:
rInfo.SetSuffix( aPart );
break;
}
}
return nStrPos;
}
void SwHTMLParser::FillEndNoteInfo( std::u16string_view aContent )
{
SwEndNoteInfo aInfo( m_xDoc->GetEndNoteInfo() );
lcl_html_getEndNoteInfo( aInfo, aContent, true );
m_xDoc->SetEndNoteInfo( aInfo );
}
void SwHTMLParser::FillFootNoteInfo( std::u16string_view aContent )
{
SwFootnoteInfo aInfo( m_xDoc->GetFootnoteInfo() );
sal_Int32 nStrPos = lcl_html_getEndNoteInfo( aInfo, aContent, false );
for( int nPart = 4; nPart < 8; ++nPart )
{
OUString aPart;
if( -1 != nStrPos )
nStrPos = lcl_html_getNextPart( aPart, aContent, nStrPos );
switch( nPart )
{
case 4:
aInfo.m_eNum = FTNNUM_DOC;
if( !aPart.isEmpty() )
{
switch( aPart[0] )
{
case 'D': aInfo.m_eNum = FTNNUM_DOC; break;
case 'C': aInfo.m_eNum = FTNNUM_CHAPTER; break;
case 'P': aInfo.m_eNum = FTNNUM_PAGE; break;
}
}
break;
case 5:
aInfo.m_ePos = FTNPOS_PAGE;
if( !aPart.isEmpty() )
{
switch( aPart[0] )
{
case 'C': aInfo.m_ePos = FTNPOS_CHAPTER; break;
case 'P': aInfo.m_ePos = FTNPOS_PAGE; break;
}
}
break;
case 6:
aInfo.m_aQuoVadis = aPart;
break;
case 7:
aInfo.m_aErgoSum = aPart;
break;
}
}
m_xDoc->SetFootnoteInfo( aInfo );
}
void SwHTMLParser::InsertFootEndNote( const OUString& rName, bool bEndNote,
bool bFixed )
{
if( !m_pFootEndNoteImpl )
m_pFootEndNoteImpl.reset(new SwHTMLFootEndNote_Impl);
m_pFootEndNoteImpl->sName = rName;
if( m_pFootEndNoteImpl->sName.getLength() > 3 )
m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.copy( 0, m_pFootEndNoteImpl->sName.getLength() - 3 );
m_pFootEndNoteImpl->sName = m_pFootEndNoteImpl->sName.toAsciiUpperCase();
m_pFootEndNoteImpl->bEndNote = bEndNote;
m_pFootEndNoteImpl->bFixed = bFixed;
m_pFootEndNoteImpl->sContent.clear();
}
void SwHTMLParser::FinishFootEndNote()
{
if( !m_pFootEndNoteImpl )
return;
SwFormatFootnote aFootnote( m_pFootEndNoteImpl->bEndNote );
if( m_pFootEndNoteImpl->bFixed )
aFootnote.SetNumStr( m_pFootEndNoteImpl->sContent );
m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, aFootnote );
SwTextFootnote * const pTextFootnote = static_cast<SwTextFootnote *>(
m_pPam->GetPointNode().GetTextNode()->GetTextAttrForCharAt(
m_pPam->GetPoint()->GetContentIndex() - 1, RES_TXTATR_FTN ) );
// In header and footer no footnotes can be inserted.
if (pTextFootnote)
m_pFootEndNoteImpl->aTextFootnotes.emplace_back(m_pFootEndNoteImpl->sName,pTextFootnote);
m_pFootEndNoteImpl->sName.clear();
m_pFootEndNoteImpl->sContent.clear();
m_pFootEndNoteImpl->bFixed = false;
}
void SwHTMLParser::InsertFootEndNoteText()
{
if( m_pFootEndNoteImpl && m_pFootEndNoteImpl->bFixed )
m_pFootEndNoteImpl->sContent += aToken;
}
SwNodeIndex *SwHTMLParser::GetFootEndNoteSection( const OUString& rName )
{
SwNodeIndex *pStartNodeIdx = nullptr;
if (m_pFootEndNoteImpl)
{
OUString aName(rName.toAsciiUpperCase());
size_t nCount = m_pFootEndNoteImpl->aTextFootnotes.size();
for(size_t i = 0; i < nCount; ++i)
{
if (m_pFootEndNoteImpl->aTextFootnotes[i].GetName() == aName)
{
pStartNodeIdx = const_cast<SwNodeIndex*>(m_pFootEndNoteImpl->aTextFootnotes[i].GetStartNode());
m_pFootEndNoteImpl->aTextFootnotes.erase( m_pFootEndNoteImpl->aTextFootnotes.begin() + i );
if (m_pFootEndNoteImpl->aTextFootnotes.empty())
{
m_pFootEndNoteImpl.reset();
}
break;
}
}
}
return pStartNodeIdx;
}
SwHTMLWriter& OutHTML_SwFormatLineBreak(SwHTMLWriter& rWrt, const SfxPoolItem& rHt)
{
const auto& rLineBreak = static_cast<const SwFormatLineBreak&>(rHt);
HtmlWriter aWriter(rWrt.Strm(), rWrt.maNamespace);
aWriter.start(OOO_STRING_SVTOOLS_HTML_linebreak ""_ostr);
switch (rLineBreak.GetValue())
{
case SwLineBreakClear::NONE:
aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "none");
break;
case SwLineBreakClear::LEFT:
aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "left");
break;
case SwLineBreakClear::RIGHT:
aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "right");
break;
case SwLineBreakClear::ALL:
aWriter.attribute(OOO_STRING_SVTOOLS_HTML_O_clear, "all");
break;
}
aWriter.end();
return rWrt;
}
SwHTMLWriter& OutHTML_SwFormatFootnote( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
SwFormatFootnote& rFormatFootnote = const_cast<SwFormatFootnote&>(static_cast<const SwFormatFootnote&>(rHt));
SwTextFootnote *pTextFootnote = rFormatFootnote.GetTextFootnote();
if( !pTextFootnote )
return rWrt;
OUString sFootnoteName, sClass;
size_t nPos;
if( rFormatFootnote.IsEndNote() )
{
nPos = rWrt.m_xFootEndNotes ? rWrt.m_xFootEndNotes->size() : 0;
OSL_ENSURE( nPos == static_cast<size_t>(rWrt.m_nFootNote + rWrt.m_nEndNote),
"OutHTML_SwFormatFootnote: wrong position" );
sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_anc;
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number( static_cast<sal_Int32>(++rWrt.m_nEndNote) );
}
else
{
nPos = rWrt.m_nFootNote;
sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_anc;
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number( static_cast<sal_Int32>(++rWrt.m_nFootNote));
}
if( !rWrt.m_xFootEndNotes )
rWrt.m_xFootEndNotes.emplace();
rWrt.m_xFootEndNotes->insert( rWrt.m_xFootEndNotes->begin() + nPos, pTextFootnote );
OStringBuffer sOut;
OString aTag = rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor;
sOut.append("<" + aTag + " " OOO_STRING_SVTOOLS_HTML_O_class "=\"");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( rWrt.Strm(), sClass );
sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName );
sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor "\" "
OOO_STRING_SVTOOLS_HTML_O_href "=\"#");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( rWrt.Strm(), sFootnoteName );
sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol "\"");
if( !rFormatFootnote.GetNumStr().isEmpty() )
sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_sdfixed);
sOut.append(">");
rWrt.Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript ));
HTMLOutFuncs::Out_String( rWrt.Strm(), rFormatFootnote.GetViewNumStr(*rWrt.m_pDoc, nullptr) );
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_superscript), false );
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
return rWrt;
}
void SwHTMLWriter::OutFootEndNotes()
{
OSL_ENSURE( m_xFootEndNotes,
"SwHTMLWriter::OutFootEndNotes(): unnecessary call" );
if( !m_xFootEndNotes )
return;
#if OSL_DEBUG_LEVEL > 0
sal_uInt16 nFootnote = m_nFootNote, nEn = m_nEndNote;
#endif
m_nFootNote = 0;
m_nEndNote = 0;
for( auto *pTextFootnote : *m_xFootEndNotes )
{
m_pFormatFootnote = &pTextFootnote->GetFootnote();
OUString sFootnoteName;
if( m_pFormatFootnote->IsEndNote() )
{
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote + OUString::number(static_cast<sal_Int32>(++m_nEndNote));
}
else
{
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote + OUString::number(static_cast<sal_Int32>(++m_nFootNote));
}
if (IsLFPossible())
OutNewLine();
OString sOut =
"<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_division
" " OOO_STRING_SVTOOLS_HTML_O_id "=\"";
Strm().WriteOString( sOut );
HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
Strm().WriteOString( "\">" );
SetLFPossible(true);
IncIndentLevel(); // indent content of <DIV>
assert(pTextFootnote && "SwHTMLWriter::OutFootEndNotes: SwTextFootnote is missing");
const SwNodeIndex *pSttNdIdx = pTextFootnote->GetStartNode();
OSL_ENSURE( pSttNdIdx,
"SwHTMLWriter::OutFootEndNotes: StartNode-Index is missing" );
if( pSttNdIdx )
{
HTMLSaveData aSaveData( *this, pSttNdIdx->GetIndex()+1,
pSttNdIdx->GetNode().EndOfSectionIndex(), false );
Out_SwDoc( m_pCurrentPam.get() );
}
DecIndentLevel(); // indent content of <DIV>
if (IsLFPossible())
OutNewLine();
HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_division), false );
SetLFPossible(true);
OSL_ENSURE( !m_pFormatFootnote,
"SwHTMLWriter::OutFootEndNotes: Footnote was not output" );
if( m_pFormatFootnote )
{
if( m_pFormatFootnote->IsEndNote() )
m_nEndNote++;
else
m_nFootNote++;
m_pFormatFootnote = nullptr;
}
}
#if OSL_DEBUG_LEVEL > 0
OSL_ENSURE( nFootnote == m_nFootNote,
"SwHTMLWriter::OutFootEndNotes: Number of footnotes does not match" );
OSL_ENSURE( nEn == m_nEndNote,
"SwHTMLWriter::OutFootEndNotes: Number of endnotes does not match" );
#endif
m_xFootEndNotes.reset();
m_nFootNote = m_nEndNote = 0;
}
OUString SwHTMLWriter::GetFootEndNoteSym( const SwFormatFootnote& rFormatFootnote )
{
const SwEndNoteInfo * pInfo = nullptr;
if( rFormatFootnote.GetNumStr().isEmpty() )
pInfo = rFormatFootnote.IsEndNote() ? &m_pDoc->GetEndNoteInfo()
: &m_pDoc->GetFootnoteInfo();
OUString sRet;
if( pInfo )
sRet = pInfo->GetPrefix();
sRet += rFormatFootnote.GetViewNumStr(*m_pDoc, nullptr);
if( pInfo )
sRet += pInfo->GetSuffix();
return sRet;
}
void SwHTMLWriter::OutFootEndNoteSym( const SwFormatFootnote& rFormatFootnote,
std::u16string_view rNum,
sal_uInt16 nScript )
{
const SwEndNoteInfo *pInfo;
OUString sFootnoteName, sClass;
if( rFormatFootnote.IsEndNote() )
{
sClass = OOO_STRING_SVTOOLS_HTML_sdendnote_sym;
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdendnote +
OUString::number(static_cast<sal_Int32>(m_nEndNote));
pInfo = &m_pDoc->GetEndNoteInfo();
}
else
{
sClass = OOO_STRING_SVTOOLS_HTML_sdfootnote_sym;
sFootnoteName = OOO_STRING_SVTOOLS_HTML_sdfootnote +
OUString::number(static_cast<sal_Int32>(m_nFootNote));
pInfo = &m_pDoc->GetFootnoteInfo();
}
const SwCharFormat *pSymCharFormat = pInfo->GetCharFormat( *m_pDoc );
if( pSymCharFormat && 0 != m_aScriptTextStyles.count( pSymCharFormat->GetName() ) )
{
switch( nScript )
{
case CSS1_OUTMODE_WESTERN:
sClass += "-western";
break;
case CSS1_OUTMODE_CJK:
sClass += "-cjk";
break;
case CSS1_OUTMODE_CTL:
sClass += "-ctl";
break;
}
}
OStringBuffer sOut("<"
+ GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor " "
OOO_STRING_SVTOOLS_HTML_O_class "=\"");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), sClass );
sOut.append("\" " OOO_STRING_SVTOOLS_HTML_O_name "=\"");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_symbol "\" "
OOO_STRING_SVTOOLS_HTML_O_href "=\"#");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), sFootnoteName );
sOut.append(OOO_STRING_SVTOOLS_HTML_FTN_anchor "\">");
Strm().WriteOString( sOut );
sOut.setLength(0);
HTMLOutFuncs::Out_String( Strm(), rNum );
HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_anchor), false );
}
static int lcl_html_fillEndNoteInfo( const SwEndNoteInfo& rInfo,
OUString *pParts,
bool bEndNote )
{
int nParts = 0;
sal_Int16 eFormat = rInfo.m_aFormat.GetNumberingType();
if( (bEndNote ? SVX_NUM_ROMAN_LOWER : SVX_NUM_ARABIC) != eFormat )
{
const char *pStr = SwHTMLWriter::GetNumFormat( eFormat );
if( pStr )
{
pParts[0] = OUString::createFromAscii( pStr );
nParts = 1;
}
}
if( rInfo.m_nFootnoteOffset > 0 )
{
pParts[1] = OUString::number(rInfo.m_nFootnoteOffset);
nParts = 2;
}
if( !rInfo.GetPrefix().isEmpty() )
{
pParts[2] = rInfo.GetPrefix();
nParts = 3;
}
if( !rInfo.GetSuffix().isEmpty() )
{
pParts[3] = rInfo.GetSuffix();
nParts = 4;
}
return nParts;
}
static void lcl_html_outFootEndNoteInfo( SwHTMLWriter& rWrt, OUString const *pParts,
int nParts, const char *pName )
{
OUStringBuffer aContent;
for( int i=0; i<nParts; ++i )
{
OUString aTmp( pParts[i] );
aTmp = aTmp.replaceAll( "\\", "\\\\" );
aTmp = aTmp.replaceAll( ";", "\\;" );
if( i > 0 )
aContent.append(";");
aContent.append(aTmp);
}
rWrt.OutNewLine();
OString sOut =
"<" + rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_meta " "
OOO_STRING_SVTOOLS_HTML_O_name "=\"" + pName +
"\" " OOO_STRING_SVTOOLS_HTML_O_content "=\"";
rWrt.Strm().WriteOString( sOut );
HTMLOutFuncs::Out_String( rWrt.Strm(), aContent.makeStringAndClear() );
rWrt.Strm().WriteOString( "\">" );
}
void SwHTMLWriter::OutFootEndNoteInfo()
{
// Number type (1 or i)
// Offset (0)
// Before it
// Behind it
// Doc/Page/Chap (D)
// Position (S)
// Next page
// Beginning
{
const SwFootnoteInfo& rInfo = m_pDoc->GetFootnoteInfo();
OUString aParts[8];
int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, false );
if( rInfo.m_eNum != FTNNUM_DOC )
{
aParts[4] = rInfo.m_eNum == FTNNUM_CHAPTER ? std::u16string_view( u"C" ) : std::u16string_view( u"P" );
nParts = 5;
}
if( rInfo.m_ePos != FTNPOS_PAGE)
{
aParts[5] = "C";
nParts = 6;
}
if( !rInfo.m_aQuoVadis.isEmpty() )
{
aParts[6] = rInfo.m_aQuoVadis;
nParts = 7;
}
if( !rInfo.m_aErgoSum.isEmpty() )
{
aParts[7] = rInfo.m_aErgoSum;
nParts = 8;
}
if( nParts > 0 )
lcl_html_outFootEndNoteInfo( *this, aParts, nParts,
OOO_STRING_SVTOOLS_HTML_META_sdfootnote );
}
{
const SwEndNoteInfo& rInfo = m_pDoc->GetEndNoteInfo();
OUString aParts[4];
const int nParts = lcl_html_fillEndNoteInfo( rInfo, aParts, true );
if( nParts > 0 )
lcl_html_outFootEndNoteInfo( *this, aParts, nParts,
OOO_STRING_SVTOOLS_HTML_META_sdendnote );
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V595 The 'pTextFootnote' pointer was utilized before it was verified against nullptr. Check lines: 346, 370.
↑ V1028 Possible overflow. Consider casting operands, not the result.
↑ V1048 The 'aInfo.m_eNum' variable was assigned the same value.
↑ V1048 The 'aInfo.m_ePos' variable was assigned the same value.