/* -*- 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 <hintids.hxx>
#include <comphelper/string.hxx>
#include <comphelper/xmlencode.hxx>
#include <vcl/svapp.hxx>
#include <svl/whiter.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/blinkitem.hxx>
#include <editeng/cmapitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/widwitem.hxx>
#include <editeng/spltitem.hxx>
#include <editeng/orphitem.hxx>
#include <editeng/charhiddenitem.hxx>
#include <svx/xoutbmp.hxx>
#include <svx/svdobj.hxx>
#include <editeng/langitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <svtools/htmlout.hxx>
#include <svtools/htmlkywd.hxx>
#include <svl/urihelper.hxx>
#include <unotools/charclass.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <charfmt.hxx>
#include <fmtclds.hxx>
#include <fmtcol.hxx>
#include <fmtfsize.hxx>
#include <fmtornt.hxx>
#include <fmtpdsc.hxx>
#include <fmtlsplt.hxx>
#include <pagedesc.hxx>
#include <fmtanchr.hxx>
#include <docary.hxx>
#include <pam.hxx>
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <swtable.hxx>
// NOTES
#include <ftninfo.hxx>
#include <ftnidx.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
// FOOTNOTES
#include <doc.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <swerror.h>
#include <paratr.hxx>
#include <frmatr.hxx>
#include <poolfmt.hxx>
#include "css1kywd.hxx"
#include "wrthtml.hxx"
#include "htmlnum.hxx"
#include "css1atr.hxx"
 
#include <IDocumentStylePoolAccess.hxx>
#include <numrule.hxx>
#include <o3tl/typed_flags_set.hxx>
#include <o3tl/unit_conversion.hxx>
 
#include <rtl/strbuf.hxx>
#include <osl/diagnose.h>
 
using namespace css;
using editeng::SvxBorderLine;
 
#define HTML_HEADSPACE (12*20)
 
namespace {
 
enum class Css1FrameSize {
    NONE       = 0x00,
    Width      = 0x01,
    MinHeight  = 0x02,
    FixHeight  = 0x04,
    Pixel      = 0x10,
};
 
}
 
namespace o3tl {
    template<> struct typed_flags<Css1FrameSize> : is_typed_flags<Css1FrameSize, 0x17> {};
}
 
constexpr int DOT_LEADERS_MAX_WIDTH = 18; // cm
 
static SwHTMLWriter& OutCSS1_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
                              IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate );
static SwHTMLWriter& OutCSS1_SwPageDesc( SwHTMLWriter& rWrt, const SwPageDesc& rFormat,
                                   IDocumentStylePoolAccess /*SwDoc*/ *pDoc, SwDoc *pTemplate,
                                   sal_uInt16 nRefPoolId, bool bExtRef,
                                   bool bPseudo=true );
static SwHTMLWriter& OutCSS1_SwFootnoteInfo( SwHTMLWriter& rWrt, const SwEndNoteInfo& rInfo,
                                  SwDoc *pDoc, bool bHasNotes, bool bEndNote );
static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt,
                                    const SwFormatDrop& rDrop,
                                     const SfxItemSet *pCharFormatItemSet=nullptr );
static SwHTMLWriter& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( SwHTMLWriter& rWrt,
                    const SvxUnderlineItem *pUItem,
                    const SvxOverlineItem *pOItem,
                    const SvxCrossedOutItem *pCOItem,
                    const SvxBlinkItem *pBItem );
static SwHTMLWriter& OutCSS1_SvxFontWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
static SwHTMLWriter& OutCSS1_SvxPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
static SwHTMLWriter& OutCSS1_SvxULSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
static SwHTMLWriter& OutCSS1_SvxFirstLineIndent(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
static SwHTMLWriter& OutCSS1_SvxTextLeftMargin(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
static SwHTMLWriter& OutCSS1_SvxRightMargin(SwHTMLWriter& rWrt, const SfxPoolItem& rHt);
static SwHTMLWriter& OutCSS1_SvxLRSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
                                        const SvxULSpaceItem *pULSpace,
                                        const SvxLRSpaceItem *pLRSpace );
static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
                                        const SfxItemSet& rItemSet );
static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
                                 sw::Css1Background nMode,
                                 const OUString *pGraphicName );
static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
static SwHTMLWriter& OutCSS1_SwFormatFrameSize( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
                                     Css1FrameSize nMode );
static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
                                        const SfxItemSet& rItemSet,
                                        bool bDeep );
static SwHTMLWriter& OutCSS1_SwFormatLayoutSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt );
 
namespace
{
 
const char sCSS1_rule_end[] = " }";
const char sCSS1_span_tag_end[] = "\">";
const char cCSS1_style_opt_end  = '\"';
 
const char* const sHTML_FTN_fontheight = "57%";
 
OString lclConvToHex(sal_uInt16 nHex)
{
    char aNToABuf[] = "00";
 
    // set pointer to end of buffer
    char *pStr = aNToABuf + (sizeof(aNToABuf)-1);
    for( sal_uInt8 n = 0; n < 2; ++n )
    {
        *(--pStr) = static_cast<char>(nHex & 0xf ) + 48;
        if( *pStr > '9' )
            *pStr += 39;
        nHex >>= 4;
    }
 
    return OString(aNToABuf, 2);
}
}
 
bool IgnorePropertyForReqIF(bool bReqIF, std::string_view rProperty, std::string_view rValue,
                            std::optional<sw::Css1Background> oMode)
{
    if (!bReqIF)
        return false;
 
    if (oMode.has_value() && *oMode != sw::Css1Background::TableCell)
    {
        // Table or row.
        if (rProperty == sCSS1_P_background && rValue == "transparent")
        {
            // This is the default already.
            return true;
        }
 
        return false;
    }
 
    // Only allow these two keys, nothing else in ReqIF mode.
    if (rProperty == sCSS1_P_text_decoration)
    {
        // Deny other text-decoration values (e.g. "none").
        if (rValue == "underline" || rValue == "line-through")
        {
            return false;
        }
 
        return true;
    }
 
    if (rProperty == sCSS1_P_color)
        return false;
 
    return true;
}
 
OString GetCSS1_Color(const Color& rColor)
{
    return "#" + lclConvToHex(rColor.GetRed()) + lclConvToHex(rColor.GetGreen()) + lclConvToHex(rColor.GetBlue());
}
 
namespace {
 
class SwCSS1OutMode
{
    SwHTMLWriter& rWrt;
    sal_uInt16 nOldMode;
 
public:
 
    SwCSS1OutMode( SwHTMLWriter& rHWrt, sal_uInt16 nMode,
                   const OUString *pSelector ) :
        rWrt( rHWrt ),
        nOldMode( rHWrt.m_nCSS1OutMode )
    {
        rWrt.m_nCSS1OutMode = nMode;
        rWrt.m_bFirstCSS1Property = true;
        if( pSelector )
            rWrt.m_aCSS1Selector = *pSelector;
    }
 
    ~SwCSS1OutMode()
    {
        rWrt.m_nCSS1OutMode = nOldMode;
    }
};
 
}
 
void SwHTMLWriter::OutCSS1_Property( std::string_view pProp,
                                     std::string_view sVal,
                                     const OUString *pSVal,
                                     std::optional<sw::Css1Background> oMode )
{
    OString aPropertyValue(sVal);
    if (aPropertyValue.isEmpty() && pSVal)
    {
        aPropertyValue = OUStringToOString(*pSVal, RTL_TEXTENCODING_UTF8);
    }
    if (IgnorePropertyForReqIF(mbReqIF, pProp, aPropertyValue, oMode))
        return;
 
    OStringBuffer sOut;
 
    if( m_bFirstCSS1Rule && (m_nCSS1OutMode & CSS1_OUTMODE_RULE_ON)!=0 )
    {
        m_bFirstCSS1Rule = false;
        OutNewLine();
        sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_style " "
                    OOO_STRING_SVTOOLS_HTML_O_type "=\"text/css\">");
    //  Optional CSS2 code for dot leaders (dotted line between the Table of Contents titles and page numbers):
    //  (More information: http://www.w3.org/Style/Examples/007/leaders.en.html)
    //
    //  p.leaders {
    //      /* FIXME:
    //         (1) dots line up vertically only in the paragraphs with the same alignment/level
    //         (2) max-width = 18 cm instead of 80em; possible improvement with the new CSS3 calc() */
    //      max-width: 18cm; /* note: need to overwrite max-width with max-width - border-left_of_the_actual_paragraph */
    //      padding: 0;
    //      overflow-x: hidden;
    //      line-height: 120%; /* note: avoid HTML scrollbars and missing descenders of the letters */
    //  }
    //  p.leaders:after {
    //      float: left;
    //      width: 0;
    //      white-space: nowrap;
    //      content: ". . . . . . . . . . . . . . . . . . ...";
    //  }
    //  p.leaders span:first-child {
    //      padding-right: 0.33em;
    //      background: white;
    //  }
    //  p.leaders span + span {
    //      float: right;
    //      padding-left: 0.33em;
    //      background: white;
    //      position: relative;
    //      z-index: 1
    //  }
 
        if (m_bCfgPrintLayout) {
            sOut.append(
                "p." sCSS2_P_CLASS_leaders "{max-width:" + OString::number(DOT_LEADERS_MAX_WIDTH) +
                    "cm;padding:0;overflow-x:hidden;line-height:120%}"
                "p." sCSS2_P_CLASS_leaders ":after{float:left;width:0;white-space:nowrap;content:\"");
            for (int i = 0; i < 100; i++ )
                sOut.append(". ");
            sOut.append(
                    "\"}p." sCSS2_P_CLASS_leaders " span:first-child{padding-right:0.33em;background:white}"
                    "p." sCSS2_P_CLASS_leaders " span+span{float:right;padding-left:0.33em;"
                    "background:white;position:relative;z-index:1}");
        }
        Strm().WriteOString( sOut );
        sOut.setLength(0);
 
        IncIndentLevel();
    }
 
    if( m_bFirstCSS1Property )
    {
        switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON )
        {
        case CSS1_OUTMODE_SPAN_TAG_ON:
        case CSS1_OUTMODE_SPAN_TAG1_ON:
            if( m_bTagOn )
            {
                sOut.append("<" + GetNamespace() + OOO_STRING_SVTOOLS_HTML_span
                            " " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
            }
            else
            {
                HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
                return;
            }
            break;
 
        case CSS1_OUTMODE_RULE_ON:
            {
                OutNewLine();
                sOut.append(OUStringToOString(m_aCSS1Selector, RTL_TEXTENCODING_UTF8) + " { ");
            }
            break;
 
        case CSS1_OUTMODE_STYLE_OPT_ON:
            sOut.append(" " OOO_STRING_SVTOOLS_HTML_O_style "=\"");
            break;
        }
        m_bFirstCSS1Property = false;
    }
    else
    {
        sOut.append("; ");
    }
 
    sOut.append(pProp + OString::Concat(": "));
    if( m_nCSS1OutMode & CSS1_OUTMODE_ENCODE )
    {
        // for STYLE-Option encode string
        Strm().WriteOString( sOut );
        sOut.setLength(0);
        if( !sVal.empty() )
            HTMLOutFuncs::Out_String( Strm(), OUString::createFromAscii(sVal) );
        else if( pSVal )
            HTMLOutFuncs::Out_String( Strm(), *pSVal );
    }
    else
    {
        // for STYLE-Tag print string directly
        sOut.append(aPropertyValue);
    }
 
    if (!sOut.isEmpty())
        Strm().WriteOString( sOut );
}
 
static void AddUnitPropertyValue(OStringBuffer &rOut, tools::Long nVal,
    FieldUnit eUnit)
{
    if( nVal < 0 )
    {
        // special-case sign symbol
        nVal = -nVal;
        rOut.append('-');
    }
 
    o3tl::Length eTo;
    int nFac; // used to get specific number of decimals
    std::string_view pUnit;
    switch( eUnit )
    {
    case FieldUnit::MM_100TH:
        OSL_ENSURE( FieldUnit::MM == eUnit, "Measuring unit not supported" );
        [[fallthrough]];
    case FieldUnit::MM:
        eTo = o3tl::Length::mm;
        nFac = 100;
        pUnit = sCSS1_UNIT_mm;
        break;
 
    case FieldUnit::M:
    case FieldUnit::KM:
        OSL_ENSURE( FieldUnit::CM == eUnit, "Measuring unit not supported" );
        [[fallthrough]];
    case FieldUnit::CM:
        eTo = o3tl::Length::cm;
        nFac = 100;
        pUnit = sCSS1_UNIT_cm;
        break;
 
    case FieldUnit::TWIP:
        OSL_ENSURE( FieldUnit::POINT == eUnit, "Measuring unit not supported" );
        [[fallthrough]];
    case FieldUnit::POINT:
        eTo = o3tl::Length::pt;
        nFac = 10;
        pUnit = sCSS1_UNIT_pt;
        break;
 
    case FieldUnit::PICA:
        eTo = o3tl::Length::pc;
        nFac = 100;
        pUnit = sCSS1_UNIT_pc;
        break;
 
    case FieldUnit::NONE:
    case FieldUnit::FOOT:
    case FieldUnit::MILE:
    case FieldUnit::CUSTOM:
    case FieldUnit::PERCENT:
    case FieldUnit::INCH:
    default:
        OSL_ENSURE( FieldUnit::INCH == eUnit, "Measuring unit not supported" );
        eTo = o3tl::Length::in;
        nFac = 100;
        pUnit = sCSS1_UNIT_inch;
        break;
    }
 
    sal_Int64 nResult = o3tl::convert(nVal * nFac, o3tl::Length::twip, eTo);
    rOut.append(nResult/nFac);
    if ((nResult % nFac) != 0)
    {
        rOut.append('.');
        while (nFac > 1 && (nResult % nFac) != 0)
        {
            nFac /= 10;
            rOut.append((nResult / nFac) % 10);
        }
    }
 
    rOut.append(pUnit);
}
 
void SwHTMLWriter::OutCSS1_UnitProperty( std::string_view pProp, tools::Long nVal )
{
    OStringBuffer sOut;
    AddUnitPropertyValue(sOut, nVal, m_eCSS1Unit);
    OutCSS1_PropertyAscii(pProp, sOut);
}
 
void SwHTMLWriter::OutCSS1_PixelProperty( std::string_view pProp, tools::Long nTwips )
{
    OString sOut(OString::number(ToPixel(nTwips)) + sCSS1_UNIT_px);
    OutCSS1_PropertyAscii(pProp, sOut);
}
 
void SwHTMLWriter::OutStyleSheet( const SwPageDesc& rPageDesc )
{
    m_bFirstCSS1Rule = true;
 
// Feature: PrintExt
    if( IsHTMLMode(HTMLMODE_PRINT_EXT) )
    {
        const SwPageDesc *pFirstPageDesc = nullptr;
        sal_uInt16 nFirstRefPoolId = RES_POOLPAGE_HTML;
        m_bCSS1IgnoreFirstPageDesc = true;
 
        // First we try to guess how the document is constructed.
        // Allowed are only the templates: HTML, 1st page, left page, and right page.
        // A first page is only exported, if it matches the template "1st page".
        // Left and right pages are only exported, if their templates are linked.
        // If other templates are used, only very simple cases are exported.
        const SwPageDesc *pPageDesc = &rPageDesc;
        const SwPageDesc *pFollow = rPageDesc.GetFollow();
        if( RES_POOLPAGE_FIRST == pPageDesc->GetPoolFormatId() &&
            pFollow != pPageDesc &&
            !IsPoolUserFormat( pFollow->GetPoolFormatId() ) )
        {
            // the document has a first page
            pFirstPageDesc = pPageDesc;
            pPageDesc = pFollow;
            pFollow = pPageDesc->GetFollow();
        }
 
        IDocumentStylePoolAccess* pStylePoolAccess = &getIDocumentStylePoolAccess();
        if( pPageDesc == pFollow )
        {
            // The document is one-sided; no matter what page, we do not create a 2-sided doc.
            // The attribute is exported relative to the HTML page template.
            OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(),
                                RES_POOLPAGE_HTML, true, false );
            nFirstRefPoolId = pFollow->GetPoolFormatId();
        }
        else if( (RES_POOLPAGE_LEFT == pPageDesc->GetPoolFormatId() &&
                  RES_POOLPAGE_RIGHT == pFollow->GetPoolFormatId()) ||
                 (RES_POOLPAGE_RIGHT == pPageDesc->GetPoolFormatId() &&
                  RES_POOLPAGE_LEFT == pFollow->GetPoolFormatId()) )
        {
            // the document is double-sided
            OutCSS1_SwPageDesc( *this, *pPageDesc, pStylePoolAccess, m_xTemplate.get(),
                                RES_POOLPAGE_HTML, true );
            OutCSS1_SwPageDesc( *this, *pFollow, pStylePoolAccess, m_xTemplate.get(),
                                RES_POOLPAGE_HTML, true );
            nFirstRefPoolId = RES_POOLPAGE_RIGHT;
            m_bCSS1IgnoreFirstPageDesc = false;
        }
        // other cases we miss
 
        if( pFirstPageDesc )
          OutCSS1_SwPageDesc( *this, *pFirstPageDesc, pStylePoolAccess, m_xTemplate.get(),
                                nFirstRefPoolId, false );
    }
 
    // The text body style has to be exported always (if it is changed compared
    // to the template), because it is used as reference for any style
    // that maps to <P>, and that's especially the standard style
    getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT, false );
 
    // the Default-TextStyle is not also exported !!
    // 0-Style is the Default; is never exported !!
    const size_t nTextFormats = m_pDoc->GetTextFormatColls()->size();
    for( size_t i = 1; i < nTextFormats; ++i )
    {
        const SwTextFormatColl* pColl = (*m_pDoc->GetTextFormatColls())[i];
        sal_uInt16 nPoolId = pColl->GetPoolFormatId();
        if( nPoolId == RES_POOLCOLL_TEXT || m_pDoc->IsUsed( *pColl ) )
            OutCSS1_SwFormat( *this, *pColl, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() );
    }
 
    // the Default-TextStyle is not also exported !!
    const size_t nCharFormats = m_pDoc->GetCharFormats()->size();
    for( size_t i = 1; i < nCharFormats; ++i )
    {
        const SwCharFormat *pCFormat = (*m_pDoc->GetCharFormats())[i];
        sal_uInt16 nPoolId = pCFormat->GetPoolFormatId();
        if( nPoolId == RES_POOLCHR_INET_NORMAL ||
            nPoolId == RES_POOLCHR_INET_VISIT ||
            m_pDoc->IsUsed( *pCFormat ) )
            OutCSS1_SwFormat( *this, *pCFormat, &m_pDoc->getIDocumentStylePoolAccess(), m_xTemplate.get() );
    }
 
    bool bHasEndNotes {false};
    bool bHasFootNotes {false};
    const SwFootnoteIdxs& rIdxs = m_pDoc->GetFootnoteIdxs();
    for( auto pIdx : rIdxs )
    {
        if( pIdx->GetFootnote().IsEndNote() )
        {
            bHasEndNotes = true;
            if (bHasFootNotes)
                break;
        }
        else
        {
            bHasFootNotes = true;
            if (bHasEndNotes)
                break;
        }
    }
    OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetFootnoteInfo(), m_pDoc, bHasFootNotes, false );
    OutCSS1_SwFootnoteInfo( *this, m_pDoc->GetEndNoteInfo(), m_pDoc, bHasEndNotes, true );
 
    if( !m_bFirstCSS1Rule )
    {
        DecIndentLevel();
 
        OutNewLine();
        HTMLOutFuncs::Out_AsciiTag( Strm(), Concat2View(GetNamespace() + OOO_STRING_SVTOOLS_HTML_style), false );
    }
    else
    {
        m_bFirstCSS1Rule = false;
    }
 
    m_nDfltTopMargin = 0;
    m_nDfltBottomMargin = 0;
}
 
// if pPseudo is set, Styles-Sheets will be exported;
// otherwise we only search for Token and Class for a Format
sal_uInt16 SwHTMLWriter::GetCSS1Selector( const SwFormat *pFormat, OString& rToken,
                                      OUString& rClass, sal_uInt16& rRefPoolId,
                                      OUString *pPseudo )
{
    sal_uInt16 nDeep = 0;
    rToken.clear();
    rClass.clear();
    rRefPoolId = 0;
    if( pPseudo )
        pPseudo->clear();
 
    bool bChrFormat = RES_CHRFMT==pFormat->Which();
 
    // search formats above for the nearest standard or HTML-Tag template
    const SwFormat *pPFormat = pFormat;
    while( pPFormat && !pPFormat->IsDefault() )
    {
        bool bStop = false;
        sal_uInt16 nPoolId = pPFormat->GetPoolFormatId();
        if( USER_FMT & nPoolId )
        {
            // user templates
            const OUString& aNm(pPFormat->GetName());
 
            if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_blockquote)
            {
                rRefPoolId = RES_POOLCOLL_HTML_BLOCKQUOTE;
                rToken = OOO_STRING_SVTOOLS_HTML_blockquote ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_citation)
            {
                rRefPoolId = RES_POOLCHR_HTML_CITATION;
                rToken = OOO_STRING_SVTOOLS_HTML_citation ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_code)
            {
                rRefPoolId = RES_POOLCHR_HTML_CODE;
                rToken = OOO_STRING_SVTOOLS_HTML_code ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_definstance)
            {
                rRefPoolId = RES_POOLCHR_HTML_DEFINSTANCE;
                rToken = OOO_STRING_SVTOOLS_HTML_definstance ""_ostr;
            }
            else if (!bChrFormat && (aNm == OOO_STRING_SVTOOLS_HTML_dd ||
                                  aNm == OOO_STRING_SVTOOLS_HTML_dt))
            {
                sal_uInt16 nDefListLvl = GetDefListLvl(aNm, nPoolId);
                // Export the templates DD 1/DT 1,
                // but none of their derived templates,
                // also not DD 2/DT 2 etc.
                if (nDefListLvl)
                {
                    if (pPseudo && (nDeep || (nDefListLvl & 0x0fff) > 1))
                    {
                        bStop = true;
                    }
                    else if (nDefListLvl & HTML_DLCOLL_DD)
                    {
                        rRefPoolId = RES_POOLCOLL_HTML_DD;
                        rToken = OOO_STRING_SVTOOLS_HTML_dd ""_ostr;
                    }
                    else
                    {
                        rRefPoolId = RES_POOLCOLL_HTML_DT;
                        rToken = OOO_STRING_SVTOOLS_HTML_dt ""_ostr;
                    }
                }
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_emphasis)
            {
                rRefPoolId = RES_POOLCHR_HTML_EMPHASIS;
                rToken = OOO_STRING_SVTOOLS_HTML_emphasis ""_ostr;
            }
            else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_horzrule)
            {
                // do not export HR !
                bStop = (nDeep==0);
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_keyboard)
            {
                rRefPoolId = RES_POOLCHR_HTML_KEYBOARD;
                rToken = OOO_STRING_SVTOOLS_HTML_keyboard ""_ostr;
            }
            else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_listing)
            {
                // Export Listings as PRE or PRE-derived template
                rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
                rRefPoolId = RES_POOLCOLL_HTML_PRE;
                nDeep = CSS1_FMT_CMPREF;
            }
            else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_preformtxt)
            {
                rRefPoolId = RES_POOLCOLL_HTML_PRE;
                rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_sample)
            {
                rRefPoolId = RES_POOLCHR_HTML_SAMPLE;
                rToken = OOO_STRING_SVTOOLS_HTML_sample ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_strong)
            {
                rRefPoolId = RES_POOLCHR_HTML_STRONG;
                rToken = OOO_STRING_SVTOOLS_HTML_strong ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_teletype)
            {
                rRefPoolId = RES_POOLCHR_HTML_TELETYPE;
                rToken = OOO_STRING_SVTOOLS_HTML_teletype ""_ostr;
            }
            else if (bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_variable)
            {
                rRefPoolId = RES_POOLCHR_HTML_VARIABLE;
                rToken = OOO_STRING_SVTOOLS_HTML_variable ""_ostr;
            }
            else if (!bChrFormat && aNm == OOO_STRING_SVTOOLS_HTML_xmp)
            {
                // export XMP as PRE (but not the template as Style)
                rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
                rRefPoolId = RES_POOLCOLL_HTML_PRE;
                nDeep = CSS1_FMT_CMPREF;
            }
 
            // if a PoolId is set, the Name of the template is that of the related Token
            OSL_ENSURE( (rRefPoolId != 0) == (!rToken.isEmpty()),
                    "Token missing" );
        }
        else
        {
            // Pool templates
            switch( nPoolId )
            {
            // paragraph templates
            case RES_POOLCOLL_HEADLINE_BASE:
            case RES_POOLCOLL_STANDARD:
                // do not export this template
            case RES_POOLCOLL_HTML_HR:
                // do not export HR !
                bStop = (nDeep==0);
                break;
            case RES_POOLCOLL_TEXT:
                rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE1:
                rToken = OOO_STRING_SVTOOLS_HTML_head1 ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE2:
                rToken = OOO_STRING_SVTOOLS_HTML_head2 ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE3:
                rToken = OOO_STRING_SVTOOLS_HTML_head3 ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE4:
                rToken = OOO_STRING_SVTOOLS_HTML_head4 ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE5:
                rToken = OOO_STRING_SVTOOLS_HTML_head5 ""_ostr;
                break;
            case RES_POOLCOLL_HEADLINE6:
                rToken = OOO_STRING_SVTOOLS_HTML_head6 ""_ostr;
                break;
            case RES_POOLCOLL_SEND_ADDRESS:
                rToken = OOO_STRING_SVTOOLS_HTML_address ""_ostr;
                break;
            case RES_POOLCOLL_HTML_BLOCKQUOTE:
                rToken = OOO_STRING_SVTOOLS_HTML_blockquote ""_ostr;
                break;
            case RES_POOLCOLL_HTML_PRE:
                rToken = OOO_STRING_SVTOOLS_HTML_preformtxt ""_ostr;
                break;
 
            case RES_POOLCOLL_HTML_DD:
                rToken = OOO_STRING_SVTOOLS_HTML_dd ""_ostr;
                break;
            case RES_POOLCOLL_HTML_DT:
                rToken = OOO_STRING_SVTOOLS_HTML_dt ""_ostr;
                break;
 
            case RES_POOLCOLL_TABLE:
                if( pPseudo )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_tabledata " "
                             OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                }
                else
                    rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                break;
            case RES_POOLCOLL_TABLE_HDLN:
                if( pPseudo )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_tableheader " "
                             OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                }
                else
                    rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                break;
            case RES_POOLCOLL_FOOTNOTE:
                if( !nDeep )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                    rClass = OOO_STRING_SVTOOLS_HTML_sdfootnote;
                    rRefPoolId = RES_POOLCOLL_TEXT;
                    nDeep = CSS1_FMT_CMPREF;
                }
                break;
            case RES_POOLCOLL_ENDNOTE:
                if( !nDeep )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_parabreak ""_ostr;
                    rClass = OOO_STRING_SVTOOLS_HTML_sdendnote;
                    rRefPoolId = RES_POOLCOLL_TEXT;
                    nDeep = CSS1_FMT_CMPREF;
                }
                break;
 
            // character templates
            case RES_POOLCHR_HTML_EMPHASIS:
                rToken = OOO_STRING_SVTOOLS_HTML_emphasis ""_ostr;
                break;
            case RES_POOLCHR_HTML_CITATION:
                rToken = OOO_STRING_SVTOOLS_HTML_citation ""_ostr;
                break;
            case RES_POOLCHR_HTML_STRONG:
                rToken = OOO_STRING_SVTOOLS_HTML_strong ""_ostr;
                break;
            case RES_POOLCHR_HTML_CODE:
                rToken = OOO_STRING_SVTOOLS_HTML_code ""_ostr;
                break;
            case RES_POOLCHR_HTML_SAMPLE:
                rToken = OOO_STRING_SVTOOLS_HTML_sample ""_ostr;
                break;
            case RES_POOLCHR_HTML_KEYBOARD:
                rToken = OOO_STRING_SVTOOLS_HTML_keyboard ""_ostr;
                break;
            case RES_POOLCHR_HTML_VARIABLE:
                rToken = OOO_STRING_SVTOOLS_HTML_variable ""_ostr;
                break;
            case RES_POOLCHR_HTML_DEFINSTANCE:
                rToken = OOO_STRING_SVTOOLS_HTML_definstance ""_ostr;
                break;
            case RES_POOLCHR_HTML_TELETYPE:
                rToken = OOO_STRING_SVTOOLS_HTML_teletype ""_ostr;
                break;
 
            case RES_POOLCHR_INET_NORMAL:
                if( pPseudo )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_anchor ""_ostr;
                    *pPseudo = sCSS1_link;
                }
                break;
            case RES_POOLCHR_INET_VISIT:
                if( pPseudo )
                {
                    rToken = OOO_STRING_SVTOOLS_HTML_anchor ""_ostr;
                    *pPseudo = sCSS1_visited;
                }
                break;
            }
 
            // if a token is set, PoolId contains the related template
            if( !rToken.isEmpty() && !rRefPoolId )
                rRefPoolId = nPoolId;
        }
 
        if( !rToken.isEmpty() || bStop )
        {
            // stop if a HTML-Tag template was found
            break;
        }
        else
        {
            // continue otherwise
            nDeep++;
            pPFormat = pPFormat->DerivedFrom();
        }
    }
 
    if( !rToken.isEmpty() )
    {
        // this is a HTML-Tag template
        if( !nDeep )
            nDeep = CSS1_FMT_ISTAG;
    }
    else
    {
        // this is not a HTML-Tag template nor derived from one
        nDeep = 0;
    }
    if( nDeep > 0 && nDeep < CSS1_FMT_SPECIAL )
    {
        // If the template is derived from a HTML template,
        // we export it as <TOKEN>.<CLASS>, otherwise as .<CLASS>.
        // <CLASS> is the name of the template after removing all characters
        // before and including the first '.'
        rClass = pFormat->GetName();
        sal_Int32 nPos = rClass.indexOf( '.' );
        if( nPos >= 0 && rClass.getLength() > nPos+1 )
        {
            rClass = rClass.replaceAt( 0, nPos+1, u"" );
        }
 
        rClass = GetAppCharClass().lowercase( rClass );
        rClass = rClass.replaceAll( ".", "-" );
        rClass = rClass.replaceAll( " ", "-" );
        rClass = rClass.replaceAll( "_", "-" );
    }
 
    return nDeep;
}
 
static sal_uInt16 GetCSS1Selector( const SwFormat *pFormat, OUString& rSelector,
                               sal_uInt16& rRefPoolId )
{
    OString aToken;
    OUString aClass;
    OUString aPseudo;
 
    sal_uInt16 nDeep = SwHTMLWriter::GetCSS1Selector( pFormat, aToken, aClass,
                                                  rRefPoolId, &aPseudo );
    if( nDeep )
    {
        if( !aToken.isEmpty() )
            rSelector = OStringToOUString(aToken, RTL_TEXTENCODING_ASCII_US);
        else
            rSelector.clear();
 
        if( !aClass.isEmpty() )
            rSelector += "." + aClass;
        if( !aPseudo.isEmpty() )
            rSelector += ":" + aPseudo;
    }
 
    return nDeep;
}
 
const SwFormat *SwHTMLWriter::GetTemplateFormat( sal_uInt16 nPoolFormatId,
                                           IDocumentStylePoolAccess* pTemplate /*SwDoc *pTemplate*/)
{
    const SwFormat *pRefFormat = nullptr;
 
    if( pTemplate )
    {
        OSL_ENSURE( !(USER_FMT & nPoolFormatId),
                "No user templates found" );
        if( POOLGRP_NOCOLLID & nPoolFormatId )
            pRefFormat = pTemplate->GetCharFormatFromPool( nPoolFormatId );
        else
            pRefFormat = pTemplate->GetTextCollFromPool( nPoolFormatId, false );
    }
 
    return pRefFormat;
}
 
const SwFormat *SwHTMLWriter::GetParentFormat( const SwFormat& rFormat, sal_uInt16 nDeep )
{
    OSL_ENSURE( nDeep != USHRT_MAX, "Called GetParent for HTML-template!" );
    const SwFormat *pRefFormat = nullptr;
 
    if( nDeep > 0 )
    {
        // get the pointer for the HTML-Tag template, from which the template is derived
        pRefFormat = &rFormat;
        for( sal_uInt16 i=nDeep; i>0; i-- )
            pRefFormat = pRefFormat->DerivedFrom();
 
        if( pRefFormat && pRefFormat->IsDefault() )
            pRefFormat = nullptr;
    }
 
    return pRefFormat;
}
 
bool swhtml_css1atr_equalFontItems( const SfxPoolItem& r1, const SfxPoolItem& r2 )
{
    return  static_cast<const SvxFontItem &>(r1).GetFamilyName() ==
                    static_cast<const SvxFontItem &>(r2).GetFamilyName() &&
            static_cast<const SvxFontItem &>(r1).GetFamily() ==
                    static_cast<const SvxFontItem &>(r2).GetFamily();
}
 
void SwHTMLWriter::SubtractItemSet( SfxItemSet& rItemSet,
                                    const SfxItemSet& rRefItemSet,
                                    bool bSetDefaults,
                                    bool bClearSame,
                                     const SfxItemSet *pRefScriptItemSet )
{
    OSL_ENSURE( bSetDefaults || bClearSame,
            "SwHTMLWriter::SubtractItemSet: No action for this Flag" );
    SfxItemSet aRefItemSet( *rRefItemSet.GetPool(), rRefItemSet.GetRanges() );
    aRefItemSet.Set( rRefItemSet );
 
    // compare with the Attr-Set of the template
    SfxWhichIter aIter( rItemSet );
    sal_uInt16 nWhich = aIter.FirstWhich();
    while( nWhich )
    {
        const SfxPoolItem *pRefItem, *pItem;
        bool bItemSet = ( SfxItemState::SET ==
                aIter.GetItemState( false, &pItem) );
        bool bRefItemSet;
 
        if( pRefScriptItemSet )
        {
            switch( nWhich )
            {
            case RES_CHRATR_FONT:
            case RES_CHRATR_FONTSIZE:
            case RES_CHRATR_LANGUAGE:
            case RES_CHRATR_POSTURE:
            case RES_CHRATR_WEIGHT:
            case RES_CHRATR_CJK_FONT:
            case RES_CHRATR_CJK_FONTSIZE:
            case RES_CHRATR_CJK_LANGUAGE:
            case RES_CHRATR_CJK_POSTURE:
            case RES_CHRATR_CJK_WEIGHT:
            case RES_CHRATR_CTL_FONT:
            case RES_CHRATR_CTL_FONTSIZE:
            case RES_CHRATR_CTL_LANGUAGE:
            case RES_CHRATR_CTL_POSTURE:
            case RES_CHRATR_CTL_WEIGHT:
                bRefItemSet = ( SfxItemState::SET ==
                    pRefScriptItemSet->GetItemState( nWhich, true, &pRefItem) );
                break;
            default:
                bRefItemSet = ( SfxItemState::SET ==
                    aRefItemSet.GetItemState( nWhich, false, &pRefItem) );
                break;
            }
        }
        else
        {
            bRefItemSet = ( SfxItemState::SET ==
                aRefItemSet.GetItemState( nWhich, false, &pRefItem) );
        }
 
        if( bItemSet )
        {
            if( (bClearSame || pRefScriptItemSet) && bRefItemSet &&
                ( *pItem == *pRefItem ||
                  ((RES_CHRATR_FONT == nWhich ||
                    RES_CHRATR_CJK_FONT == nWhich ||
                    RES_CHRATR_CTL_FONT == nWhich)  &&
                   swhtml_css1atr_equalFontItems( *pItem, *pRefItem )) ) )
            {
                // the Attribute is in both templates with the same value
                // and does not need to be exported
                rItemSet.ClearItem( nWhich );
            }
        }
        else
        {
            if( (bSetDefaults || pRefScriptItemSet) && bRefItemSet )
            {
                // the Attribute exists only in the reference; the default
                // might have to be exported
                rItemSet.Put( rItemSet.GetPool()->GetUserOrPoolDefaultItem(nWhich) );
            }
        }
 
        nWhich = aIter.NextWhich();
    }
}
 
void SwHTMLWriter::PrepareFontList( const SvxFontItem& rFontItem,
                                    OUString& rNames,
                                    sal_Unicode cQuote, bool bGeneric )
{
    rNames.clear();
    const OUString& rName = rFontItem.GetFamilyName();
    bool bContainsKeyword = false;
    if( !rName.isEmpty() )
    {
        sal_Int32 nStrPos = 0;
        while( nStrPos != -1 )
        {
            OUString aName = rName.getToken( 0, ';', nStrPos );
            aName = comphelper::string::encodeForXml(comphelper::string::strip(aName, ' '));
            if( aName.isEmpty() )
                continue;
 
            bool bIsKeyword = false;
            switch( aName[0] )
            {
            case 'c':
            case 'C':
                bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_cursive );
                break;
 
            case 'f':
            case 'F':
                bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_fantasy );
                break;
 
            case 'm':
            case 'M':
                bIsKeyword = aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_monospace );
                break;
 
            case 's':
            case 'S':
                bIsKeyword =
                    aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_serif ) ||
                    aName.equalsIgnoreAsciiCaseAscii( sCSS1_PV_sans_serif );
                break;
            }
 
            bContainsKeyword |= bIsKeyword;
 
            if( !rNames.isEmpty() )
                rNames += ", ";
            if( cQuote && !bIsKeyword )
                rNames += OUStringChar( cQuote );
            rNames += aName;
            if( cQuote && !bIsKeyword )
                rNames += OUStringChar( cQuote );
        }
    }
 
    if( bContainsKeyword || !bGeneric )
        return;
 
    std::string_view pStr;
    switch( rFontItem.GetFamily() )
    {
    case FAMILY_ROMAN:      pStr = sCSS1_PV_serif;      break;
    case FAMILY_SWISS:      pStr = sCSS1_PV_sans_serif; break;
    case FAMILY_SCRIPT:     pStr = sCSS1_PV_cursive;    break;
    case FAMILY_DECORATIVE: pStr = sCSS1_PV_fantasy;    break;
    case FAMILY_MODERN:     pStr = sCSS1_PV_monospace;  break;
    default:
        ;
    }
 
    if( !pStr.empty() )
    {
        if( !rNames.isEmpty() )
            rNames += ", ";
        rNames += OStringToOUString( pStr, RTL_TEXTENCODING_ASCII_US );
    }
}
 
bool SwHTMLWriter::HasScriptDependentItems( const SfxItemSet& rItemSet,
                                                 bool bCheckDropCap )
{
    static const sal_uInt16 aWhichIds[] =
    {
        RES_CHRATR_FONT,        RES_CHRATR_CJK_FONT,        RES_CHRATR_CTL_FONT,
        RES_CHRATR_FONTSIZE,    RES_CHRATR_CJK_FONTSIZE,    RES_CHRATR_CTL_FONTSIZE,
        RES_CHRATR_LANGUAGE,    RES_CHRATR_CJK_LANGUAGE,    RES_CHRATR_CTL_LANGUAGE,
        RES_CHRATR_POSTURE,     RES_CHRATR_CJK_POSTURE,     RES_CHRATR_CTL_POSTURE,
        RES_CHRATR_WEIGHT,      RES_CHRATR_CJK_WEIGHT,      RES_CHRATR_CTL_WEIGHT,
        0,                      0,                          0
    };
 
    for( int i=0; aWhichIds[i]; i += 3 )
    {
        const SfxPoolItem *pItem = nullptr, *pItemCJK = nullptr, *pItemCTL = nullptr, *pTmp;
        int nItemCount = 0;
        if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i], false,
                                                   &pTmp ) )
        {
            pItem = pTmp;
            nItemCount++;
        }
        if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+1], false,
                                                   &pTmp ) )
        {
            pItemCJK = pTmp;
            nItemCount++;
        }
        if( SfxItemState::SET == rItemSet.GetItemState( aWhichIds[i+2], false,
                                                   &pTmp ) )
        {
            pItemCTL = pTmp;
            nItemCount++;
        }
 
        // If some of the items are set, but not all, we need script dependent
        // styles
        if( nItemCount > 0 && nItemCount < 3 )
            return true;
 
        if( 3 == nItemCount )
        {
            // If all items are set, but some of them have different values,
            // we need script dependent styles, too. For font items, we have
            // to take care about their special HTML/CSS1 representation.
            if( RES_CHRATR_FONT == aWhichIds[i] )
            {
                if( !swhtml_css1atr_equalFontItems( *pItem, *pItemCJK ) ||
                    !swhtml_css1atr_equalFontItems( *pItem, *pItemCTL ) ||
                    !swhtml_css1atr_equalFontItems( *pItemCJK, *pItemCTL ) )
                    return true;
            }
            else
            {
                if( *pItem != *pItemCJK ||
                    *pItem != *pItemCTL ||
                    *pItemCJK != *pItemCTL )
                    return true;
            }
        }
    }
 
    const SwFormatDrop *pDrop;
    if( bCheckDropCap &&
        (pDrop = rItemSet.GetItemIfSet( RES_PARATR_DROP )) )
    {
        const SwCharFormat *pDCCharFormat = pDrop->GetCharFormat();
        if( pDCCharFormat )
        {
            //sequence of (start, end) property ranges we want to
            //query
            SfxItemSetFixed<
                    RES_CHRATR_FONT, RES_CHRATR_FONT,
                    RES_CHRATR_POSTURE, RES_CHRATR_POSTURE,
                    RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
                    RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT,
                    RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_FONT,
                    RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT>
                 aTstItemSet(*pDCCharFormat->GetAttrSet().GetPool());
            aTstItemSet.Set( pDCCharFormat->GetAttrSet() );
            return HasScriptDependentItems( aTstItemSet, false );
        }
    }
 
    return false;
}
 
static bool OutCSS1Rule( SwHTMLWriter& rWrt, const OUString& rSelector,
                    const SfxItemSet& rItemSet, bool bHasClass,
                     bool bCheckForPseudo  )
{
    bool bScriptDependent = false;
    if( SwHTMLWriter::HasScriptDependentItems( rItemSet, bHasClass ) )
    {
        bScriptDependent = true;
        std::u16string_view aSelector( rSelector );
 
        std::u16string_view aPseudo;
        if( bCheckForPseudo )
        {
            size_t nPos = aSelector.rfind( ':' );
            if( nPos != std::u16string_view::npos )
            {
                aPseudo = aSelector.substr( nPos );
                aSelector =aSelector.substr( 0, nPos );
            }
        }
 
        if( !bHasClass )
        {
            // If we are exporting styles for a tag we have to export a tag
            // rule for all properties that aren't style dependent and
            // some class rule for the additional style dependen properties
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &rSelector );
                rWrt.OutCSS1_SfxItemSet( rItemSet, false );
            }
 
            //sequence of (start, end) property ranges we want to
            //query
            SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
                                       RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE,
                                       RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
                                       RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>
                aScriptItemSet( *rItemSet.GetPool() );
            aScriptItemSet.Put( rItemSet );
 
            OUString aNewSelector = OUString::Concat(aSelector) + ".western" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
            }
 
            aNewSelector = OUString::Concat(aSelector) + ".cjk" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
            }
 
            aNewSelector = OUString::Concat(aSelector) + ".ctl" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( aScriptItemSet, false );
            }
        }
        else
        {
            // If there are script dependencies and we are derived from a tag,
            // when we have to export a style dependent class for all
            // scripts
            OUString aNewSelector = OUString::Concat(aSelector) + "-western" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( rItemSet, false );
            }
 
            aNewSelector = OUString::Concat(aSelector) + "-cjk" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( rItemSet, false );
            }
 
            aNewSelector = OUString::Concat(aSelector) + "-ctl" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                                     &aNewSelector );
                rWrt.OutCSS1_SfxItemSet( rItemSet, false );
            }
        }
    }
    else
    {
        // If there are no script dependencies, when all items are
        // exported in one step. For hyperlinks only, a script information
        // must be there, because these two chr formats don't support
        // script dependencies by now.
        SwCSS1OutMode aMode( rWrt,
                rWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                             &rSelector );
        rWrt.OutCSS1_SfxItemSet( rItemSet, false );
    }
 
    return bScriptDependent;
}
 
static void OutCSS1DropCapRule(
                    SwHTMLWriter& rWrt, const OUString& rSelector,
                    const SwFormatDrop& rDrop, bool bHasClass,
                     bool bHasScriptDependencies  )
{
    const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat();
    if( (bHasScriptDependencies && bHasClass) ||
         (pDCCharFormat && SwHTMLWriter::HasScriptDependentItems( pDCCharFormat->GetAttrSet(), false ) ) )
    {
        std::u16string_view aSelector( rSelector );
 
        std::u16string_view aPseudo;
        size_t nPos = aSelector.rfind( ':' );
        if( nPos != std::u16string_view::npos )
        {
            aPseudo = aSelector.substr( nPos );
            aSelector = aSelector.substr( 0, nPos );
        }
 
        if( !bHasClass )
        {
            // If we are exporting styles for a tag we have to export a tag
            // rule for all properties that aren't style dependent and
            // some class rule for the additional style dependen properties
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_NO_SCRIPT|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &rSelector );
                OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
            }
 
            SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
                           RES_CHRATR_LANGUAGE, RES_CHRATR_POSTURE,
                           RES_CHRATR_WEIGHT, RES_CHRATR_WEIGHT,
                           RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_WEIGHT>
                aScriptItemSet( rWrt.m_pDoc->GetAttrPool() );
            if( pDCCharFormat )
                aScriptItemSet.Set( pDCCharFormat->GetAttrSet() );
 
            OUString aNewSelector = OUString::Concat(aSelector) + ".western" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop, &aScriptItemSet );
            }
 
            aNewSelector = OUString::Concat(aSelector) + ".cjk" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop, &aScriptItemSet );
            }
 
            aNewSelector = OUString::Concat(aSelector) + ".ctl" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop, &aScriptItemSet );
            }
        }
        else
        {
            // If there are script dependencies and we are derived from a tag,
            // when we have to export a style dependent class for all
            // scripts
            OUString aNewSelector = OUString::Concat(aSelector) + "-western" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_WESTERN|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop );
            }
 
            aNewSelector = OUString::Concat(aSelector) + "-cjk" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CJK|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop );
            }
 
            aNewSelector = OUString::Concat(aSelector) + "-ctl" + aPseudo;
            {
                SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_CTL|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                                     &aNewSelector );
                OutCSS1_SwFormatDropAttrs(  rWrt, rDrop );
            }
        }
    }
    else
    {
        // If there are no script dependencies, when all items are
        // exported in one step. For hyperlinks only, a script information
        // must be there, because these two chr formats don't support
        // script dependencies by now.
        SwCSS1OutMode aMode( rWrt,
                rWrt.m_nCSS1Script|CSS1_OUTMODE_RULE|CSS1_OUTMODE_DROPCAP,
                             &rSelector );
        OutCSS1_SwFormatDropAttrs( rWrt, rDrop );
    }
}
 
static SwHTMLWriter& OutCSS1_SwFormat( SwHTMLWriter& rWrt, const SwFormat& rFormat,
                              IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate )
{
    bool bCharFormat = false;
    switch( rFormat.Which() )
    {
    case RES_CHRFMT:
        bCharFormat = true;
        break;
 
    case RES_TXTFMTCOLL:
    case RES_CONDTXTFMTCOLL:
        // these template-types can be exported
        break;
 
    default:
        // but not these
        return rWrt;
    }
 
    // determine Selector and the to-be-exported Attr-Set-depth
    OUString aSelector;
    sal_uInt16 nRefPoolId = 0;
    sal_uInt16 nDeep = GetCSS1Selector( &rFormat, aSelector, nRefPoolId );
    if( !nDeep )
        return rWrt;    // not derived from a HTML-template
 
    sal_uInt16 nPoolFormatId = rFormat.GetPoolFormatId();
 
    // Determine the to-be-exported Attr-Set. We have to distinguish 3 cases:
    // - HTML-Tag templates (nDeep==USHRT_MAX):
    //   Export Attrs...
    //     - that are set in the template, but not in the original of the HTML template
    //     - the Default-Attrs for the Attrs, that are set in the Original of the
    //       HTML template, but not in the current template.
    // - templates directly derived from HTML templates (nDeep==1):
    //   Export only Attributes of the template Item-Set w/o its parents.
    // - templates in-directly derived from HTML templates (nDeep>1):
    //   Export Attributes of the template Item-Set incl. its Parents,
    //   but w/o Attributes that are set in the HTML-Tag template.
 
    // create Item-Set with all Attributes from the template
    // (all but for nDeep==1)
    const SfxItemSet& rFormatItemSet = rFormat.GetAttrSet();
    SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() );
    aItemSet.Set( rFormatItemSet ); // Was nDeep!=1 that is not working
                                    // for script dependent items but should
                                    // not make a difference for any other
 
    bool bSetDefaults = true, bClearSame = true;
    const SwFormat *pRefFormat = nullptr;
    const SwFormat *pRefFormatScript = nullptr;
    switch( nDeep )
    {
    case CSS1_FMT_ISTAG:
        pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
        break;
    case CSS1_FMT_CMPREF:
        pRefFormat = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pDoc );
        pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
        bClearSame = false;
        break;
    default:
        pRefFormat = SwHTMLWriter::GetParentFormat( rFormat, nDeep );
        pRefFormatScript = SwHTMLWriter::GetTemplateFormat( nRefPoolId, pTemplate == nullptr ? nullptr : &pTemplate->getIDocumentStylePoolAccess() );
        bSetDefaults = false;
        break;
    }
 
    if( pRefFormat )
    {
        // subtract Item-Set of the Reference template (incl. its Parents)
        SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(),
                                       bSetDefaults, bClearSame,
                                       pRefFormatScript
                                               ? &pRefFormatScript->GetAttrSet()
                                            : nullptr  );
 
        if( !bCharFormat )
        {
            const SvxULSpaceItem& rULItem = pRefFormat->GetULSpace();
            rWrt.m_nDfltTopMargin = rULItem.GetUpper();
            rWrt.m_nDfltBottomMargin = rULItem.GetLower();
        }
    }
    else if( CSS1_FMT_ISTAG==nDeep && !bCharFormat )
    {
        // set Default-distance above and below (for the
        // case that there is no reference template)
        rWrt.m_nDfltTopMargin = 0;
        rWrt.m_nDfltBottomMargin = HTML_PARSPACE;
        if( USER_FMT & nPoolFormatId )
        {
            // user templates
            const OUString& aNm(rFormat.GetName());
 
            if (aNm == "DD 1" || aNm == "DT 1")
                rWrt.m_nDfltBottomMargin = 0;
            else if (aNm == OOO_STRING_SVTOOLS_HTML_listing)
                rWrt.m_nDfltBottomMargin = 0;
            else if (aNm == OOO_STRING_SVTOOLS_HTML_preformtxt)
                rWrt.m_nDfltBottomMargin = 0;
            else if (aNm == OOO_STRING_SVTOOLS_HTML_xmp)
                rWrt.m_nDfltBottomMargin = 0;
        }
        else
        {
            // Pool templates
            switch( nPoolFormatId )
            {
            case RES_POOLCOLL_HEADLINE1:
            case RES_POOLCOLL_HEADLINE2:
            case RES_POOLCOLL_HEADLINE3:
            case RES_POOLCOLL_HEADLINE4:
            case RES_POOLCOLL_HEADLINE5:
            case RES_POOLCOLL_HEADLINE6:
                rWrt.m_nDfltTopMargin = HTML_HEADSPACE;
                break;
            case RES_POOLCOLL_SEND_ADDRESS:
            case RES_POOLCOLL_HTML_DT:
            case RES_POOLCOLL_HTML_DD:
            case RES_POOLCOLL_HTML_PRE:
                rWrt.m_nDfltBottomMargin = 0;
                break;
            }
        }
    }
 
    // if nothing is to be exported ...
    if( !aItemSet.Count() )
        return rWrt;
 
    // There is no support for script dependent hyperlinks by now.
    bool bCheckForPseudo = false;
    if( bCharFormat &&
        (RES_POOLCHR_INET_NORMAL==nRefPoolId ||
         RES_POOLCHR_INET_VISIT==nRefPoolId) )
        bCheckForPseudo = true;
 
    // export now the Attributes (incl. selector)
    bool bHasScriptDependencies = false;
    if( OutCSS1Rule( rWrt, aSelector, aItemSet, CSS1_FMT_ISTAG != nDeep,
                      bCheckForPseudo ) )
    {
        if( bCharFormat )
            rWrt.m_aScriptTextStyles.insert( rFormat.GetName() );
        else
        {
            if( nPoolFormatId==RES_POOLCOLL_TEXT )
                rWrt.m_aScriptParaStyles.insert( pDoc->GetTextCollFromPool( RES_POOLCOLL_STANDARD, false )->GetName() );
            rWrt.m_aScriptParaStyles.insert( rFormat.GetName() );
        }
        bHasScriptDependencies = true;
    }
 
    // export Drop-Caps
    if( const SwFormatDrop *pDrop = aItemSet.GetItemIfSet( RES_PARATR_DROP, false ) )
    {
        OUString sOut = aSelector + ":" + sCSS1_first_letter;
        OutCSS1DropCapRule( rWrt, sOut, *pDrop, CSS1_FMT_ISTAG != nDeep, bHasScriptDependencies );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SwPageDesc( SwHTMLWriter& rWrt, const SwPageDesc& rPageDesc,
                                   IDocumentStylePoolAccess/*SwDoc*/ *pDoc, SwDoc *pTemplate,
                                   sal_uInt16 nRefPoolId, bool bExtRef,
                                   bool bPseudo )
{
    const SwPageDesc* pRefPageDesc = nullptr;
    if( !bExtRef )
        pRefPageDesc = pDoc->GetPageDescFromPool( nRefPoolId, false );
    else if( pTemplate )
        pRefPageDesc = pTemplate->getIDocumentStylePoolAccess().GetPageDescFromPool( nRefPoolId, false );
 
    OUString aSelector = OUString::Concat("@") + sCSS1_page;
 
    if( bPseudo )
    {
        std::u16string_view pPseudo;
        switch( rPageDesc.GetPoolFormatId() )
        {
        case RES_POOLPAGE_FIRST:    pPseudo = sCSS1_first;  break;
        case RES_POOLPAGE_LEFT:     pPseudo = sCSS1_left;   break;
        case RES_POOLPAGE_RIGHT:    pPseudo = sCSS1_right;  break;
        }
        if( !pPseudo.empty() )
            aSelector += OUString::Concat(":") + pPseudo;
    }
 
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_RULE_ON|CSS1_OUTMODE_TEMPLATE,
                         &aSelector );
 
    // Size: If the only difference is the Landscape-Flag,
    // only export Portrait or Landscape. Otherwise export size.
    bool bRefLandscape = pRefPageDesc && pRefPageDesc->GetLandscape();
    Size aRefSz;
    const Size& rSz = rPageDesc.GetMaster().GetFrameSize().GetSize();
    if( pRefPageDesc )
    {
        aRefSz = pRefPageDesc->GetMaster().GetFrameSize().GetSize();
        if( bRefLandscape != rPageDesc.GetLandscape() )
        {
            tools::Long nTmp = aRefSz.Height();
            aRefSz.setHeight( aRefSz.Width() );
            aRefSz.setWidth( nTmp );
        }
    }
 
    // TODO: Bad Hack: On the Page-Tabpage there are small rounding errors
    // for the page size. Partially because of bug 25535, we stupidly still
    // use the Size-Item from Dialog, even if nothing changed.
    // Thus: once one visited the Page-Dialog and left it with OK, we get a
    // new page size that then gets exported here. To avoid that, we allow
    // here small deviations.
    if( std::abs( rSz.Width() - aRefSz.Width() ) <= 2 &&
        std::abs( rSz.Height() - aRefSz.Height() ) <= 2 )
    {
        if( bRefLandscape != rPageDesc.GetLandscape() )
        {
            rWrt.OutCSS1_PropertyAscii( sCSS1_P_size,
                rPageDesc.GetLandscape() ? sCSS1_PV_landscape
                                         : sCSS1_PV_portrait );
        }
    }
    else
    {
        OStringBuffer sVal;
        AddUnitPropertyValue(sVal, rSz.Width(), rWrt.GetCSS1Unit());
        sVal.append(' ');
        AddUnitPropertyValue(sVal, rSz.Height(), rWrt.GetCSS1Unit());
        rWrt.OutCSS1_PropertyAscii(sCSS1_P_size, sVal);
    }
 
    // Export the distance-Attributes as normally
    const SwFrameFormat &rMaster = rPageDesc.GetMaster();
    SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE> aItemSet( *rMaster.GetAttrSet().GetPool() );
    aItemSet.Set( rMaster.GetAttrSet() );
 
    if( pRefPageDesc )
    {
        SwHTMLWriter::SubtractItemSet( aItemSet,
                                       pRefPageDesc->GetMaster().GetAttrSet(),
                                       true );
    }
 
    OutCSS1_SvxULSpace_SvxLRSpace( rWrt, aItemSet );
 
    // If for a Pseudo-Selector no Property had been set, we still
    // have to export something, so that the corresponding template is
    // created on the next import.
    if( rWrt.m_bFirstCSS1Property && bPseudo )
    {
        rWrt.OutNewLine();
        OString sTmp(OUStringToOString(aSelector, RTL_TEXTENCODING_UTF8));
        rWrt.Strm().WriteOString( sTmp ).WriteOString( " {" );
        rWrt.m_bFirstCSS1Property = false;
    }
 
    if( !rWrt.m_bFirstCSS1Property )
        rWrt.Strm().WriteOString( sCSS1_rule_end );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SwFootnoteInfo( SwHTMLWriter& rWrt, const SwEndNoteInfo& rInfo,
                                  SwDoc *pDoc, bool bHasNotes, bool bEndNote )
{
    OUString aSelector;
 
    if( bHasNotes )
    {
        aSelector = OUString::Concat(OOO_STRING_SVTOOLS_HTML_anchor ".") +
                    ( bEndNote ? std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdendnote_anc)
                               : std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdfootnote_anc) );
        SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_RULE|CSS1_OUTMODE_TEMPLATE,
                             &aSelector );
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_size,
                                        sHTML_FTN_fontheight );
        rWrt.Strm().WriteOString( sCSS1_rule_end );
    }
 
    const SwCharFormat *pSymCharFormat = rInfo.GetCharFormat( *pDoc );
    if( pSymCharFormat )
    {
        const SfxItemSet& rFormatItemSet = pSymCharFormat->GetAttrSet();
        SfxItemSet aItemSet( *rFormatItemSet.GetPool(), rFormatItemSet.GetRanges() );
        aItemSet.Set( rFormatItemSet );
 
        // If there are footnotes or endnotes, then all Attributes have to be
        // exported, so that Netscape displays the document correctly.
        // Otherwise it is sufficient, to export the differences to the
        // footnote and endnote template.
        if( !bHasNotes && rWrt.m_xTemplate.is() )
        {
            SwFormat *pRefFormat = rWrt.m_xTemplate->getIDocumentStylePoolAccess().GetCharFormatFromPool(
                        static_cast< sal_uInt16 >(bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE) );
            if( pRefFormat )
                SwHTMLWriter::SubtractItemSet( aItemSet, pRefFormat->GetAttrSet(),
                                               true );
        }
        if( aItemSet.Count() )
        {
            aSelector = OUString::Concat(OOO_STRING_SVTOOLS_HTML_anchor ".") +
                        ( bEndNote ? std::u16string_view(u"" OOO_STRING_SVTOOLS_HTML_sdendnote_sym)
                                   : std::u16string_view(
                                       u"" OOO_STRING_SVTOOLS_HTML_sdfootnote_sym));
            if( OutCSS1Rule( rWrt, aSelector, aItemSet, true, false ))
                rWrt.m_aScriptTextStyles.insert( pSymCharFormat->GetName() );
        }
    }
 
    return rWrt;
}
 
SwHTMLWriter& OutCSS1_BodyTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet )
{
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
                                   CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_BODY, nullptr );
 
    // Only export the attributes of the page template.
    // The attributes of the default paragraph template were
    // considered already when exporting the paragraph template.
 
    const SfxPoolItem *pItem;
    if( SfxItemState::SET == rItemSet.GetItemState( RES_BACKGROUND, false,
                                               &pItem ) )
    {
        OUString rEmbeddedGraphicName;
        OutCSS1_SvxBrush( rWrt, *pItem, sw::Css1Background::Page, &rEmbeddedGraphicName );
    }
 
    if( SfxItemState::SET == rItemSet.GetItemState( RES_BOX, false,
                                               &pItem ))
    {
        OutCSS1_SvxBox( rWrt, *pItem );
    }
 
    if( !rWrt.m_bFirstCSS1Property )
    {
        // if a Property was exported as part of a Style-Option,
        // the Option still needs to be finished
        rWrt.Strm().WriteChar( '\"' );
    }
 
    return rWrt;
}
 
SwHTMLWriter& OutCSS1_ParaTagStyleOpt( SwHTMLWriter& rWrt, const SfxItemSet& rItemSet, std::string_view rAdd )
{
    SwCSS1OutMode aMode( rWrt, rWrt.m_nCSS1Script|CSS1_OUTMODE_STYLE_OPT |
                                   CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr );
    rWrt.OutCSS1_SfxItemSet( rItemSet, false, rAdd );
 
    return rWrt;
}
 
SwHTMLWriter& OutCSS1_TableBGStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
                                   CSS1_OUTMODE_ENCODE|
                                   CSS1_OUTMODE_TABLEBOX, nullptr );
    OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::TableRow, nullptr );
 
    if (!rWrt.m_bFirstCSS1Property)
        rWrt.Strm().WriteChar(cCSS1_style_opt_end);
 
    return rWrt;
}
 
SwHTMLWriter& OutCSS1_NumberBulletListStyleOpt( SwHTMLWriter& rWrt, const SwNumRule& rNumRule,
                                    sal_uInt8 nLevel )
{
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT |
                                   CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_PARA, nullptr );
 
    const SwNumFormat& rNumFormat = rNumRule.Get( nLevel );
 
    tools::Long nLSpace = rNumFormat.GetAbsLSpace();
    tools::Long nFirstLineOffset = rNumFormat.GetFirstLineOffset();
    tools::Long nDfltFirstLineOffset = HTML_NUMBER_BULLET_INDENT;
    if( nLevel > 0 )
    {
        const SwNumFormat& rPrevNumFormat = rNumRule.Get( nLevel-1 );
        nLSpace -= rPrevNumFormat.GetAbsLSpace();
        nDfltFirstLineOffset = rPrevNumFormat.GetFirstLineOffset();
    }
 
    if( rWrt.IsHTMLMode(HTMLMODE_LSPACE_IN_NUMBER_BULLET) &&
        nLSpace != HTML_NUMBER_BULLET_MARGINLEFT )
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLSpace );
 
    if( rWrt.IsHTMLMode(HTMLMODE_FRSTLINE_IN_NUMBER_BULLET) &&
        nFirstLineOffset != nDfltFirstLineOffset )
        rWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent, nFirstLineOffset );
 
    if( !rWrt.m_bFirstCSS1Property )
        rWrt.Strm().WriteChar( '\"' );
 
    return rWrt;
}
 
void SwHTMLWriter::OutCSS1_FrameFormatOptions( const SwFrameFormat& rFrameFormat,
                                          HtmlFrmOpts nFrameOpts,
                                          const SdrObject *pSdrObj,
                                          const SfxItemSet *pItemSet )
{
    SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
                                CSS1_OUTMODE_ENCODE|
                                CSS1_OUTMODE_FRAME, nullptr );
 
    const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
    SvxLRSpaceItem aLRItem( rFrameFormat.GetLRSpace() );
    SvxULSpaceItem aULItem( rFrameFormat.GetULSpace() );
    if( nFrameOpts & HtmlFrmOpts::SAlign )
    {
        const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
        switch( rAnchor.GetAnchorId() )
        {
        case RndStdIds::FLY_AT_PARA:
        case RndStdIds::FLY_AT_CHAR:
            if( text::RelOrientation::FRAME == rHoriOri.GetRelationOrient() ||
                text::RelOrientation::PRINT_AREA == rHoriOri.GetRelationOrient() )
            {
                if( !(nFrameOpts & HtmlFrmOpts::Align) )
                {
                    // float
                    std::string_view pStr = text::HoriOrientation::RIGHT==rHoriOri.GetHoriOrient()
                            ? sCSS1_PV_right
                            : sCSS1_PV_left;
                    OutCSS1_PropertyAscii( sCSS1_P_float, pStr );
                }
                break;
            }
            [[fallthrough]];
 
        case RndStdIds::FLY_AT_PAGE:
        case RndStdIds::FLY_AT_FLY:
            {
                // position
                OutCSS1_PropertyAscii( sCSS1_P_position, sCSS1_PV_absolute );
 
                // For top/left we need to subtract the distance to the frame
                // from the position, as in CSS1 it is added to the position.
                // This works also for automatically aligned frames, even that
                // in this case Writer also adds the distance; because in this
                // case the Orient-Attribute contains the correct position.
 
                // top
                tools::Long nXPos=0, nYPos=0;
                bool bOutXPos = false, bOutYPos = false;
                if( RES_DRAWFRMFMT == rFrameFormat.Which() )
                {
                    OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" );
                    if( !pSdrObj )
                        pSdrObj = rFrameFormat.FindSdrObject();
                    OSL_ENSURE( pSdrObj, "Where is the SdrObject" );
                    if( pSdrObj )
                    {
                        Point aPos( pSdrObj->GetRelativePos() );
                        nXPos = aPos.X();
                        nYPos = aPos.Y();
                    }
                    bOutXPos = bOutYPos = true;
                }
                else
                {
                    bOutXPos = text::RelOrientation::CHAR != rHoriOri.GetRelationOrient();
                    nXPos = text::HoriOrientation::NONE == rHoriOri.GetHoriOrient()
                                ? rHoriOri.GetPos() : 0;
 
                    const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
                    bOutYPos = text::RelOrientation::CHAR != rVertOri.GetRelationOrient();
                    nYPos = text::VertOrientation::NONE == rVertOri.GetVertOrient()
                                 ? rVertOri.GetPos() : 0;
                }
 
                if( bOutYPos )
                {
                    if( IsHTMLMode( HTMLMODE_FLY_MARGINS) )
                    {
                        nYPos -= aULItem.GetUpper();
                        if( nYPos < 0 )
                        {
                            aULItem.SetUpper( o3tl::narrowing<sal_uInt16>(aULItem.GetUpper() + nYPos) );
                            nYPos = 0;
                        }
                    }
 
                    OutCSS1_UnitProperty( sCSS1_P_top, nYPos );
                }
 
                if( bOutXPos )
                {
                    // left
                    if( IsHTMLMode( HTMLMODE_FLY_MARGINS) )
                    {
                        nXPos -= aLRItem.GetLeft();
                        if( nXPos < 0 )
                        {
                            aLRItem.SetLeft( o3tl::narrowing<sal_uInt16>(aLRItem.GetLeft() + nXPos) );
                            nXPos = 0;
                        }
                    }
 
                    OutCSS1_UnitProperty( sCSS1_P_left, nXPos );
                }
            }
            break;
 
        default:
            ;
        }
    }
 
    // width/height
    if( nFrameOpts & HtmlFrmOpts::SSize )
    {
        if( RES_DRAWFRMFMT == rFrameFormat.Which() )
        {
            OSL_ENSURE( pSdrObj, "Do not pass a SdrObject. Inefficient" );
            if( !pSdrObj )
                pSdrObj = rFrameFormat.FindSdrObject();
            OSL_ENSURE( pSdrObj, "Where is the SdrObject" );
            if( pSdrObj )
            {
                Size aTwipSz( pSdrObj->GetLogicRect().GetSize() );
                if( nFrameOpts & HtmlFrmOpts::SWidth )
                {
                    if( nFrameOpts & HtmlFrmOpts::SPixSize )
                        OutCSS1_PixelProperty( sCSS1_P_width, aTwipSz.Width() );
                    else
                        OutCSS1_UnitProperty( sCSS1_P_width, aTwipSz.Width() );
                }
                if( nFrameOpts & HtmlFrmOpts::SHeight )
                {
                    if( nFrameOpts & HtmlFrmOpts::SPixSize )
                        OutCSS1_PixelProperty( sCSS1_P_height, aTwipSz.Height() );
                    else
                        OutCSS1_UnitProperty( sCSS1_P_height, aTwipSz.Height() );
                }
            }
        }
        else
        {
            OSL_ENSURE( HtmlFrmOpts::AbsSize & nFrameOpts,
                    "Export absolute size" );
            OSL_ENSURE( HtmlFrmOpts::AnySize & nFrameOpts,
                    "Export every size" );
            Css1FrameSize nMode = Css1FrameSize::NONE;
            if( nFrameOpts & HtmlFrmOpts::SWidth )
                nMode |= Css1FrameSize::Width;
            if( nFrameOpts & HtmlFrmOpts::SHeight )
                nMode |= Css1FrameSize::MinHeight|Css1FrameSize::FixHeight;
            if( nFrameOpts & HtmlFrmOpts::SPixSize )
                nMode |= Css1FrameSize::Pixel;
 
            OutCSS1_SwFormatFrameSize( *this, rFrameFormat.GetFrameSize(), nMode );
        }
    }
 
    const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
    // margin-*
    if( (nFrameOpts & HtmlFrmOpts::SSpace) &&
        IsHTMLMode( HTMLMODE_FLY_MARGINS) )
    {
        const SvxLRSpaceItem *pLRItem = nullptr;
        const SvxULSpaceItem *pULItem = nullptr;
        if( SfxItemState::SET == rItemSet.GetItemState( RES_LR_SPACE ) )
            pLRItem = &aLRItem;
        if( SfxItemState::SET == rItemSet.GetItemState( RES_UL_SPACE ) )
            pULItem = &aULItem;
        if( pLRItem || pULItem )
            OutCSS1_SvxULSpace_SvxLRSpace( *this, pULItem, pLRItem );
    }
 
    // border
    if( nFrameOpts & HtmlFrmOpts::SBorder )
    {
        const SfxPoolItem* pItem;
        if( nFrameOpts & HtmlFrmOpts::SNoBorder )
            OutCSS1_SvxBox( *this, rFrameFormat.GetBox() );
        else if( SfxItemState::SET==rItemSet.GetItemState( RES_BOX, true, &pItem ) )
            OutCSS1_SvxBox( *this, *pItem );
    }
 
    // background (if, then the color must be set also)
    if( nFrameOpts & HtmlFrmOpts::SBackground )
        OutCSS1_FrameFormatBackground( rFrameFormat );
 
    if( pItemSet )
        OutCSS1_SfxItemSet( *pItemSet, false );
 
    if( !m_bFirstCSS1Property )
        Strm().WriteChar( '\"' );
}
 
void SwHTMLWriter::OutCSS1_TableFrameFormatOptions( const SwFrameFormat& rFrameFormat )
{
    SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
                                CSS1_OUTMODE_ENCODE|
                                CSS1_OUTMODE_TABLE, nullptr );
 
    const SfxPoolItem *pItem;
    const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
    if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
        OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Table, nullptr );
 
    if( IsHTMLMode( HTMLMODE_PRINT_EXT ) )
        OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, false );
 
    if( SfxItemState::SET==rItemSet.GetItemState( RES_LAYOUT_SPLIT, false, &pItem ) )
        OutCSS1_SwFormatLayoutSplit( *this, *pItem );
 
    if (mbXHTML)
    {
        sal_Int16 eTabHoriOri = rFrameFormat.GetHoriOrient().GetHoriOrient();
        if (eTabHoriOri == text::HoriOrientation::CENTER)
        {
            // Emit XHTML's center using inline CSS.
            OutCSS1_Property(sCSS1_P_margin_left, "auto", nullptr, sw::Css1Background::Table);
            OutCSS1_Property(sCSS1_P_margin_right, "auto", nullptr, sw::Css1Background::Table);
        }
    }
 
    if( !m_bFirstCSS1Property )
        Strm().WriteChar( '\"' );
}
 
void SwHTMLWriter::OutCSS1_TableCellBordersAndBG(SwFrameFormat const& rFrameFormat, const SvxBrushItem *pBrushItem)
{
    SwCSS1OutMode const aMode( *this,
        CSS1_OUTMODE_STYLE_OPT_ON|CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_TABLEBOX, nullptr );
    if (pBrushItem)
        OutCSS1_SvxBrush(*this, *pBrushItem, sw::Css1Background::TableCell, nullptr);
    OutCSS1_SvxBox(*this, rFrameFormat.GetBox());
    if (!m_bFirstCSS1Property)
        Strm().WriteChar(cCSS1_style_opt_end);
}
 
void SwHTMLWriter::OutCSS1_SectionFormatOptions( const SwFrameFormat& rFrameFormat, const SwFormatCol *pCol )
{
    SwCSS1OutMode aMode( *this, CSS1_OUTMODE_STYLE_OPT_ON |
                                CSS1_OUTMODE_ENCODE|
                                CSS1_OUTMODE_SECTION, nullptr );
 
    const SfxPoolItem *pItem;
    const SfxItemSet& rItemSet = rFrameFormat.GetAttrSet();
    if( SfxItemState::SET==rItemSet.GetItemState( RES_BACKGROUND, false, &pItem ) )
        OutCSS1_SvxBrush( *this, *pItem, sw::Css1Background::Section, nullptr );
 
    if (mbXHTML)
    {
        SvxFrameDirection nDir = GetHTMLDirection(rFrameFormat.GetAttrSet());
        OString sConvertedDirection = convertDirection(nDir);
        if (!sConvertedDirection.isEmpty())
        {
            OutCSS1_Property(sCSS1_P_dir, sConvertedDirection, nullptr,
                             sw::Css1Background::Section);
        }
    }
 
    if (pCol)
    {
        OString sColumnCount(OString::number(static_cast<sal_Int32>(pCol->GetNumCols())));
        OutCSS1_PropertyAscii(sCSS1_P_column_count, sColumnCount);
    }
 
    if( !m_bFirstCSS1Property )
        Strm().WriteChar( '\"' );
}
 
static bool OutCSS1_FrameFormatBrush( SwHTMLWriter& rWrt,
                                 const SvxBrushItem& rBrushItem )
{
    bool bWritten = false;
    /// output brush of frame format, if its background color is not "no fill"/"auto fill"
    /// or it has a background graphic.
    if( rBrushItem.GetColor() != COL_TRANSPARENT ||
        !rBrushItem.GetGraphicLink().isEmpty() ||
        0 != rBrushItem.GetGraphicPos() )
    {
        OutCSS1_SvxBrush( rWrt, rBrushItem, sw::Css1Background::Fly, nullptr );
        bWritten = true;
    }
    return bWritten;
}
 
void SwHTMLWriter::OutCSS1_FrameFormatBackground( const SwFrameFormat& rFrameFormat )
{
    // If the frame itself has a background, then export.
    if( OutCSS1_FrameFormatBrush( *this, *rFrameFormat.makeBackgroundBrushItem() ) )
        return;
 
    // If the frame is not linked to a page, we use the background of the anchor.
    const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
    RndStdIds eAnchorId = rAnchor.GetAnchorId();
    const SwNode *pAnchorNode = rAnchor.GetAnchorNode();
    if (RndStdIds::FLY_AT_PAGE != eAnchorId && pAnchorNode)
    {
        if( pAnchorNode->IsContentNode() )
        {
            // If the frame is linked to a content-node,
            // we take the background of the content-node, if it has one.
            if( OutCSS1_FrameFormatBrush( *this,
                    pAnchorNode->GetContentNode()->GetSwAttrSet().GetBackground()) )
                return;
 
            // Otherwise we also could be in a table
            const SwTableNode *pTableNd = pAnchorNode->FindTableNode();
            if( pTableNd )
            {
                const SwStartNode *pBoxSttNd = pAnchorNode->FindTableBoxStartNode();
                const SwTableBox *pBox =
                    pTableNd->GetTable().GetTableBox( pBoxSttNd->GetIndex() );
 
                // If the box has a background, we take it.
                if( OutCSS1_FrameFormatBrush( *this,
                        *pBox->GetFrameFormat()->makeBackgroundBrushItem() ) )
                    return;
 
                // Otherwise we use that of the lines
                const SwTableLine *pLine = pBox->GetUpper();
                while( pLine )
                {
                    if( OutCSS1_FrameFormatBrush( *this,
                            *pLine->GetFrameFormat()->makeBackgroundBrushItem() ) )
                        return;
                    pBox = pLine->GetUpper();
                    pLine = pBox ? pBox->GetUpper() : nullptr;
                }
 
                // If there was none either, we use the background of the table.
                if( OutCSS1_FrameFormatBrush( *this,
                        *pTableNd->GetTable().GetFrameFormat()->makeBackgroundBrushItem() ) )
                    return;
            }
 
        }
 
        // If the anchor is again in a Fly-Frame, use the background of the Fly-Frame.
        const SwFrameFormat *pFrameFormat = pAnchorNode->GetFlyFormat();
        if( pFrameFormat )
        {
            OutCSS1_FrameFormatBackground( *pFrameFormat );
            return;
        }
    }
 
    // At last there is the background of the page, and as the final rescue
    // the value of the Config.
    assert(m_pCurrPageDesc && "no page template found");
    if( OutCSS1_FrameFormatBrush( *this,
                              *m_pCurrPageDesc->GetMaster().makeBackgroundBrushItem() ) )
        return;
 
    Color aColor( COL_WHITE );
 
    // The background color is normally only used in Browse-Mode.
    // We always use it for a HTML document, but for a text document
    // only if viewed in Browse-Mode.
    if( m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE) ||
        m_pDoc->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
    {
        SwViewShell *pVSh = m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
        if ( pVSh &&
             COL_TRANSPARENT != pVSh->GetViewOptions()->GetRetoucheColor())
            aColor = pVSh->GetViewOptions()->GetRetoucheColor();
    }
 
    OutCSS1_PropertyAscii(sCSS1_P_background, GetCSS1_Color(aColor));
}
 
static SwHTMLWriter& OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( SwHTMLWriter& rWrt,
                    const SvxUnderlineItem *pUItem,
                    const SvxOverlineItem *pOItem,
                    const SvxCrossedOutItem *pCOItem,
                    const SvxBlinkItem *pBItem )
{
    bool bNone = false;
    OStringBuffer sOut;
 
    if( pUItem )
    {
        switch( pUItem->GetLineStyle() )
        {
        case LINESTYLE_NONE:
            bNone = true;
            break;
        case LINESTYLE_DONTKNOW:
            break;
        default:
            if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
            {
                // this also works in HTML does not need to be written as
                // a STYLE-Options, and must not be written as Hint
                OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rWrt.mbReqIF,
                        "write underline as Hint?" );
                sOut.append(sCSS1_PV_underline);
            }
            break;
        }
    }
 
    if( pOItem )
    {
        switch( pOItem->GetLineStyle() )
        {
        case LINESTYLE_NONE:
            bNone = true;
            break;
        case LINESTYLE_DONTKNOW:
            break;
        default:
            if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
            {
                // this also works in HTML does not need to be written as
                // a STYLE-Options, and must not be written as Hint
                OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
                        "write overline as Hint?" );
                if (!sOut.isEmpty())
                    sOut.append(' ');
                sOut.append(sCSS1_PV_overline);
            }
            break;
        }
    }
 
    if( pCOItem )
    {
        switch( pCOItem->GetStrikeout() )
        {
        case STRIKEOUT_NONE:
            bNone = true;
            break;
        case STRIKEOUT_DONTKNOW:
            break;
        default:
            if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
            {
                // this also works in HTML does not need to be written as
                // a STYLE-Options, and must not be written as Hint
                OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) || rWrt.mbReqIF,
                        "write crossedOut as Hint?" );
                if (!sOut.isEmpty())
                    sOut.append(' ');
                sOut.append(sCSS1_PV_line_through);
            }
            break;
        }
    }
 
    if( pBItem )
    {
        if( !pBItem->GetValue() )
        {
            bNone = true;
        }
        else if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        {
            // this also works in HTML does not need to be written as
            // a STYLE-Options, and must not be written as Hint
            OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
                    "write blink as Hint?" );
            if (!sOut.isEmpty())
                sOut.append(' ');
            sOut.append(sCSS1_PV_blink);
        }
    }
 
    if (!sOut.isEmpty())
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sOut );
    else if( bNone )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_decoration, sCSS1_PV_none );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxCaseMap( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    switch( static_cast<const SvxCaseMapItem&>(rHt).GetCaseMap() )
    {
    case SvxCaseMap::NotMapped:
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_normal );
        break;
    case SvxCaseMap::SmallCaps:
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_variant, sCSS1_PV_small_caps );
        break;
    case SvxCaseMap::Uppercase:
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_uppercase );
        break;
    case SvxCaseMap::Lowercase:
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_lowercase );
        break;
    case SvxCaseMap::Capitalize:
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_transform, sCSS1_PV_capitalize );
        break;
    default:
        ;
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxColor( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Colors do not need to be exported for Style-Option.
    if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) &&
        !rWrt.m_bCfgPreferStyles )
        return rWrt;
    OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
            "write color as Hint?" );
 
    Color aColor( static_cast<const SvxColorItem&>(rHt).GetValue() );
    if( COL_AUTO == aColor )
        aColor = COL_BLACK;
 
    rWrt.OutCSS1_PropertyAscii(sCSS1_P_color, GetCSS1_Color(aColor));
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxCrossedOut( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // This function only exports Hints!
    // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
 
    if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
        OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
                nullptr, nullptr, static_cast<const SvxCrossedOutItem *>(&rHt), nullptr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFont( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // No need to export Fonts for the Style-Option.
    if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        return rWrt;
 
    sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
    switch( rHt.Which() )
    {
    case RES_CHRATR_CJK_FONT:   nScript = CSS1_OUTMODE_CJK; break;
    case RES_CHRATR_CTL_FONT:   nScript = CSS1_OUTMODE_CTL; break;
    }
    if( !rWrt.IsCSS1Script( nScript ) )
        return rWrt;
 
    OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
            "write Font as Hint?" );
 
    OUString sOut;
    // MS IE3b1 has problems with single quotes
    sal_uInt16 nMode = rWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_ON;
    sal_Unicode cQuote = nMode == CSS1_OUTMODE_RULE_ON ? '\"' : '\'';
    SwHTMLWriter::PrepareFontList( static_cast<const SvxFontItem&>(rHt), sOut, cQuote,
                                   true );
 
    rWrt.OutCSS1_Property( sCSS1_P_font_family, sOut );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFontHeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Font-Height need not be exported in the Style-Option.
    // For Drop-Caps another Font-Size is exported.
    if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ||
        rWrt.IsCSS1Source( CSS1_OUTMODE_DROPCAP ) )
        return rWrt;
 
    sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
    switch( rHt.Which() )
    {
    case RES_CHRATR_CJK_FONTSIZE:   nScript = CSS1_OUTMODE_CJK; break;
    case RES_CHRATR_CTL_FONTSIZE:   nScript = CSS1_OUTMODE_CTL; break;
    }
    if( !rWrt.IsCSS1Script( nScript ) )
        return rWrt;
 
    sal_uInt32 nHeight = static_cast<const SvxFontHeightItem&>(rHt).GetHeight();
    OString sHeight(OString::number(nHeight/20) + sCSS1_UNIT_pt);
    rWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sHeight);
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxPosture( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
    switch( rHt.Which() )
    {
    case RES_CHRATR_CJK_POSTURE:    nScript = CSS1_OUTMODE_CJK; break;
    case RES_CHRATR_CTL_POSTURE:    nScript = CSS1_OUTMODE_CTL; break;
    }
    if( !rWrt.IsCSS1Script( nScript ) )
        return rWrt;
 
    std::string_view pStr;
    switch( static_cast<const SvxPostureItem&>(rHt).GetPosture() )
    {
    case ITALIC_NONE:       pStr = sCSS1_PV_normal;     break;
    case ITALIC_OBLIQUE:    pStr = sCSS1_PV_oblique;    break;
    case ITALIC_NORMAL:
        if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        {
            // this also works in HTML does not need to be written as
            // a STYLE-Options, and must not be written as Hint
            OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
                    "write italic as Hint?" );
            pStr = sCSS1_PV_italic;
        }
        break;
    default:
        ;
    }
 
    if( !pStr.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_style, pStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxKerning( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    sal_Int16 nValue = static_cast<const SvxKerningItem&>(rHt).GetValue();
    if( nValue )
    {
        OStringBuffer sOut;
        if( nValue < 0 )
        {
            sOut.append('-');
            nValue = -nValue;
        }
 
        // Width as n.n pt
        nValue = (nValue + 1) / 2;  // 1/10pt
        sOut.append(OString::number(nValue  / 10) + "." + OString::number(nValue % 10) +
                    sCSS1_UNIT_pt);
 
        rWrt.OutCSS1_PropertyAscii(sCSS1_P_letter_spacing, sOut);
        sOut.setLength(0);
    }
    else
    {
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_letter_spacing,
                                        sCSS1_PV_normal );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxLanguage( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Only export Language rules
    if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        return rWrt;
 
    sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
    switch( rHt.Which() )
    {
    case RES_CHRATR_CJK_LANGUAGE:   nScript = CSS1_OUTMODE_CJK; break;
    case RES_CHRATR_CTL_LANGUAGE:   nScript = CSS1_OUTMODE_CTL; break;
    }
    if( !rWrt.IsCSS1Script( nScript ) )
        return rWrt;
 
    OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
            "write Language as Hint?" );
 
    LanguageType eLang = static_cast<const SvxLanguageItem &>(rHt).GetLanguage();
    if( LANGUAGE_DONTKNOW == eLang )
        return rWrt;
 
    OUString sOut = LanguageTag::convertToBcp47( eLang );
 
    rWrt.OutCSS1_Property( sCSS1_P_so_language, sOut );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxUnderline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // This function only exports Hints!
    // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
 
    if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
        OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
                static_cast<const SvxUnderlineItem *>(&rHt), nullptr, nullptr, nullptr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxOverline( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // This function only exports Hints!
    // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
 
    if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
        OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
                nullptr, static_cast<const SvxOverlineItem *>(&rHt), nullptr, nullptr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxHidden( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    if ( static_cast<const SvxCharHiddenItem&>(rHt).GetValue() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_display, sCSS1_PV_none );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFontWeight( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    sal_uInt16 nScript = CSS1_OUTMODE_WESTERN;
    switch( rHt.Which() )
    {
    case RES_CHRATR_CJK_WEIGHT: nScript = CSS1_OUTMODE_CJK; break;
    case RES_CHRATR_CTL_WEIGHT: nScript = CSS1_OUTMODE_CTL; break;
    }
    if( !rWrt.IsCSS1Script( nScript ) )
        return rWrt;
 
    std::string_view pStr;
    switch( static_cast<const SvxWeightItem&>(rHt).GetWeight() )
    {
    case WEIGHT_ULTRALIGHT: pStr = sCSS1_PV_extra_light;    break;
    case WEIGHT_LIGHT:      pStr = sCSS1_PV_light;          break;
    case WEIGHT_SEMILIGHT:  pStr = sCSS1_PV_demi_light;     break;
    case WEIGHT_NORMAL:     pStr = sCSS1_PV_normal;         break;
    case WEIGHT_SEMIBOLD:   pStr = sCSS1_PV_demi_bold;      break;
    case WEIGHT_BOLD:
        if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        {
            // this also works in HTML does not need to be written as
            // a STYLE-Options, and must not be written as Hint
            OSL_ENSURE( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT),
                    "write bold as Hint?" );
            pStr = sCSS1_PV_bold;
        }
        break;
    case WEIGHT_ULTRABOLD:  pStr = sCSS1_PV_extra_bold;     break;
    default:
        pStr = sCSS1_PV_normal;
    }
 
    if( !pStr.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_font_weight, pStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxBlink( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // This function only exports Hints!
    // Otherwise OutCSS1_SvxTextLn_SvxCrOut_SvxBlink() is called directly.
 
    if( rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
        OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( rWrt,
                nullptr, nullptr, nullptr, static_cast<const SvxBlinkItem *>(&rHt) );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxLineSpacing( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Netscape4 has big problems with cell heights if the line spacing is
    // changed within a table and the width of the table is not calculated
    // automatically (== if there is a WIDTH-Option)
    if( rWrt.m_bOutTable && rWrt.m_bCfgNetscape4 )
        return rWrt;
 
    const SvxLineSpacingItem& rLSItem = static_cast<const SvxLineSpacingItem&>(rHt);
 
    sal_uInt16 nHeight = 0;
    sal_uInt16 nPercentHeight = 0;
    SvxLineSpaceRule eLineSpace = rLSItem.GetLineSpaceRule();
    switch( rLSItem.GetInterLineSpaceRule() )
    {
    case SvxInterLineSpaceRule::Off:
    case SvxInterLineSpaceRule::Fix:
        {
            switch( eLineSpace )
            {
            case SvxLineSpaceRule::Min:
            case SvxLineSpaceRule::Fix:
                nHeight = rLSItem.GetLineHeight();
                break;
            case SvxLineSpaceRule::Auto:
                nPercentHeight = 100;
                break;
            default:
                ;
            }
        }
        break;
    case SvxInterLineSpaceRule::Prop:
        nPercentHeight = rLSItem.GetPropLineSpace();
        break;
 
    default:
        ;
    }
 
    if( nHeight )
        rWrt.OutCSS1_UnitProperty( sCSS1_P_line_height, static_cast<tools::Long>(nHeight) );
    else if( nPercentHeight &&
        !(nPercentHeight < 115 && rWrt.m_bParaDotLeaders )) // avoid HTML scrollbars and missing descenders
    {
        OString sHeight(OString::number(nPercentHeight) + "%");
        rWrt.OutCSS1_PropertyAscii(sCSS1_P_line_height, sHeight);
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxAdjust( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Export Alignment in Style-Option only if the Tag does not allow ALIGN=xxx
    if( rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) &&
        !rWrt.m_bNoAlign)
        return rWrt;
 
    std::string_view pStr;
    switch( static_cast<const SvxAdjustItem&>(rHt).GetAdjust() )
    {
    case SvxAdjust::Left:   pStr = sCSS1_PV_left;       break;
    case SvxAdjust::Right:  pStr = sCSS1_PV_right;      break;
    case SvxAdjust::Block:  pStr = sCSS1_PV_justify;    break;
    case SvxAdjust::Center: pStr = sCSS1_PV_center;     break;
    default:
        ;
    }
 
    if( !pStr.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_text_align, pStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFormatSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    std::string_view pStr = static_cast<const SvxFormatSplitItem&>(rHt).GetValue()
                            ? sCSS1_PV_auto
                            : sCSS1_PV_avoid;
    rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SwFormatLayoutSplit( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    std::string_view pStr = static_cast<const SwFormatLayoutSplit&>(rHt).GetValue()
                            ? sCSS1_PV_auto
                            : sCSS1_PV_avoid;
    rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_inside, pStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxWidows( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    OString aStr(OString::number(static_cast<const SvxWidowsItem&>(rHt).GetValue()));
    rWrt.OutCSS1_PropertyAscii( sCSS1_P_widows, aStr );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxOrphans( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    OString aStr(OString::number(static_cast<const SvxOrphansItem&>(rHt).GetValue()));
    rWrt.OutCSS1_PropertyAscii( sCSS1_P_orphans, aStr );
 
    return rWrt;
}
 
static void OutCSS1_SwFormatDropAttrs( SwHTMLWriter& rHWrt,
                                    const SwFormatDrop& rDrop,
                                     const SfxItemSet *pCharFormatItemSet )
{
    // Text flows around on right side
    rHWrt.OutCSS1_PropertyAscii( sCSS1_P_float, sCSS1_PV_left );
 
    // number of lines -> use % for Font-Height!
    OString sOut(OString::number(rDrop.GetLines()*100) + "%");
    rHWrt.OutCSS1_PropertyAscii(sCSS1_P_font_size, sOut);
 
    // distance to Text = right margin
    sal_uInt16 nDistance = rDrop.GetDistance();
    if( nDistance > 0 )
        rHWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, nDistance );
 
    const SwCharFormat *pDCCharFormat = rDrop.GetCharFormat();
    if( pCharFormatItemSet )
        rHWrt.OutCSS1_SfxItemSet( *pCharFormatItemSet );
    else if( pDCCharFormat )
        rHWrt.OutCSS1_SfxItemSet( pDCCharFormat->GetAttrSet() );
    else if( (rHWrt.m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF) == CSS1_OUTMODE_RULE_OFF )
        rHWrt.Strm().WriteOString( sCSS1_rule_end );
 
}
 
static SwHTMLWriter& OutCSS1_SwFormatDrop( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // never export as an Option of a paragraph, but only as Hints
    if( !rWrt.IsCSS1Source(CSS1_OUTMODE_HINT) )
        return rWrt;
 
    if( rWrt.m_bTagOn )
    {
        SwCSS1OutMode aMode( rWrt,
                             rWrt.m_nCSS1Script|CSS1_OUTMODE_SPAN_TAG1_ON|CSS1_OUTMODE_ENCODE|
                             CSS1_OUTMODE_DROPCAP, nullptr );
 
        OutCSS1_SwFormatDropAttrs( rWrt, static_cast<const SwFormatDrop&>(rHt) );
        // A "> is already printed by the calling OutCSS1_HintAsSpanTag.
    }
    else
    {
        HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SwFormatFrameSize( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
                                     Css1FrameSize nMode )
{
    const SwFormatFrameSize& rFSItem = static_cast<const SwFormatFrameSize&>(rHt);
 
    if( nMode & Css1FrameSize::Width )
    {
        sal_uInt8 nPercentWidth = rFSItem.GetWidthPercent();
        if( nPercentWidth )
        {
            OString sOut(OString::number(nPercentWidth) + "%");
            rWrt.OutCSS1_PropertyAscii(sCSS1_P_width, sOut);
        }
        else if( nMode & Css1FrameSize::Pixel )
        {
            rWrt.OutCSS1_PixelProperty( sCSS1_P_width,
                                            rFSItem.GetSize().Width() );
        }
        else
        {
            rWrt.OutCSS1_UnitProperty( sCSS1_P_width,
                                           rFSItem.GetSize().Width() );
        }
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFirstLineIndent(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
{
    const SvxFirstLineIndentItem & rFirstLine(static_cast<const SvxFirstLineIndentItem&>(rHt));
 
    // No Export of a firm attribute is needed if the new values
    // match that of the current template
 
    // The LineIndent of the first line might contain the room for numbering
    tools::Long nFirstLineIndent = static_cast<tools::Long>(rFirstLine.GetTextFirstLineOffset())
            - rWrt.m_nFirstLineIndent;
    if (rWrt.m_nDfltFirstLineIndent != nFirstLineIndent)
    {
        rWrt.OutCSS1_UnitProperty(sCSS1_P_text_indent, nFirstLineIndent);
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxTextLeftMargin(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
{
    const SvxTextLeftMarginItem& rLeftMargin(static_cast<const SvxTextLeftMarginItem&>(rHt));
 
    // No Export of a firm attribute is needed if the new values
    // match that of the current template
 
    // A left margin can exist because of a list nearby
    tools::Long nLeftMargin = rLeftMargin.GetTextLeft() - rWrt.m_nLeftMargin;
    if (rWrt.m_nDfltLeftMargin != nLeftMargin)
    {
        rWrt.OutCSS1_UnitProperty(sCSS1_P_margin_left, nLeftMargin);
 
        // max-width = max-width - margin-left for TOC paragraphs with dot leaders
        if (rWrt.m_bParaDotLeaders)
            rWrt.OutCSS1_UnitProperty(sCSS1_P_max_width, o3tl::convert(DOT_LEADERS_MAX_WIDTH, o3tl::Length::cm, o3tl::Length::twip) - nLeftMargin);
 
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxRightMargin(SwHTMLWriter & rWrt, SfxPoolItem const& rHt)
{
    const SvxRightMarginItem& rRightMargin(static_cast<const SvxRightMarginItem&>(rHt));
 
    // No Export of a firm attribute is needed if the new values
    // match that of the current template
 
    if (rWrt.m_nDfltRightMargin != rRightMargin.GetRight())
    {
        rWrt.OutCSS1_UnitProperty(sCSS1_P_margin_right, rRightMargin.GetRight());
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxLRSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    const SvxLRSpaceItem& rLRItem = static_cast<const SvxLRSpaceItem&>(rHt);
 
    // No Export of a firm attribute is needed if the new values
    // match that of the current template
 
    // A left margin can exist because of a list nearby
    tools::Long nLeftMargin = rLRItem.GetTextLeft() - rWrt.m_nLeftMargin;
    if( rWrt.m_nDfltLeftMargin != nLeftMargin )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_left, nLeftMargin );
 
        // max-width = max-width - margin-left for TOC paragraphs with dot leaders
        if( rWrt.m_bParaDotLeaders )
            rWrt.OutCSS1_UnitProperty( sCSS1_P_max_width, o3tl::convert(DOT_LEADERS_MAX_WIDTH, o3tl::Length::cm, o3tl::Length::twip) - nLeftMargin );
 
    }
 
    if( rWrt.m_nDfltRightMargin != rLRItem.GetRight() )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_right, rLRItem.GetRight() );
    }
 
    // The LineIndent of the first line might contain the room for numbering
    tools::Long nFirstLineIndent = static_cast<tools::Long>(rLRItem.GetTextFirstLineOffset()) -
        rWrt.m_nFirstLineIndent;
    if( rWrt.m_nDfltFirstLineIndent != nFirstLineIndent )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_text_indent,
                                     nFirstLineIndent );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxULSpace( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    const SvxULSpaceItem& rULItem = static_cast<const SvxULSpaceItem&>(rHt);
 
    if( rWrt.m_nDfltTopMargin != rULItem.GetUpper() )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_top,
                                     static_cast<tools::Long>(rULItem.GetUpper()) );
    }
 
    if( rWrt.m_nDfltBottomMargin != rULItem.GetLower() )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin_bottom,
                                     static_cast<tools::Long>(rULItem.GetLower()) );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
                                        const SvxULSpaceItem *pULItem,
                                        const SvxLRSpaceItem *pLRItem )
{
    if( pLRItem && pULItem &&
        pLRItem->GetLeft() == pLRItem->GetRight() &&
        pLRItem->GetLeft() == pULItem->GetUpper() &&
        pLRItem->GetLeft() == pULItem->GetLower() &&
        pLRItem->GetLeft() != rWrt.m_nDfltLeftMargin &&
        pLRItem->GetRight() != rWrt.m_nDfltRightMargin &&
        pULItem->GetUpper() != rWrt.m_nDfltTopMargin &&
        pULItem->GetLower() != rWrt.m_nDfltBottomMargin )
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_margin, pLRItem->GetLeft() );
    }
    else
    {
        if( pLRItem )
            OutCSS1_SvxLRSpace( rWrt, *pLRItem );
        if( pULItem )
            OutCSS1_SvxULSpace( rWrt, *pULItem );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxULSpace_SvxLRSpace( SwHTMLWriter& rWrt,
                                        const SfxItemSet& rItemSet )
{
    const SvxLRSpaceItem *pLRSpace = rItemSet.GetItemIfSet( RES_LR_SPACE, false/*bDeep*/ );
    const SvxULSpaceItem *pULSpace = rItemSet.GetItemIfSet( RES_UL_SPACE, false/*bDeep*/ );
 
    if( pLRSpace || pULSpace )
        OutCSS1_SvxULSpace_SvxLRSpace( rWrt, pULSpace, pLRSpace );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
                                        const SvxFormatBreakItem *pBreakItem,
                                        const SwFormatPageDesc *pPDescItem,
                                        const SvxFormatKeepItem *pKeepItem )
{
    if( !rWrt.IsHTMLMode(HTMLMODE_PRINT_EXT) )
        return rWrt;
 
    std::string_view pBreakBefore;
    std::string_view pBreakAfter;
 
    if( pKeepItem )
    {
        pBreakAfter = pKeepItem->GetValue() ? sCSS1_PV_avoid : sCSS1_PV_auto;
    }
    if( pBreakItem )
    {
        switch( pBreakItem->GetBreak() )
        {
        case SvxBreak::NONE:
            pBreakBefore = sCSS1_PV_auto;
            if( pBreakAfter.empty() )
                pBreakAfter = sCSS1_PV_auto;
            break;
 
        case SvxBreak::PageBefore:
            pBreakBefore = sCSS1_PV_always;
            break;
 
        case SvxBreak::PageAfter:
            pBreakAfter= sCSS1_PV_always;
            break;
 
        default:
            ;
        }
    }
    if( pPDescItem )
    {
        const SwPageDesc *pPDesc = pPDescItem->GetPageDesc();
        if( pPDesc )
        {
            switch( pPDesc->GetPoolFormatId() )
            {
            case RES_POOLPAGE_LEFT:     pBreakBefore = sCSS1_PV_left;   break;
            case RES_POOLPAGE_RIGHT:    pBreakBefore = sCSS1_PV_right;  break;
            default:                    pBreakBefore = sCSS1_PV_always; break;
            }
        }
        else if( pBreakBefore.empty() )
        {
            pBreakBefore = sCSS1_PV_auto;
        }
    }
 
    if (rWrt.mbSkipHeaderFooter)
        // No page break when writing only a fragment.
        return rWrt;
 
    if( !pBreakBefore.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_before,
                                        pBreakBefore );
    if( !pBreakAfter.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_page_break_after,
                                        pBreakAfter );
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( SwHTMLWriter& rWrt,
                                        const SfxItemSet& rItemSet,
                                        bool bDeep )
{
    const SvxFormatBreakItem *pBreakItem = rItemSet.GetItemIfSet( RES_BREAK, bDeep );
 
    const SwFormatPageDesc *pPDescItem = nullptr;
    if( !rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) ||
        !rWrt.m_bCSS1IgnoreFirstPageDesc ||
        rWrt.m_pStartNdIdx->GetIndex() !=
                    rWrt.m_pCurrentPam->GetPoint()->GetNodeIndex() )
        pPDescItem = rItemSet.GetItemIfSet( RES_PAGEDESC, bDeep );
 
    const SvxFormatKeepItem *pKeepItem = rItemSet.GetItemIfSet( RES_KEEP, bDeep );
 
    if( pBreakItem || pPDescItem || pKeepItem )
        OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( rWrt, pBreakItem,
                                                   pPDescItem, pKeepItem );
 
    return rWrt;
}
 
// Wrapper for OutCSS1_SfxItemSet etc.
static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    OutCSS1_SvxBrush( rWrt, rHt, sw::Css1Background::Attr, nullptr );
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxBrush( SwHTMLWriter& rWrt, const SfxPoolItem& rHt,
                                 sw::Css1Background nMode,
                                 const OUString* pGraphicName)
{
    // The Character-Attribute is skipped, if we are about to
    // exporting options
    if( rHt.Which() < RES_CHRATR_END &&
        rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        return rWrt;
 
    // start getting a few values
//  const Brush &rBrush = static_cast<const SvxBrushItem &>(rHt).GetBrush();
    const Color & rColor = static_cast<const SvxBrushItem &>(rHt).GetColor();
    OUString aLink = pGraphicName ? *pGraphicName
                            : static_cast<const SvxBrushItem &>(rHt).GetGraphicLink();
    bool bOwn = false;
    SvxGraphicPosition ePos = static_cast<const SvxBrushItem &>(rHt).GetGraphicPos();
    if( sw::Css1Background::Page == nMode && !rWrt.mbEmbedImages )
    {
        // page style images are exported if not tiled
        if( aLink.isEmpty() || GPOS_TILED==ePos )
            return rWrt;
    }
 
    // get the color
    bool bColor = false;
    /// set <bTransparent> to true, if color is "no fill"/"auto fill"
    bool bTransparent = (rColor == COL_TRANSPARENT);
    Color aColor;
    if( !bTransparent )
    {
        aColor = rColor;
        bColor = true;
    }
 
    // and now the Graphic
    OUString aGraphicInBase64;
 
    // Embedded Graphic -> export WriteEmbedded
    const Graphic* pGrf = nullptr;
    if( rWrt.mbEmbedImages || aLink.isEmpty())
    {
        pGrf = static_cast<const SvxBrushItem &>(rHt).GetGraphic();
        if( pGrf )
        {
            if( !XOutBitmap::GraphicToBase64(*pGrf, aGraphicInBase64) )
            {
                rWrt.m_nWarn = WARN_SWG_POOR_LOAD;
            }
        }
        aLink.clear();
    }
    else if( !pGraphicName && rWrt.m_bCfgCpyLinkedGrfs )
    {
        OUString aGraphicAsLink = aLink;
        bOwn = rWrt.CopyLocalFileToINet( aGraphicAsLink );
        aLink = aGraphicAsLink;
    }
    // In tables we only export something if there is a Graphic
    if( (nMode == sw::Css1Background::Table || nMode == sw::Css1Background::TableRow) && !pGrf && !aLink.isEmpty())
        return rWrt;
 
    // if necessary, add the orientation of the Graphic
    std::u16string_view pRepeat, pHori;
    std::string_view pVert;
    if( pGrf || !aLink.isEmpty() )
    {
        if( GPOS_TILED==ePos )
        {
            pRepeat = sCSS1_PV_repeat;
        }
        else
        {
            switch( ePos )
            {
            case GPOS_LT:
            case GPOS_MT:
            case GPOS_RT:
                pHori = sCSS1_PV_top;
                break;
 
            case GPOS_LM:
            case GPOS_MM:
            case GPOS_RM:
                pHori = sCSS1_PV_middle;
                break;
 
            case GPOS_LB:
            case GPOS_MB:
            case GPOS_RB:
                pHori = sCSS1_PV_bottom;
                break;
 
            default:
                ;
            }
 
            switch( ePos )
            {
            case GPOS_LT:
            case GPOS_LM:
            case GPOS_LB:
                pVert = sCSS1_PV_left;
                break;
 
            case GPOS_MT:
            case GPOS_MM:
            case GPOS_MB:
                pVert = sCSS1_PV_center;
                break;
 
            case GPOS_RT:
            case GPOS_RM:
            case GPOS_RB:
                pVert = sCSS1_PV_right;
                break;
 
            default:
                ;
            }
 
            if( !pHori.empty() || !pVert.empty() )
                pRepeat = sCSS1_PV_no_repeat;
        }
    }
 
    // now build the string
    OUString sOut;
    if( !pGrf && aLink.isEmpty() && !bColor )
    {
        // no color and no Link, but a transparent Brush
        if( bTransparent && sw::Css1Background::Fly != nMode )
            sOut += sCSS1_PV_transparent;
    }
    else
    {
        if( bColor )
        {
            OString sTmp(GetCSS1_Color(aColor));
            sOut += OStringToOUString(sTmp, RTL_TEXTENCODING_ASCII_US);
        }
 
        if( pGrf || !aLink.isEmpty() )
        {
            if( bColor )
                sOut += " ";
 
            if(pGrf)
            {
                sOut += OUString::Concat(sCSS1_url) +
                    "(\'" OOO_STRING_SVTOOLS_HTML_O_data ":" + aGraphicInBase64 + "\')";
            }
            else
            {
                sOut += OUString::Concat(sCSS1_url) + "(" + rWrt.normalizeURL(aLink, bOwn) + ")";
            }
 
            if( !pRepeat.empty() )
            {
                sOut += OUString::Concat(" ") + pRepeat;
            }
 
            if( !pHori.empty() )
            {
                sOut += OUString::Concat(" ") + pHori;
            }
            if( !pVert.empty() )
            {
                sOut += " " + OStringToOUString(pVert, RTL_TEXTENCODING_ASCII_US);
            }
 
            sOut += OUString::Concat(" ") + sCSS1_PV_scroll + " ";
        }
    }
 
    if( !sOut.isEmpty() )
    {
        rWrt.OutCSS1_Property(sCSS1_P_background, std::string_view(), &sOut,
                                  nMode);
    }
 
    return rWrt;
}
 
static void OutCSS1_SvxBorderLine( SwHTMLWriter& rWrt,
                                   std::string_view pProperty,
                                   const SvxBorderLine *pLine )
{
    if( !pLine || pLine->isEmpty() )
    {
        rWrt.OutCSS1_PropertyAscii( pProperty, sCSS1_PV_none );
        return;
    }
 
    sal_Int32 nWidth = pLine->GetWidth();
 
    OStringBuffer sOut;
    if( nWidth <= o3tl::convert(1, o3tl::Length::px, o3tl::Length::twip) )
    {
        // If the width is smaller than one pixel, then export as 1px
        // so that Netscape and IE show the line.
        sOut.append("1px");
    }
    else
    {
        nWidth *= 5;    // 1/100pt
 
        // width in n.nn pt
        sOut.append(OString::number(nWidth / 100) + "." + OString::number((nWidth/10) % 10) +
                    OString::number(nWidth % 10) + sCSS1_UNIT_pt);
    }
 
    // Line-Style: solid or double
    sOut.append(' ');
    switch (pLine->GetBorderLineStyle())
    {
        case SvxBorderLineStyle::SOLID:
            sOut.append(sCSS1_PV_solid);
            break;
        case SvxBorderLineStyle::DOTTED:
            sOut.append(sCSS1_PV_dotted);
            break;
        case SvxBorderLineStyle::DASHED:
            sOut.append(sCSS1_PV_dashed);
            break;
        case SvxBorderLineStyle::DOUBLE:
        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            sOut.append(sCSS1_PV_double);
            break;
        case SvxBorderLineStyle::EMBOSSED:
            sOut.append(sCSS1_PV_ridge);
            break;
        case SvxBorderLineStyle::ENGRAVED:
            sOut.append(sCSS1_PV_groove);
            break;
        case SvxBorderLineStyle::INSET:
            sOut.append(sCSS1_PV_inset);
            break;
        case SvxBorderLineStyle::OUTSET:
            sOut.append(sCSS1_PV_outset);
            break;
        default:
            sOut.append(sCSS1_PV_none);
    }
    sOut.append(' ');
 
    // and also the color
    sOut.append(GetCSS1_Color(pLine->GetColor()));
 
    rWrt.OutCSS1_PropertyAscii(pProperty, sOut);
}
 
SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Avoid interference between character and paragraph attributes
    if( rHt.Which() < RES_CHRATR_END &&
        rWrt.IsCSS1Source( CSS1_OUTMODE_PARA ) )
        return rWrt;
 
    if( rHt.Which() == RES_CHRATR_BOX )
    {
        static constexpr std::string_view inline_block("inline-block");
        if( rWrt.m_bTagOn )
        {
            // Inline-block to make the line height changing correspond to the character border
            rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, inline_block);
        }
        else
        {
            if (!IgnorePropertyForReqIF(rWrt.mbReqIF, sCSS1_P_display, inline_block))
                HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
            return rWrt;
        }
    }
 
    const SvxBoxItem& rBoxItem = static_cast<const SvxBoxItem&>(rHt);
    const SvxBorderLine *pTop = rBoxItem.GetTop();
    const SvxBorderLine *pBottom = rBoxItem.GetBottom();
    const SvxBorderLine *pLeft = rBoxItem.GetLeft();
    const SvxBorderLine *pRight = rBoxItem.GetRight();
 
    if( (pTop && pBottom && pLeft && pRight &&
         *pTop == *pBottom && *pTop == *pLeft && *pTop == *pRight) ||
         (!pTop && !pBottom && !pLeft && !pRight) )
    {
        // all Lines are set and equal, or all Lines are not set
        // => border : ...
        OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border, pTop );
    }
    else
    {
        // otherwise export all Lines separately
        OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_top, pTop );
        OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_bottom, pBottom );
        OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_left, pLeft );
        OutCSS1_SvxBorderLine( rWrt, sCSS1_P_border_right, pRight );
    }
 
    tools::Long nTopDist = pTop ? rBoxItem.GetDistance( SvxBoxItemLine::TOP ) : 0;
    tools::Long nBottomDist = pBottom ? rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) : 0;
    tools::Long nLeftDist = pLeft ? rBoxItem.GetDistance( SvxBoxItemLine::LEFT ) : 0;
    tools::Long nRightDist = pRight ? rBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) : 0;
 
    if( nTopDist == nBottomDist && nLeftDist == nRightDist )
    {
        OStringBuffer sVal;
        AddUnitPropertyValue(sVal, nTopDist, rWrt.GetCSS1Unit());
        if( nTopDist != nLeftDist )
        {
            sVal.append(' ');
            AddUnitPropertyValue(sVal, nLeftDist, rWrt.GetCSS1Unit());
        }
        rWrt.OutCSS1_PropertyAscii(sCSS1_P_padding, sVal);
    }
    else
    {
        rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_top, nTopDist );
        rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_bottom, nBottomDist );
        rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_left, nLeftDist );
        rWrt.OutCSS1_UnitProperty( sCSS1_P_padding_right, nRightDist );
    }
 
    return rWrt;
}
 
static SwHTMLWriter& OutCSS1_SvxFrameDirection( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    // Language will be exported rules only
    if( !rWrt.IsCSS1Source( CSS1_OUTMODE_TEMPLATE ) )
        return rWrt;
 
    SvxFrameDirection nDir =
        static_cast< const SvxFrameDirectionItem& >( rHt ).GetValue();
    std::string_view pStr;
    switch( nDir )
    {
    case SvxFrameDirection::Horizontal_LR_TB:
    case SvxFrameDirection::Vertical_LR_TB:
        pStr = sCSS1_PV_ltr;
        break;
    case SvxFrameDirection::Horizontal_RL_TB:
    case SvxFrameDirection::Vertical_RL_TB:
        pStr = sCSS1_PV_rtl;
        break;
    case SvxFrameDirection::Environment:
        pStr = sCSS1_PV_inherit;
        break;
    default: break;
    }
 
    if( !pStr.empty() )
        rWrt.OutCSS1_PropertyAscii( sCSS1_P_direction, pStr );
 
    return rWrt;
}
 
/*
 * Place here the table for the HTML-Function-Pointer to the
 * Export-Functions.
 * They are local structures, only needed within the HTML-DLL.
 */
 
SwAttrFnTab const aCSS1AttrFnTab = {
/* RES_CHRATR_CASEMAP   */          OutCSS1_SvxCaseMap,
/* RES_CHRATR_CHARSETCOLOR  */      nullptr,
/* RES_CHRATR_COLOR */              OutCSS1_SvxColor,
/* RES_CHRATR_CONTOUR   */          nullptr,
/* RES_CHRATR_CROSSEDOUT    */      OutCSS1_SvxCrossedOut,
/* RES_CHRATR_ESCAPEMENT    */      nullptr,
/* RES_CHRATR_FONT  */              OutCSS1_SvxFont,
/* RES_CHRATR_FONTSIZE  */          OutCSS1_SvxFontHeight,
/* RES_CHRATR_KERNING   */          OutCSS1_SvxKerning,
/* RES_CHRATR_LANGUAGE  */          OutCSS1_SvxLanguage,
/* RES_CHRATR_POSTURE   */          OutCSS1_SvxPosture,
/* RES_CHRATR_UNUSED1*/             nullptr,
/* RES_CHRATR_SHADOWED  */          nullptr,
/* RES_CHRATR_UNDERLINE */          OutCSS1_SvxUnderline,
/* RES_CHRATR_WEIGHT    */          OutCSS1_SvxFontWeight,
/* RES_CHRATR_WORDLINEMODE  */      nullptr,
/* RES_CHRATR_AUTOKERN  */          nullptr,
/* RES_CHRATR_BLINK */              OutCSS1_SvxBlink,
/* RES_CHRATR_NOHYPHEN  */          nullptr, // new: don't separate
/* RES_CHRATR_UNUSED2 */            nullptr,
/* RES_CHRATR_BACKGROUND */         OutCSS1_SvxBrush, // new: character background
/* RES_CHRATR_CJK_FONT */           OutCSS1_SvxFont,
/* RES_CHRATR_CJK_FONTSIZE */       OutCSS1_SvxFontHeight,
/* RES_CHRATR_CJK_LANGUAGE */       OutCSS1_SvxLanguage,
/* RES_CHRATR_CJK_POSTURE */        OutCSS1_SvxPosture,
/* RES_CHRATR_CJK_WEIGHT */         OutCSS1_SvxFontWeight,
/* RES_CHRATR_CTL_FONT */           OutCSS1_SvxFont,
/* RES_CHRATR_CTL_FONTSIZE */       OutCSS1_SvxFontHeight,
/* RES_CHRATR_CTL_LANGUAGE */       OutCSS1_SvxLanguage,
/* RES_CHRATR_CTL_POSTURE */        OutCSS1_SvxPosture,
/* RES_CHRATR_CTL_WEIGHT */         OutCSS1_SvxFontWeight,
/* RES_CHRATR_ROTATE */             nullptr,
/* RES_CHRATR_EMPHASIS_MARK */      nullptr,
/* RES_CHRATR_TWO_LINES */          nullptr,
/* RES_CHRATR_SCALEW */             nullptr,
/* RES_CHRATR_RELIEF */             nullptr,
/* RES_CHRATR_HIDDEN */             OutCSS1_SvxHidden,
/* RES_CHRATR_OVERLINE */           OutCSS1_SvxOverline,
/* RES_CHRATR_RSID */               nullptr,
/* RES_CHRATR_BOX */                OutCSS1_SvxBox,
/* RES_CHRATR_SHADOW */             nullptr,
/* RES_CHRATR_HIGHLIGHT */          nullptr,
/* RES_CHRATR_GRABBAG */            nullptr,
/* RES_CHRATR_BIDIRTL */            nullptr,
/* RES_CHRATR_IDCTHINT */           nullptr,
 
/* RES_TXTATR_REFMARK */            nullptr,
/* RES_TXTATR_TOXMARK */            nullptr,
/* RES_TXTATR_META */               nullptr,
/* RES_TXTATR_METAFIELD */          nullptr,
/* RES_TXTATR_AUTOFMT */            nullptr,
/* RES_TXTATR_INETFMT */            nullptr,
/* RES_TXTATR_CHARFMT */            nullptr,
/* RES_TXTATR_CJK_RUBY */           nullptr,
/* RES_TXTATR_UNKNOWN_CONTAINER */  nullptr,
/* RES_TXTATR_INPUTFIELD */         nullptr,
/* RES_TXTATR_CONTENTCONTROL */     nullptr,
 
/* RES_TXTATR_FIELD */              nullptr,
/* RES_TXTATR_FLYCNT */             nullptr,
/* RES_TXTATR_FTN */                nullptr,
/* RES_TXTATR_ANNOTATION */         nullptr,
/* RES_TXTATR_LINEBREAK */          nullptr,
/* RES_TXTATR_DUMMY1 */             nullptr, // Dummy:
 
/* RES_PARATR_LINESPACING   */      OutCSS1_SvxLineSpacing,
/* RES_PARATR_ADJUST    */          OutCSS1_SvxAdjust,
/* RES_PARATR_SPLIT */              OutCSS1_SvxFormatSplit,
/* RES_PARATR_ORPHANS   */          OutCSS1_SvxOrphans,
/* RES_PARATR_WIDOWS    */          OutCSS1_SvxWidows,
/* RES_PARATR_TABSTOP   */          nullptr,
/* RES_PARATR_HYPHENZONE*/          nullptr,
/* RES_PARATR_DROP */               OutCSS1_SwFormatDrop,
/* RES_PARATR_REGISTER */           nullptr, // new: register-true
/* RES_PARATR_NUMRULE */            nullptr,
/* RES_PARATR_SCRIPTSPACE */        nullptr,
/* RES_PARATR_HANGINGPUNCTUATION */ nullptr,
/* RES_PARATR_FORBIDDEN_RULES */    nullptr, // new
/* RES_PARATR_VERTALIGN */          nullptr, // new
/* RES_PARATR_SNAPTOGRID*/          nullptr, // new
/* RES_PARATR_CONNECT_TO_BORDER */  nullptr, // new
/* RES_PARATR_OUTLINELEVEL */       nullptr, // new since cws outlinelevel
/* RES_PARATR_RSID */               nullptr, // new
/* RES_PARATR_GRABBAG */            nullptr,
 
/* RES_PARATR_LIST_ID */            nullptr, // new
/* RES_PARATR_LIST_LEVEL */         nullptr, // new
/* RES_PARATR_LIST_ISRESTART */     nullptr, // new
/* RES_PARATR_LIST_RESTARTVALUE */  nullptr, // new
/* RES_PARATR_LIST_ISCOUNTED */     nullptr, // new
/* RES_PARATR_LIST_AUTOFMT */       nullptr, // new
 
/* RES_FILL_ORDER   */              nullptr,
/* RES_FRM_SIZE */                  nullptr,
/* RES_PAPER_BIN    */              nullptr,
/* RES_MARGIN_FIRSTLINE */          OutCSS1_SvxFirstLineIndent,
/* RES_MARGIN_TEXTLEFT */           OutCSS1_SvxTextLeftMargin,
/* RES_MARGIN_RIGHT */              OutCSS1_SvxRightMargin,
/* RES_MARGIN_LEFT */               nullptr,
/* RES_MARGIN_GUTTER */             nullptr,
/* RES_MARGIN_GUTTER_RIGHT */       nullptr,
/* RES_LR_SPACE */                  OutCSS1_SvxLRSpace,
/* RES_UL_SPACE */                  OutCSS1_SvxULSpace,
/* RES_PAGEDESC */                  nullptr,
/* RES_BREAK */                     nullptr,
/* RES_CNTNT */                     nullptr,
/* RES_HEADER */                    nullptr,
/* RES_FOOTER */                    nullptr,
/* RES_PRINT */                     nullptr,
/* RES_OPAQUE */                    nullptr,
/* RES_PROTECT */                   nullptr,
/* RES_SURROUND */                  nullptr,
/* RES_VERT_ORIENT */               nullptr,
/* RES_HORI_ORIENT */               nullptr,
/* RES_ANCHOR */                    nullptr,
/* RES_BACKGROUND */                OutCSS1_SvxBrush,
/* RES_BOX  */                      OutCSS1_SvxBox,
/* RES_SHADOW */                    nullptr,
/* RES_FRMMACRO */                  nullptr,
/* RES_COL */                       nullptr,
/* RES_KEEP */                      nullptr,
/* RES_URL */                       nullptr,
/* RES_EDIT_IN_READONLY */          nullptr,
/* RES_LAYOUT_SPLIT */              nullptr,
/* RES_CHAIN */                     nullptr,
/* RES_TEXTGRID */                  nullptr,
/* RES_LINENUMBER */                nullptr,
/* RES_FTN_AT_TXTEND */             nullptr,
/* RES_END_AT_TXTEND */             nullptr,
/* RES_COLUMNBALANCE */             nullptr,
/* RES_FRAMEDIR */                  OutCSS1_SvxFrameDirection,
/* RES_HEADER_FOOTER_EAT_SPACING */ nullptr,
/* RES_ROW_SPLIT */                 nullptr,
/* RES_FLY_SPLIT */                 nullptr,
/* RES_FOLLOW_TEXT_FLOW */          nullptr,
/* RES_COLLAPSING_BORDERS */        nullptr,
/* RES_WRAP_INFLUENCE_ON_OBJPOS */  nullptr,
/* RES_AUTO_STYLE */                nullptr,
/* RES_FRMATR_STYLE_NAME */         nullptr,
/* RES_FRMATR_CONDITIONAL_STYLE_NAME */ nullptr,
/* RES_FRMATR_GRABBAG */            nullptr,
/* RES_TEXT_VERT_ADJUST */          nullptr,
/* RES_BACKGROUND_FULL_SIZE */      nullptr,
/* RES_RTL_GUTTER */                nullptr,
/* RES_DECORATIVE */                nullptr,
 
/* RES_GRFATR_MIRRORGRF */          nullptr,
/* RES_GRFATR_CROPGRF   */          nullptr,
/* RES_GRFATR_ROTATION */           nullptr,
/* RES_GRFATR_LUMINANCE */          nullptr,
/* RES_GRFATR_CONTRAST */           nullptr,
/* RES_GRFATR_CHANNELR */           nullptr,
/* RES_GRFATR_CHANNELG */           nullptr,
/* RES_GRFATR_CHANNELB */           nullptr,
/* RES_GRFATR_GAMMA */              nullptr,
/* RES_GRFATR_INVERT */             nullptr,
/* RES_GRFATR_TRANSPARENCY */       nullptr,
/* RES_GRFATR_DRWAMODE */           nullptr,
/* RES_GRFATR_DUMMY3 */             nullptr,
/* RES_GRFATR_DUMMY4 */             nullptr,
/* RES_GRFATR_DUMMY5 */             nullptr,
 
/* RES_BOXATR_FORMAT */             nullptr,
/* RES_BOXATR_FORMULA */            nullptr,
/* RES_BOXATR_VALUE */              nullptr
};
 
static_assert(SAL_N_ELEMENTS(aCSS1AttrFnTab) == RES_BOXATR_END);
 
void SwHTMLWriter::OutCSS1_SfxItemSet( const SfxItemSet& rItemSet,
                                       bool bDeep, std::string_view rAdd )
{
    // print ItemSet, including all attributes
    Out_SfxItemSet( aCSS1AttrFnTab, *this, rItemSet, bDeep );
 
    // some Attributes require special treatment
 
    // Underline, Overline, CrossedOut and Blink form together a CSS1-Property
    // (doesn't work of course for Hints)
    if( !IsCSS1Source(CSS1_OUTMODE_HINT) )
    {
        const SvxUnderlineItem *pUnderlineItem =
            rItemSet.GetItemIfSet( RES_CHRATR_UNDERLINE, bDeep );
 
        const SvxOverlineItem *pOverlineItem =
            rItemSet.GetItemIfSet( RES_CHRATR_OVERLINE, bDeep );
 
        const SvxCrossedOutItem *pCrossedOutItem =
            rItemSet.GetItemIfSet( RES_CHRATR_CROSSEDOUT, bDeep );
 
        const SvxBlinkItem *pBlinkItem =
            rItemSet.GetItemIfSet( RES_CHRATR_BLINK, bDeep );
 
        if( pUnderlineItem || pOverlineItem || pCrossedOutItem || pBlinkItem )
            OutCSS1_SvxTextLn_SvxCrOut_SvxBlink( *this, pUnderlineItem,
                                                 pOverlineItem,
                                                 pCrossedOutItem,
                                                 pBlinkItem );
 
        OutCSS1_SvxFormatBreak_SwFormatPDesc_SvxFormatKeep( *this, rItemSet, bDeep );
    }
 
    if (!rAdd.empty())
    {
        for (std::size_t index = 0; index != std::string_view::npos;)
        {
            std::string_view attr = o3tl::trim(o3tl::getToken(rAdd, ':', index));
            assert(!attr.empty());
            assert(index != std::string_view::npos);
 
            std::string_view val = o3tl::trim(o3tl::getToken(rAdd, ':', index));
            assert(!val.empty());
            OutCSS1_PropertyAscii(attr, val);
        }
    }
 
    if( m_bFirstCSS1Property )
        return;
 
    // if a Property was exported as part of a Style-Option,
    // the Option still needs to be finished
    OStringBuffer sOut;
    switch( m_nCSS1OutMode & CSS1_OUTMODE_ANY_OFF )
    {
    case CSS1_OUTMODE_SPAN_TAG_OFF:
        sOut.append(sCSS1_span_tag_end);
        break;
 
    case CSS1_OUTMODE_STYLE_OPT_OFF:
        sOut.append(cCSS1_style_opt_end);
        break;
 
    case CSS1_OUTMODE_RULE_OFF:
        sOut.append(sCSS1_rule_end);
        break;
    }
    if (!sOut.isEmpty())
        Strm().WriteOString( sOut );
}
 
SwHTMLWriter& OutCSS1_HintSpanTag( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_SPAN_TAG |
                                   CSS1_OUTMODE_ENCODE|CSS1_OUTMODE_HINT, nullptr );
 
    Out( aCSS1AttrFnTab, rHt, rWrt );
 
    if( !rWrt.m_bFirstCSS1Property  && rWrt.m_bTagOn )
        rWrt.Strm().WriteOString( sCSS1_span_tag_end );
 
    return rWrt;
}
 
SwHTMLWriter& OutCSS1_HintStyleOpt( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
{
    SwCSS1OutMode aMode( rWrt, CSS1_OUTMODE_STYLE_OPT_ON |
                                   CSS1_OUTMODE_ENCODE|
                                   CSS1_OUTMODE_HINT, nullptr );
 
    Out( aCSS1AttrFnTab, rHt, rWrt );
 
    if( !rWrt.m_bFirstCSS1Property )
        rWrt.Strm().WriteChar( '\"' );
 
    return rWrt;
}
 
/* 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 '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 '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 '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 '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.

V547 Expression 'FieldUnit::MM == eUnit' is always false.

V547 Expression 'FieldUnit::CM == eUnit' is always false.

V547 Expression 'FieldUnit::POINT == eUnit' is always false.