/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <hintids.hxx>
#include <svl/itemiter.hxx>
#include <svl/urihelper.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <sfx2/docfile.hxx>
#include <editeng/editids.hrc>
#include <editeng/fhgtitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <o3tl/string_view.hxx>
#include <svtools/htmltokn.h>
#include <svtools/htmlkywd.hxx>
#include <fmtpdsc.hxx>
#include <fmtanchr.hxx>
#include <fmtornt.hxx>
#include <fmtsrnd.hxx>
#include <fmtfsize.hxx>
#include <frmatr.hxx>
#include <charfmt.hxx>
#include <docary.hxx>
#include <osl/diagnose.h>

#include <doc.hxx>
#include <IDocumentStylePoolAccess.hxx>
#include <pam.hxx>
#include <poolfmt.hxx>
#include <docsh.hxx>
#include <paratr.hxx>
#include <pagedesc.hxx>
#include "css1kywd.hxx"
#include "swcss1.hxx"
#include "htmlnum.hxx"
#include "swhtml.hxx"
#include <numrule.hxx>
#include "css1atr.hxx"

using namespace ::com::sun::star;

// How many rows/characters are allowed for DropCaps?
// (Are there maybe somewhere else corresponding values?)
#define MAX_DROPCAP_LINES 9
#define MAX_DROPCAP_CHARS 9

static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc );

// Implementation of SwCSS1Parsers (actually swcss1.cxx)
const sal_uInt16 aItemIds[] =
{
    RES_BREAK,
    RES_PAGEDESC,
    RES_KEEP,
};

void SwCSS1Parser::ChgPageDesc( const SwPageDesc *pPageDesc,
                                const SwPageDesc& rNewPageDesc )
{
    size_t pos;
    bool found = m_pDoc->ContainsPageDesc( pPageDesc, &pos );
    OSL_ENSURE( found, "style not found" );
    if (found)
        m_pDoc->ChgPageDesc( pos, rNewPageDesc );
}

SwCSS1Parser::SwCSS1Parser(SwDoc *const pDoc, SwHTMLParser const& rParser,
        const sal_uInt32 aFHeights[7], const OUString& rBaseURL, bool const bNewDoc)
    : SvxCSS1Parser(pDoc->GetAttrPool(), rBaseURL,
                   aItemIds, SAL_N_ELEMENTS(aItemIds))
    , m_pDoc( pDoc )
    , m_rHTMLParser(rParser)
    , m_nDropCapCnt( 0 ),
    m_bIsNewDoc( bNewDoc ),
    m_bBodyBGColorSet( false ),
    m_bBodyBackgroundSet( false ),
    m_bBodyTextSet( false ),
    m_bBodyLinkSet( false ),
    m_bBodyVLinkSet( false ),
    m_bSetFirstPageDesc( false ),
    m_bSetRightPageDesc( false ),
    m_bTableHeaderTextCollSet( false ),
    m_bTableTextCollSet( false ),
    m_bLinkCharFormatsSet( false )
{
    m_aFontHeights[0] = aFHeights[0];
    m_aFontHeights[1] = aFHeights[1];
    m_aFontHeights[2] = aFHeights[2];
    m_aFontHeights[3] = aFHeights[3];
    m_aFontHeights[4] = aFHeights[4];
    m_aFontHeights[5] = aFHeights[5];
    m_aFontHeights[6] = aFHeights[6];
}

SwCSS1Parser::~SwCSS1Parser()
{
}

// Feature: PrintExt
bool SwCSS1Parser::SetFormatBreak( SfxItemSet& rItemSet,
                                const SvxCSS1PropertyInfo& rPropInfo )
{
    SvxBreak eBreak = SvxBreak::NONE;
    bool bKeep = false;
    bool bSetKeep = false, bSetBreak = false, bSetPageDesc = false;
    const SwPageDesc *pPageDesc = nullptr;
    switch( rPropInfo.m_ePageBreakBefore )
    {
    case SVX_CSS1_PBREAK_ALWAYS:
        eBreak = SvxBreak::PageBefore;
        bSetBreak = true;
        break;
    case SVX_CSS1_PBREAK_LEFT:
        pPageDesc = GetLeftPageDesc( true );
        bSetPageDesc = true;
        break;
    case SVX_CSS1_PBREAK_RIGHT:
        pPageDesc = GetRightPageDesc( true );
        bSetPageDesc = true;
        break;
    case SVX_CSS1_PBREAK_AUTO:
        bSetBreak = bSetPageDesc = true;
        break;
    default:
        ;
    }
    switch( rPropInfo.m_ePageBreakAfter )
    {
    case SVX_CSS1_PBREAK_ALWAYS:
    case SVX_CSS1_PBREAK_LEFT:
    case SVX_CSS1_PBREAK_RIGHT:
        // LEFT/RIGHT also could be set in the previous paragraph
        eBreak = SvxBreak::PageAfter;
        bSetBreak = true;
        break;
    case SVX_CSS1_PBREAK_AUTO:
        bSetBreak = bSetKeep = bSetPageDesc = true;
        break;
    case SVX_CSS1_PBREAK_AVOID:
        bKeep = bSetKeep = true;
        break;
    default:
        ;
    }

    if( bSetBreak )
        rItemSet.Put( SvxFormatBreakItem( eBreak, RES_BREAK ) );
    if( bSetPageDesc )
        rItemSet.Put( SwFormatPageDesc( pPageDesc ) );
    if( bSetKeep )
        rItemSet.Put( SvxFormatKeepItem( bKeep, RES_KEEP ) );

    return bSetBreak;
}

static void SetCharFormatAttrs( SwCharFormat *pCharFormat, SfxItemSet& rItemSet )
{
    static const TypedWhichId<SvxFontHeightItem> aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE,
                                   RES_CHRATR_CTL_FONTSIZE };
    for(auto const & i : aWhichIds)
    {
        const SvxFontHeightItem* pItem = rItemSet.GetItemIfSet( i, false );
        if( pItem && pItem->GetProp() != 100)
        {
            // percentage values at the FontHeight item aren't supported
            rItemSet.ClearItem( i );
        }
    }

    pCharFormat->SetFormatAttr( rItemSet );

    if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
    {
        // A Brush-Item with RES_BACKGROUND must be converted to one
        // with RES_CHRATR_BACKGROUND

        SvxBrushItem aBrushItem( *pItem );
        aBrushItem.SetWhich( RES_CHRATR_BACKGROUND );
        pCharFormat->SetFormatAttr( aBrushItem );
    }

    if( const SvxBoxItem* pItem = rItemSet.GetItemIfSet( RES_BOX, false ) )
    {
        SvxBoxItem aBoxItem( *pItem );
        aBoxItem.SetWhich( RES_CHRATR_BOX );
        pCharFormat->SetFormatAttr( aBoxItem );
    }
}

void SwCSS1Parser::SetLinkCharFormats()
{
    OSL_ENSURE( !m_bLinkCharFormatsSet, "Call SetLinkCharFormats unnecessary" );

    SvxCSS1MapEntry *pStyleEntry =
        GetTag( u"" OOO_STRING_SVTOOLS_HTML_anchor ""_ustr );
    SwCharFormat *pUnvisited = nullptr, *pVisited = nullptr;
    if( pStyleEntry )
    {
        SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
        bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
                                                              false));
        pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL );
        SetCharFormatAttrs( pUnvisited, rItemSet );
        m_bBodyLinkSet |= bColorSet;

        pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT );
        SetCharFormatAttrs( pVisited, rItemSet );
        m_bBodyVLinkSet |= bColorSet;
    }

    OUString sTmp = u"" OOO_STRING_SVTOOLS_HTML_anchor ":link"_ustr;

    pStyleEntry = GetTag( sTmp );
    if( pStyleEntry )
    {
        SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
        bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
                                                              false));
        if( !pUnvisited )
            pUnvisited = GetCharFormatFromPool( RES_POOLCHR_INET_NORMAL );
        SetCharFormatAttrs( pUnvisited, rItemSet );
        m_bBodyLinkSet |= bColorSet;
    }

    sTmp = OOO_STRING_SVTOOLS_HTML_anchor ":visited";

    pStyleEntry = GetTag( sTmp );
    if( pStyleEntry )
    {
        SfxItemSet& rItemSet = pStyleEntry->GetItemSet();
        bool bColorSet = (SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,
                                                              false));
        if( !pVisited )
            pVisited = GetCharFormatFromPool( RES_POOLCHR_INET_VISIT );
        SetCharFormatAttrs( pVisited, rItemSet );
        m_bBodyVLinkSet |= bColorSet;
    }

    m_bLinkCharFormatsSet = true;
}

static void SetTextCollAttrs( SwTextFormatColl *pColl, SfxItemSet& rItemSet,
                             SvxCSS1PropertyInfo const & rPropInfo,
                             SwCSS1Parser *pCSS1Parser )
{
    const SfxItemSet& rCollItemSet = pColl->GetAttrSet();

    // note: there was some SvxLRSpaceItem code here that was nonobvious
    // but it looks like the only cases in which it would be required
    // with split items are if some nProp != 100 or if SetAutoFirst() had
    // been called (on the pColl items) but it looks like none of these are
    // possible in HTML import.

    // top and bottom border
    const SvxULSpaceItem* pCollULItem;
    const SvxULSpaceItem* pULItem;
    if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) &&
        (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) &&
        (pCollULItem = rCollItemSet.GetItemIfSet(RES_UL_SPACE)) &&
        (pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE,false)) )
    {
        SvxULSpaceItem aULItem( *pCollULItem );
        if( rPropInfo.m_bTopMargin )
            aULItem.SetUpper( pULItem->GetUpper() );
        if( rPropInfo.m_bBottomMargin )
            aULItem.SetLower( pULItem->GetLower() );

        rItemSet.Put( aULItem );
    }

    static const TypedWhichId<SvxFontHeightItem> aWhichIds[3] = { RES_CHRATR_FONTSIZE,RES_CHRATR_CJK_FONTSIZE,
                                   RES_CHRATR_CTL_FONTSIZE };
    for(auto const & i : aWhichIds)
    {
        const SvxFontHeightItem* pItem = rItemSet.GetItemIfSet( i, false );
        if( pItem && pItem->GetProp() != 100)
        {
            // percentage values at the FontHeight item aren't supported
            rItemSet.ClearItem( i );
        }
    }

    pCSS1Parser->SetFormatBreak( rItemSet, rPropInfo );

    pColl->SetFormatAttr( rItemSet );
}

void SwCSS1Parser::SetTableTextColl( bool bHeader )
{
    OSL_ENSURE( !(bHeader ? m_bTableHeaderTextCollSet : m_bTableTextCollSet),
            "Call SetTableTextColl unnecessary" );

    sal_uInt16 nPoolId;
    OUString sTag;
    if( bHeader )
    {
        nPoolId = RES_POOLCOLL_TABLE_HDLN;
        sTag = OOO_STRING_SVTOOLS_HTML_tableheader;
    }
    else
    {
        nPoolId = RES_POOLCOLL_TABLE;
        sTag = OOO_STRING_SVTOOLS_HTML_tabledata;
    }

    SwTextFormatColl *pColl = nullptr;

    // The following entries will never be used again and may be changed.
    SvxCSS1MapEntry *pStyleEntry = GetTag( sTag );
    if( pStyleEntry )
    {
        pColl = GetTextFormatColl(nPoolId, OUString());
        SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(),
                         pStyleEntry->GetPropertyInfo(), this );
    }

    OUString sTmp = sTag + " " OOO_STRING_SVTOOLS_HTML_parabreak;
    pStyleEntry = GetTag( sTmp );
    if( pStyleEntry )
    {
        if( !pColl )
            pColl = GetTextFormatColl(nPoolId, OUString());
        SetTextCollAttrs( pColl, pStyleEntry->GetItemSet(),
                         pStyleEntry->GetPropertyInfo(), this );
    }

    if( bHeader )
        m_bTableHeaderTextCollSet = true;
    else
        m_bTableTextCollSet = true;
}

void SwCSS1Parser::SetPageDescAttrs( const SvxBrushItem *pBrush,
                                     SfxItemSet *pItemSet2 )
{
    std::shared_ptr<SvxBrushItem> aBrushItem(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
    std::shared_ptr<SvxBoxItem> aBoxItem(std::make_shared<SvxBoxItem>(RES_BOX));
    std::shared_ptr<SvxFrameDirectionItem> aFrameDirItem(std::make_shared<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR));
    bool bSetBrush = pBrush!=nullptr, bSetBox = false, bSetFrameDir = false;
    if( pBrush )
        aBrushItem.reset(pBrush->Clone());

    if( pItemSet2 )
    {
        if( const SvxBrushItem* pItem = pItemSet2->GetItemIfSet( RES_BACKGROUND, false ) )
        {
            // set a background
            aBrushItem.reset(pItem->Clone());
            pItemSet2->ClearItem( RES_BACKGROUND );
            bSetBrush = true;
        }

        if( const SvxBoxItem* pItem = pItemSet2->GetItemIfSet( RES_BOX, false ) )
        {
            // set a border
            aBoxItem.reset(pItem->Clone());
            pItemSet2->ClearItem( RES_BOX );
            bSetBox = true;
        }

        if( const SvxFrameDirectionItem* pItem = pItemSet2->GetItemIfSet( RES_FRAMEDIR, false ) )
        {
            // set a frame
            aFrameDirItem.reset(pItem->Clone());
            pItemSet2->ClearItem( RES_FRAMEDIR );
            bSetFrameDir = true;
        }
    }

    if( !(bSetBrush || bSetBox || bSetFrameDir) )
        return;

    static const sal_uInt16 aPoolIds[] = { RES_POOLPAGE_HTML, RES_POOLPAGE_FIRST,
                                           RES_POOLPAGE_LEFT, RES_POOLPAGE_RIGHT };
    for(sal_uInt16 i : aPoolIds)
    {
        const SwPageDesc *pPageDesc = GetPageDesc( i, false );
        if( pPageDesc )
        {
            SwPageDesc aNewPageDesc( *pPageDesc );
            SwFrameFormat &rMaster = aNewPageDesc.GetMaster();
            if( bSetBrush )
                rMaster.SetFormatAttr( *aBrushItem );
            if( bSetBox )
                rMaster.SetFormatAttr( *aBoxItem );
            if( bSetFrameDir )
                rMaster.SetFormatAttr( *aFrameDirItem );

            ChgPageDesc( pPageDesc, aNewPageDesc );
        }
    }
}

// Feature: PrintExt
void SwCSS1Parser::SetPageDescAttrs( const SwPageDesc *pPageDesc,
                                     SfxItemSet& rItemSet,
                                     const SvxCSS1PropertyInfo& rPropInfo )
{
    if( !pPageDesc )
        return;

    SwPageDesc aNewPageDesc( *pPageDesc );
    SwFrameFormat &rMaster = aNewPageDesc.GetMaster();
    const SfxItemSet& rPageItemSet = rMaster.GetAttrSet();
    bool bChanged = false;

    // left, right border and first line indentation
    ::std::optional<SvxLRSpaceItem> oLRSpace;
    assert(!rItemSet.GetItemIfSet(RES_LR_SPACE,false));
    if (rPropInfo.m_bLeftMargin)
    {
        // note: parser never creates SvxLeftMarginItem! must be converted
        if (SvxTextLeftMarginItem const*const pLeft = rItemSet.GetItemIfSet(RES_MARGIN_TEXTLEFT, false))
        {
            if (!oLRSpace)
            {
                if (const SvxLRSpaceItem* pPageItem = rPageItemSet.GetItemIfSet(RES_LR_SPACE))
                {
                    oLRSpace.emplace(*pPageItem);
                }
                else
                {
                    oLRSpace.emplace(RES_LR_SPACE);
                }
            }
            oLRSpace->SetLeft(pLeft->GetTextLeft());
        }
    }
    if (rPropInfo.m_bRightMargin)
    {
        // note: parser never creates SvxLeftMarginItem! must be converted
        if (SvxRightMarginItem const*const pRight = rItemSet.GetItemIfSet(RES_MARGIN_RIGHT, false))
        {
            if (!oLRSpace)
            {
                if (const SvxLRSpaceItem* pPageItem = rPageItemSet.GetItemIfSet(RES_LR_SPACE))
                {
                    oLRSpace.emplace(*pPageItem);
                }
                else
                {
                    oLRSpace.emplace(RES_LR_SPACE);
                }
            }
            oLRSpace->SetRight(pRight->GetRight());
        }
    }
    if (oLRSpace)
    {
        rMaster.SetFormatAttr(*oLRSpace);
        bChanged = true;
    }

    // top and bottom border
    const SvxULSpaceItem *pULItem;
    if( (rPropInfo.m_bTopMargin || rPropInfo.m_bBottomMargin) &&
        (pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE,false)) )
    {
        const SvxULSpaceItem* pPageItem;
        if( (!rPropInfo.m_bTopMargin || !rPropInfo.m_bBottomMargin) &&
            (pPageItem = rPageItemSet.GetItemIfSet(RES_UL_SPACE) ) )
        {
            SvxULSpaceItem aULItem( *pPageItem );
            if( rPropInfo.m_bTopMargin )
                aULItem.SetUpper( pULItem->GetUpper() );
            if( rPropInfo.m_bBottomMargin )
                aULItem.SetLower( pULItem->GetLower() );

            rMaster.SetFormatAttr( aULItem );
        }
        else
        {
            rMaster.SetFormatAttr( *pULItem );
        }
        bChanged = true;
    }

    // the size
    if( rPropInfo.m_eSizeType != SVX_CSS1_STYPE_NONE )
    {
        if( rPropInfo.m_eSizeType == SVX_CSS1_STYPE_TWIP )
        {
            rMaster.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed, rPropInfo.m_nWidth,
                                           rPropInfo.m_nHeight ) );
            bChanged = true;
        }
        else
        {
            // With "size: auto|portrait|landscape" the current size
            // of the style remains. If "landscape" and "portrait" then
            // the landscape flag will be set and maybe the width/height
            // are swapped.
            SwFormatFrameSize aFrameSz( rMaster.GetFrameSize() );
            bool bLandscape = aNewPageDesc.GetLandscape();
            if( ( bLandscape &&
                  rPropInfo.m_eSizeType == SVX_CSS1_STYPE_PORTRAIT ) ||
                ( !bLandscape &&
                  rPropInfo.m_eSizeType == SVX_CSS1_STYPE_LANDSCAPE ) )
            {
                SwTwips nTmp = aFrameSz.GetHeight();
                aFrameSz.SetHeight( aFrameSz.GetWidth() );
                aFrameSz.SetWidth( nTmp );
                rMaster.SetFormatAttr( aFrameSz );
                aNewPageDesc.SetLandscape( !bLandscape );
                bChanged = true;
            }
        }
    }

    // Is that possible?
    if( const SvxBrushItem* pItem = rItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
    {
        // set a background
        rMaster.SetFormatAttr( *pItem );
        rItemSet.ClearItem( RES_BACKGROUND );
        bChanged = true;
    }

    if( bChanged )
        ChgPageDesc( pPageDesc, aNewPageDesc );
}

std::unique_ptr<SvxBrushItem> SwCSS1Parser::makePageDescBackground() const
{
    return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false )
        ->GetMaster().makeBackgroundBrushItem();
}

Css1ScriptFlags SwCSS1Parser::GetScriptFromClass( OUString& rClass,
                                             bool bSubClassOnly )
{
    Css1ScriptFlags nScriptFlags = Css1ScriptFlags::AllMask;
    sal_Int32 nLen = rClass.getLength();
    sal_Int32 nPos = nLen > 4 ? rClass.lastIndexOf( '-' ) : -1;

    if( nPos == -1 )
    {
        if( bSubClassOnly )
            return nScriptFlags;
        nPos = 0;
    }
    else
    {
        nPos++;
        nLen = nLen - nPos;
    }

    switch( nLen )
    {
    case 3:
        if( rClass.matchIgnoreAsciiCase( "cjk", nPos ) )
        {
            nScriptFlags = Css1ScriptFlags::CJK;
        }
        else if( rClass.matchIgnoreAsciiCase( "ctl", nPos ) )
        {
            nScriptFlags = Css1ScriptFlags::CTL;
        }
        break;
    case 7:
        if( rClass.matchIgnoreAsciiCase( "western", nPos ) )
        {
            nScriptFlags = Css1ScriptFlags::Western;
        }
        break;
    }
    if( Css1ScriptFlags::AllMask != nScriptFlags )
    {
        if( nPos )
        {
            rClass = rClass.copy( 0, nPos-1 );
        }
        else
        {
            rClass.clear();
        }
    }

    return nScriptFlags;
}

static CSS1SelectorType GetTokenAndClass( const CSS1Selector *pSelector,
                              OUString& rToken, OUString& rClass,
                              Css1ScriptFlags& rScriptFlags )
{
    rToken = pSelector->GetString();
    rClass.clear();
    rScriptFlags = Css1ScriptFlags::AllMask;

    CSS1SelectorType eType = pSelector->GetType();
    if( CSS1_SELTYPE_ELEM_CLASS==eType  )
    {
        sal_Int32 nPos = rToken.indexOf( '.' );
        OSL_ENSURE( nPos >= 0, "No dot in Class-Selector???" );
        if( nPos >= 0 )
        {
            rClass = rToken.copy( nPos+1 );
            rToken = rToken.copy( 0, nPos );

            rScriptFlags = SwCSS1Parser::GetScriptFromClass( rClass, false );
            if( rClass.isEmpty() )
                eType = CSS1_SELTYPE_ELEMENT;
        }
    }

    rToken = rToken.toAsciiLowerCase();
    return eType;
}

static void RemoveScriptItems( SfxItemSet& rItemSet, Css1ScriptFlags nScript,
                               const SfxItemSet *pParentItemSet = nullptr )
{
    static const sal_uInt16 aWhichIds[3][5] =
    {
        { RES_CHRATR_FONT, RES_CHRATR_FONTSIZE, RES_CHRATR_LANGUAGE,
            RES_CHRATR_POSTURE, RES_CHRATR_WEIGHT },
        { RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CJK_LANGUAGE,
            RES_CHRATR_CJK_POSTURE, RES_CHRATR_CJK_WEIGHT },
        { RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, RES_CHRATR_CTL_LANGUAGE,
            RES_CHRATR_CTL_POSTURE, RES_CHRATR_CTL_WEIGHT }
    };

    bool aClearItems[3] = { false, false, false };
    switch( nScript )
    {
    case Css1ScriptFlags::Western:
        aClearItems[1] = aClearItems[2] =  true;
        break;
    case Css1ScriptFlags::CJK:
        aClearItems[0] = aClearItems[2] =  true;
        break;
    case Css1ScriptFlags::CTL:
        aClearItems[0] = aClearItems[1] =  true;
        break;
    case Css1ScriptFlags::AllMask:
        break;
    default:
        OSL_ENSURE( aClearItems[0], "unknown script type" );
        break;
       }

    for( size_t j=0; j < SAL_N_ELEMENTS(aWhichIds); ++j )
    {
        for( size_t i=0; i < SAL_N_ELEMENTS(aWhichIds[0]); ++i )
        {
            sal_uInt16 nWhich = aWhichIds[j][i];
            const SfxPoolItem *pItem;
            if( aClearItems[j] ||
                (pParentItemSet &&
                 SfxItemState::SET == rItemSet.GetItemState( nWhich, false, &pItem ) &&
                 (0==i ? swhtml_css1atr_equalFontItems( *pItem, pParentItemSet->Get(nWhich ) )
                        : *pItem == pParentItemSet->Get(nWhich ) ) ) )
            {
                rItemSet.ClearItem( nWhich );
            }
        }
    }
}

void SwCSS1Parser::StyleParsed( const CSS1Selector *pSelector,
                                SfxItemSet& rItemSet,
                                SvxCSS1PropertyInfo& rPropInfo )
{
    if( !m_bIsNewDoc )
        return;

    CSS1SelectorType eSelType = pSelector->GetType();
    const CSS1Selector *pNext = pSelector->GetNext();

    if( CSS1_SELTYPE_ID==eSelType && !pNext )
    {
        InsertId( pSelector->GetString(), rItemSet, rPropInfo );
    }
    else if( CSS1_SELTYPE_CLASS==eSelType && !pNext )
    {
        OUString aClass( pSelector->GetString() );
        Css1ScriptFlags nScript = GetScriptFromClass( aClass );
        if( Css1ScriptFlags::AllMask != nScript )
        {
            SfxItemSet aScriptItemSet( rItemSet );
            RemoveScriptItems( aScriptItemSet, nScript );
            InsertClass( aClass, aScriptItemSet, rPropInfo );
        }
        else
        {
            InsertClass( aClass, rItemSet, rPropInfo );
        }
    }
    else if( CSS1_SELTYPE_PAGE==eSelType )
    {
        if( !pNext ||
            (CSS1_SELTYPE_PSEUDO == pNext->GetType() &&
            (pNext->GetString().equalsIgnoreAsciiCase( "left" ) ||
             pNext->GetString().equalsIgnoreAsciiCase( "right" ) ||
             pNext->GetString().equalsIgnoreAsciiCase( "first" ) ) ) )
        {
            OUString aName;
            if( pNext )
                aName = pNext->GetString();
            InsertPage( aName,
                        pNext != nullptr,
                        rItemSet, rPropInfo );
        }
    }

    if( CSS1_SELTYPE_ELEMENT != eSelType &&
        CSS1_SELTYPE_ELEM_CLASS != eSelType)
        return;

    // get token and class of selector
    OUString aToken2;
    OUString aClass;
    Css1ScriptFlags nScript;
    eSelType = GetTokenAndClass( pSelector, aToken2, aClass, nScript );
    HtmlTokenId nToken2 = GetHTMLToken( aToken2 );

    // and also some information of the next element
    CSS1SelectorType eNextType = pNext ? pNext->GetType()
                                       : CSS1_SELTYPE_ELEMENT;

    // first some special cases
    if( CSS1_SELTYPE_ELEMENT==eSelType )
    {
        switch( nToken2 )
        {
        case HtmlTokenId::ANCHOR_ON:
            if( !pNext )
            {
                InsertTag( aToken2, rItemSet, rPropInfo );
                return;
            }
            else if (CSS1_SELTYPE_PSEUDO == eNextType)
            {
                // maybe A:visited or A:link

                OUString aPseudo( pNext->GetString() );
                aPseudo = aPseudo.toAsciiLowerCase();
                bool bInsert = false;
                switch( aPseudo[0] )
                {
                    case 'l':
                        if( aPseudo == "link" )
                        {
                            bInsert = true;
                        }
                        break;
                    case 'v':
                        if( aPseudo == "visited" )
                        {
                            bInsert = true;
                        }
                        break;
                }
                if( bInsert )
                {
                    OUString sTmp = aToken2 + ":" + aPseudo;
                    if( Css1ScriptFlags::AllMask != nScript )
                    {
                        SfxItemSet aScriptItemSet( rItemSet );
                        RemoveScriptItems( aScriptItemSet, nScript );
                        InsertTag( sTmp, aScriptItemSet, rPropInfo );
                    }
                    else
                    {
                        InsertTag( sTmp, rItemSet, rPropInfo );
                    }
                    return;
                }
            }
            break;
        case HtmlTokenId::BODY_ON:
            if( !pNext )
            {
                // BODY

                // We must test the background before setting, because
                // in SetPageDescAttrs it will be deleted.
                if( const SvxBrushItem *pBrushItem = rItemSet.GetItemIfSet(RES_BACKGROUND,false) )
                {
                    /// Body has a background color, if it is not "no fill"/"auto fill"
                    if( pBrushItem->GetColor() != COL_TRANSPARENT )
                        m_bBodyBGColorSet = true;
                    if( GPOS_NONE != pBrushItem->GetGraphicPos() )
                        m_bBodyBackgroundSet = true;
                }

                // Border and Padding
                rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST );

                // Some attributes must be set at the style, the ones which
                // aren't inherited
                SetPageDescAttrs( nullptr, &rItemSet );

                // all remaining options can be set at the default style,
                // then they're the default
                if( SfxItemState::SET==rItemSet.GetItemState(RES_CHRATR_COLOR,false) )
                    m_bBodyTextSet = true;
                SetTextCollAttrs(
                    GetTextCollFromPool( RES_POOLCOLL_STANDARD ),
                    rItemSet, rPropInfo, this );

                return;
            }
            break;
        default: break;
        }
    }
    else if( CSS1_SELTYPE_ELEM_CLASS==eSelType &&  HtmlTokenId::ANCHOR_ON==nToken2 &&
             !pNext && aClass.getLength() >= 9 &&
             ('s' == aClass[0] || 'S' == aClass[0]) )
    {
        sal_uInt16 nPoolFormatId = 0;
        if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote_sym) )
            nPoolFormatId = RES_POOLCHR_ENDNOTE;
        else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote_sym) )
            nPoolFormatId = RES_POOLCHR_FOOTNOTE;
        if( nPoolFormatId )
        {
            if( Css1ScriptFlags::AllMask == nScript )
            {
                SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId), rItemSet );
            }
            else
            {
                SfxItemSet aScriptItemSet( rItemSet );
                RemoveScriptItems( aScriptItemSet, nScript );
                SetCharFormatAttrs( GetCharFormatFromPool(nPoolFormatId),
                                 aScriptItemSet);
            }
            return;
        }
    }

    // Now the selectors are processed which belong to a paragraph style
    sal_uInt16 nPoolCollId = 0;
    switch( nToken2 )
    {
    case HtmlTokenId::HEAD1_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE1;
        break;
    case HtmlTokenId::HEAD2_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE2;
        break;
    case HtmlTokenId::HEAD3_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE3;
        break;
    case HtmlTokenId::HEAD4_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE4;
        break;
    case HtmlTokenId::HEAD5_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE5;
        break;
    case HtmlTokenId::HEAD6_ON:
        nPoolCollId = RES_POOLCOLL_HEADLINE6;
        break;
    case HtmlTokenId::PARABREAK_ON:
        if( aClass.getLength() >= 9 &&
            ('s' == aClass[0] || 'S' == aClass[0]) )
        {
            if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) )
                nPoolCollId = RES_POOLCOLL_ENDNOTE;
            else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) )
                nPoolCollId = RES_POOLCOLL_FOOTNOTE;

            if( nPoolCollId )
                aClass.clear();
            else
                nPoolCollId = RES_POOLCOLL_TEXT;
        }
        else
        {
            nPoolCollId = RES_POOLCOLL_TEXT;
        }
        break;
    case HtmlTokenId::ADDRESS_ON:
        nPoolCollId = RES_POOLCOLL_SEND_ADDRESS;
        break;
    case HtmlTokenId::BLOCKQUOTE_ON:
        nPoolCollId = RES_POOLCOLL_HTML_BLOCKQUOTE;
        break;
    case HtmlTokenId::DT_ON:
        nPoolCollId = RES_POOLCOLL_HTML_DT;
        break;
    case HtmlTokenId::DD_ON:
        nPoolCollId = RES_POOLCOLL_HTML_DD;
        break;
    case HtmlTokenId::PREFORMTXT_ON:
        nPoolCollId = RES_POOLCOLL_HTML_PRE;
        break;
    case HtmlTokenId::TABLEHEADER_ON:
    case HtmlTokenId::TABLEDATA_ON:
        if( CSS1_SELTYPE_ELEMENT==eSelType && !pNext )
        {
            InsertTag( aToken2, rItemSet, rPropInfo );
            return;
        }
        else if( CSS1_SELTYPE_ELEMENT==eSelType && pNext &&
                 (CSS1_SELTYPE_ELEMENT==eNextType ||
                  CSS1_SELTYPE_ELEM_CLASS==eNextType) )
        {
            // not TH and TD, but TH P and TD P
            OUString aSubToken, aSubClass;
            GetTokenAndClass( pNext, aSubToken, aSubClass, nScript );
            if( HtmlTokenId::PARABREAK_ON == GetHTMLToken( aSubToken ) )
            {
                aClass = aSubClass;
                pNext = pNext->GetNext();
                eNextType = pNext ? pNext->GetType() : CSS1_SELTYPE_ELEMENT;

                if( !aClass.isEmpty() || pNext )
                {
                    nPoolCollId = static_cast< sal_uInt16 >(
                        HtmlTokenId::TABLEHEADER_ON == nToken2 ? RES_POOLCOLL_TABLE_HDLN
                                                      : RES_POOLCOLL_TABLE );
                }
                else
                {
                    OUString sTmp = aToken2 + " " OOO_STRING_SVTOOLS_HTML_parabreak;

                    if( Css1ScriptFlags::AllMask == nScript )
                    {
                        InsertTag( sTmp, rItemSet, rPropInfo );
                    }
                    else
                    {
                        SfxItemSet aScriptItemSet( rItemSet );
                        RemoveScriptItems( aScriptItemSet, nScript );
                        InsertTag( sTmp, aScriptItemSet, rPropInfo );
                    }

                    return;
                }
            }
        }
        break;

    default:
        ;
    }

    if( nPoolCollId )
    {
        if( !pNext ||
            (CSS1_SELTYPE_PSEUDO==eNextType &&
             pNext->GetString().equalsIgnoreAsciiCase( "first-letter" ) &&
             SvxAdjust::Left == rPropInfo.m_eFloat) )
        {
            // either not a composed selector or a X:first-line { float: left; ... }

            // search resp. create the style
            SwTextFormatColl* pColl = GetTextFormatColl(nPoolCollId, OUString());
            SwTextFormatColl* pParentColl = nullptr;
            if( !aClass.isEmpty() )
            {
                UIName aName( pColl->GetName() );
                AddClassName( aName, aClass );

                pParentColl = pColl;
                pColl = m_pDoc->FindTextFormatCollByName( aName );
                if( !pColl )
                    pColl = m_pDoc->MakeTextFormatColl( aName, pParentColl );
            }
            if( !pNext )
            {
                // set only the attributes at the style
                const SvxBoxItem *pBoxItem =
                        pColl->GetAttrSet().GetItemIfSet(RES_BOX);
                rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST, pBoxItem );
                if( Css1ScriptFlags::AllMask == nScript && !pParentColl )
                {
                    SetTextCollAttrs( pColl, rItemSet, rPropInfo, this );
                }
                else
                {
                    SfxItemSet aScriptItemSet( rItemSet );
                    RemoveScriptItems( aScriptItemSet, nScript,
                                       pParentColl ? &pParentColl->GetAttrSet() : nullptr );
                    SetTextCollAttrs( pColl, aScriptItemSet, rPropInfo, this );
                }
            }
            else
            {
                // create a DropCap attribute
                SwFormatDrop aDrop( pColl->GetDrop() );
                aDrop.SetChars( 1 );

                // set the attributes of the DropCap attribute
                if( Css1ScriptFlags::AllMask == nScript )
                {
                    UIName sName(pColl->GetName());
                    FillDropCap( aDrop, rItemSet, &sName.toString() );
                }
                else
                {
                    SfxItemSet aScriptItemSet( rItemSet );
                    if( Css1ScriptFlags::Western != nScript )
                    {
                        aScriptItemSet.ClearItem( RES_CHRATR_FONT );
                        aScriptItemSet.ClearItem( RES_CHRATR_LANGUAGE );
                        aScriptItemSet.ClearItem( RES_CHRATR_POSTURE );
                        aScriptItemSet.ClearItem( RES_CHRATR_WEIGHT );
                    }
                    if( Css1ScriptFlags::CJK != nScript )
                    {
                        aScriptItemSet.ClearItem( RES_CHRATR_CJK_FONT );
                        aScriptItemSet.ClearItem( RES_CHRATR_CJK_LANGUAGE );
                        aScriptItemSet.ClearItem( RES_CHRATR_CJK_POSTURE );
                        aScriptItemSet.ClearItem( RES_CHRATR_CJK_WEIGHT );
                    }
                    if( Css1ScriptFlags::CTL != nScript )
                    {
                        aScriptItemSet.ClearItem( RES_CHRATR_CTL_FONT );
                        aScriptItemSet.ClearItem( RES_CHRATR_CTL_LANGUAGE );
                        aScriptItemSet.ClearItem( RES_CHRATR_CTL_POSTURE );
                        aScriptItemSet.ClearItem( RES_CHRATR_CTL_WEIGHT );
                    }
                    UIName sName(pColl->GetName());
                    FillDropCap( aDrop, aScriptItemSet, &sName.toString() );
                }

                // Only set the attribute if "float: left" is specified and
                // the Initial is over several lines. Otherwise the maybe
                // created character style will be later searched and set
                // via name.
                if( aDrop.GetLines() > 1 &&
                    (SvxAdjust::Left == rPropInfo.m_eFloat  ||
                     Css1ScriptFlags::AllMask == nScript) )
                {
                    pColl->SetFormatAttr( aDrop );
                }
            }
        }

        return;
    }

    // Now the selectors are processed which are belonging to the character
    // template. There are no composed ones here.
    if( pNext )
        return;

    SwCharFormat* pCFormat = GetChrFormat(nToken2, OUString());
    if( !pCFormat )
        return;

    SwCharFormat *pParentCFormat = nullptr;
    if( !aClass.isEmpty() )
    {
        UIName aName( pCFormat->GetName() );
        AddClassName( aName, aClass );
        pParentCFormat = pCFormat;

        pCFormat = m_pDoc->FindCharFormatByName( aName );
        if( !pCFormat )
        {
            pCFormat = m_pDoc->MakeCharFormat( aName, pParentCFormat );
            pCFormat->SetAuto(false);
        }
    }

    if( Css1ScriptFlags::AllMask == nScript && !pParentCFormat )
    {
        SetCharFormatAttrs( pCFormat, rItemSet );
    }
    else
    {
        SfxItemSet aScriptItemSet( rItemSet );
        RemoveScriptItems( aScriptItemSet, nScript,
                           pParentCFormat ? &pParentCFormat->GetAttrSet() : nullptr );
        SetCharFormatAttrs( pCFormat, aScriptItemSet );
    }
}

sal_uInt32 SwCSS1Parser::GetFontHeight( sal_uInt16 nSize ) const
{
    return m_aFontHeights[ std::min<sal_uInt16>(nSize,6) ];
}

const FontList *SwCSS1Parser::GetFontList() const
{
    const FontList *pFList = nullptr;
    SwDocShell *pDocSh = m_pDoc->GetDocShell();
    if( pDocSh )
    {
        const SvxFontListItem *pFListItem =
            static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
        if( pFListItem )
            pFList = pFListItem->GetFontList();
    }

    return pFList;
}

SwCharFormat* SwCSS1Parser::GetChrFormat( HtmlTokenId nToken2, const OUString& rClass ) const
{
    // search the corresponding style
    sal_uInt16 nPoolId = 0;
    const char* sName = nullptr;
    switch( nToken2 )
    {
    case HtmlTokenId::EMPHASIS_ON:      nPoolId = RES_POOLCHR_HTML_EMPHASIS;    break;
    case HtmlTokenId::CITATION_ON:      nPoolId = RES_POOLCHR_HTML_CITATION;   break;
    case HtmlTokenId::STRONG_ON:        nPoolId = RES_POOLCHR_HTML_STRONG;      break;
    case HtmlTokenId::CODE_ON:          nPoolId = RES_POOLCHR_HTML_CODE;        break;
    case HtmlTokenId::SAMPLE_ON:        nPoolId = RES_POOLCHR_HTML_SAMPLE;      break;
    case HtmlTokenId::KEYBOARD_ON:      nPoolId = RES_POOLCHR_HTML_KEYBOARD;    break;
    case HtmlTokenId::VARIABLE_ON:      nPoolId = RES_POOLCHR_HTML_VARIABLE;    break;
    case HtmlTokenId::DEFINSTANCE_ON:   nPoolId = RES_POOLCHR_HTML_DEFINSTANCE; break;
    case HtmlTokenId::TELETYPE_ON:      nPoolId = RES_POOLCHR_HTML_TELETYPE;    break;

    case HtmlTokenId::SHORTQUOTE_ON:    sName = OOO_STRING_SVTOOLS_HTML_shortquote;     break;
    case HtmlTokenId::LANGUAGE_ON:      sName = OOO_STRING_SVTOOLS_HTML_language;   break;
    case HtmlTokenId::AUTHOR_ON:        sName = OOO_STRING_SVTOOLS_HTML_author;         break;
    case HtmlTokenId::PERSON_ON:        sName = OOO_STRING_SVTOOLS_HTML_person;         break;
    case HtmlTokenId::ACRONYM_ON:       sName = OOO_STRING_SVTOOLS_HTML_acronym;        break;
    case HtmlTokenId::ABBREVIATION_ON:  sName = OOO_STRING_SVTOOLS_HTML_abbreviation;   break;
    case HtmlTokenId::INSERTEDTEXT_ON:  sName = OOO_STRING_SVTOOLS_HTML_insertedtext;   break;
    case HtmlTokenId::DELETEDTEXT_ON:   sName = OOO_STRING_SVTOOLS_HTML_deletedtext;    break;
    default: break;
    }

    // search or create the style (only possible with name)
    if( !nPoolId && !sName )
        return nullptr;

    // search or create style (without class)
    SwCharFormat *pCFormat = nullptr;
    if( nPoolId )
    {
        pCFormat = GetCharFormatFromPool( nPoolId );
    }
    else
    {
        UIName sCName( OUString::createFromAscii(sName) );
        pCFormat = m_pDoc->FindCharFormatByName( sCName );
        if( !pCFormat )
        {
            pCFormat = m_pDoc->MakeCharFormat( sCName, m_pDoc->GetDfltCharFormat() );
            pCFormat->SetAuto(false);
        }
    }

    assert(pCFormat && "No character style???");

    // If a class exists, then search for the class style but don't
    // create one.
    OUString aClass( rClass );
    GetScriptFromClass( aClass, false );
    if( !aClass.isEmpty() )
    {
        UIName aTmp( pCFormat->GetName() );
        AddClassName( aTmp, aClass );
        SwCharFormat *pClassCFormat = m_pDoc->FindCharFormatByName( aTmp );
        if( pClassCFormat )
        {
            pCFormat = pClassCFormat;
        }
        else
        {
            const SvxCSS1MapEntry *pClass = GetClass( aClass );
            if( pClass )
            {
                pCFormat = m_pDoc->MakeCharFormat( aTmp, pCFormat );
                pCFormat->SetAuto(false);
                SfxItemSet aItemSet( pClass->GetItemSet() );
                SetCharFormatAttrs( pCFormat, aItemSet );
            }
        }
    }

    return pCFormat;
}

SwTextFormatColl *SwCSS1Parser::GetTextCollFromPool( sal_uInt16 nPoolId ) const
{
    const SwTextFormatColls::size_type nOldArrLen = m_pDoc->GetTextFormatColls()->size();

    SwTextFormatColl *pColl = m_pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId, false );

    if( m_bIsNewDoc )
    {
        const SwTextFormatColls::size_type nArrLen = m_pDoc->GetTextFormatColls()->size();
        for( SwTextFormatColls::size_type i=nOldArrLen; i<nArrLen; ++i )
            lcl_swcss1_setEncoding( *(*m_pDoc->GetTextFormatColls())[i],
                                    GetDfltEncoding() );
    }

    return pColl;
}

SwCharFormat *SwCSS1Parser::GetCharFormatFromPool( sal_uInt16 nPoolId ) const
{
    const SwCharFormats::size_type nOldArrLen = m_pDoc->GetCharFormats()->size();

    SwCharFormat *pCharFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nPoolId );

    if( m_bIsNewDoc )
    {
        const SwCharFormats::size_type nArrLen = m_pDoc->GetCharFormats()->size();

        for( SwCharFormats::size_type i=nOldArrLen; i<nArrLen; i++ )
            lcl_swcss1_setEncoding( *(*m_pDoc->GetCharFormats())[i],
                                    GetDfltEncoding() );
    }

    return pCharFormat;
}

SwTextFormatColl *SwCSS1Parser::GetTextFormatColl( sal_uInt16 nTextColl,
                                           const OUString& rClass )
{
    SwTextFormatColl* pColl = nullptr;

    OUString aClass( rClass );
    GetScriptFromClass( aClass, false );
    if( RES_POOLCOLL_TEXT == nTextColl && aClass.getLength() >= 9 &&
        ('s' == aClass[0] || 'S' == aClass[0] ) )
    {
        if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdendnote) )
        {
            nTextColl = RES_POOLCOLL_ENDNOTE;
            aClass.clear();
        }
        else if( aClass.equalsIgnoreAsciiCase(OOO_STRING_SVTOOLS_HTML_sdfootnote) )
        {
            nTextColl = RES_POOLCOLL_FOOTNOTE;
            aClass.clear();
        }
    }

    if( USER_FMT & nTextColl )       // one created by Reader
    {
        OSL_ENSURE( false, "Where does the user style comes from?" );
        pColl = GetTextCollFromPool( RES_POOLCOLL_STANDARD );
    }
    else
    {
        pColl = GetTextCollFromPool( nTextColl );
    }

    if( !aClass.isEmpty() )
    {
        assert(pColl && "No paragraph style???");
        UIName aTmp( pColl->GetName() );
        AddClassName( aTmp, aClass );
        SwTextFormatColl* pClassColl = m_pDoc->FindTextFormatCollByName( aTmp );

        if( !pClassColl &&
            (nTextColl==RES_POOLCOLL_TABLE ||
             nTextColl==RES_POOLCOLL_TABLE_HDLN) )
        {
            // In this case there was a <TD><P CLASS=foo>, but no TD.foo
            // style was found. The we must use P.foo, if available.
            SwTextFormatColl* pCollText =
                GetTextCollFromPool( RES_POOLCOLL_TEXT );
            aTmp = pCollText->GetName();
            AddClassName( aTmp, aClass );
            pClassColl = m_pDoc->FindTextFormatCollByName( aTmp );
        }

        if( pClassColl )
        {
            pColl = pClassColl;
        }
        else
        {
            const SvxCSS1MapEntry *pClass = GetClass( aClass );
            if( pClass )
            {
                pColl = m_pDoc->MakeTextFormatColl( aTmp, pColl );
                SfxItemSet aItemSet( pClass->GetItemSet() );
                SvxCSS1PropertyInfo aPropInfo( pClass->GetPropertyInfo() );
                aPropInfo.SetBoxItem( aItemSet, MIN_BORDER_DIST );
                bool bPositioned = MayBePositioned( pClass->GetPropertyInfo() );
                if( bPositioned )
                    aItemSet.ClearItem( RES_BACKGROUND );
                SetTextCollAttrs( pColl, aItemSet, aPropInfo,
                                 this );
            }
        }

    }

    if( pColl )
        lcl_swcss1_setEncoding( *pColl, GetDfltEncoding() );

    return pColl;
}

SwPageDesc *SwCSS1Parser::GetMasterPageDesc()
{
    return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );
}

static SwPageDesc *FindPageDesc(SwDoc *pDoc, sal_uInt16 nPoolId)
{
    size_t nPageDescs = pDoc->GetPageDescCnt();
    size_t nPage;
    for (nPage=0; nPage < nPageDescs &&
         pDoc->GetPageDesc(nPage).GetPoolFormatId() != nPoolId; ++nPage)
         ;

    return nPage < nPageDescs ? &pDoc->GetPageDesc(nPage) : nullptr;
}

const SwPageDesc *SwCSS1Parser::GetPageDesc( sal_uInt16 nPoolId, bool bCreate )
{
    if( RES_POOLPAGE_HTML == nPoolId )
        return m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );

    const SwPageDesc *pPageDesc = FindPageDesc(m_pDoc, nPoolId);
    if( !pPageDesc && bCreate )
    {
        if (m_rHTMLParser.IsReadingHeaderOrFooter())
        {   // (there should be only one definition of header/footer in HTML)
            SAL_WARN("sw.html", "no creating PageDesc while reading header/footer");
            return nullptr;
        }

        // The first page is created from the right page, if there is one.
        SwPageDesc *pMasterPageDesc = nullptr;
        if( RES_POOLPAGE_FIRST == nPoolId )
            pMasterPageDesc = FindPageDesc(m_pDoc, RES_POOLPAGE_RIGHT);
        if( !pMasterPageDesc )
            pMasterPageDesc = m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );

        // The new page style is created by copying from master
        SwPageDesc *pNewPageDesc = m_pDoc->
            getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId, false );

        // therefore we also need the number of the new style
        OSL_ENSURE(pNewPageDesc == FindPageDesc(m_pDoc, nPoolId), "page style not found");

        m_pDoc->CopyPageDesc( *pMasterPageDesc, *pNewPageDesc, false );

        // Modify the styles for their new purpose.
        const SwPageDesc *pFollow = nullptr;
        bool bSetFollowFollow = false;
        switch( nPoolId )
        {
        case RES_POOLPAGE_FIRST:
            // If there is already a left page, then is it the follow-up
            // style, else it is the HTML style.
            pFollow = GetLeftPageDesc();
            if( !pFollow )
                pFollow = pMasterPageDesc;
            break;

        case RES_POOLPAGE_RIGHT:
            // If the left style is already created, nothing will happen here.
            // Otherwise the left style is created and ensures the link with
            // the right style.
            GetLeftPageDesc( true );
            break;

        case RES_POOLPAGE_LEFT:
            // The right style is created if none exists. No links are created.
            // If there is already a first page style, then the left style becomes
            // follow-up style of the first page.
            pFollow = GetRightPageDesc( true );
            bSetFollowFollow = true;
            {
                const SwPageDesc *pFirstPageDesc = GetFirstPageDesc();
                if( pFirstPageDesc )
                {
                    SwPageDesc aNewFirstPageDesc( *pFirstPageDesc );
                    aNewFirstPageDesc.SetFollow( pNewPageDesc );
                    ChgPageDesc( pFirstPageDesc, aNewFirstPageDesc );
                }
            }
            break;
        }

        if( pFollow )
        {
            SwPageDesc aNewPageDesc( *pNewPageDesc );
            aNewPageDesc.SetFollow( pFollow );
            ChgPageDesc( pNewPageDesc, aNewPageDesc );

            if( bSetFollowFollow )
            {
                SwPageDesc aNewFollowPageDesc( *pFollow );
                aNewFollowPageDesc.SetFollow( pNewPageDesc );
                ChgPageDesc( pFollow, aNewFollowPageDesc );
            }
        }
        pPageDesc = pNewPageDesc;
    }

    return pPageDesc;
}

bool SwCSS1Parser::MayBePositioned( const SvxCSS1PropertyInfo& rPropInfo,
                                    bool bAutoWidth )
{
    if (!rPropInfo.m_bVisible)
    {
        // Don't create a textframe for this div if it's hidden.
        return false;
    }

    // abs-pos
    // left/top none    auto    twip    perc

    // none     Z       Z       -       -
    // auto     Z       Z       -       -
    // twip     Z       Z       S/R     -
    // perc     -       -       -       -

    // - the tag will be positioned absolutely and left/top are both
    //   present and don't contain a percentage value, or
    // - the tag should flow, and
    // - a width was specified (needed in both cases)
    return ( ( SVX_CSS1_POS_ABSOLUTE     == rPropInfo.m_ePosition &&
               SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eLeftType &&
               SVX_CSS1_LTYPE_PERCENTAGE != rPropInfo.m_eTopType &&
              (SVX_CSS1_LTYPE_TWIP       == rPropInfo.m_eLeftType ||
               SVX_CSS1_LTYPE_TWIP       != rPropInfo.m_eTopType) ) ||
             ( SvxAdjust::End            != rPropInfo.m_eFloat  ) ) &&
           ( bAutoWidth ||
             SVX_CSS1_LTYPE_TWIP         == rPropInfo.m_eWidthType ||
             SVX_CSS1_LTYPE_PERCENTAGE   == rPropInfo.m_eWidthType );
}

void SwCSS1Parser::AddClassName( UIName& rFormatName, std::u16string_view rClass )
{
    OSL_ENSURE( !rClass.empty(), "Style class without length?" );

    rFormatName = UIName(rFormatName.toString() + "." + rClass);
}

void SwCSS1Parser::FillDropCap( SwFormatDrop& rDrop,
                                SfxItemSet& rItemSet,
                                const OUString *pName )
{
    // the number of lines matches somehow a percentage value
    // for the height (what happens with absolute heights???)
    sal_uInt8 nLines = rDrop.GetLines();
    if( const SvxFontHeightItem* pFontHeightItem = rItemSet.GetItemIfSet( RES_CHRATR_FONTSIZE, false ) )
    {
        sal_uInt16 nProp = pFontHeightItem->GetProp();
        nLines = static_cast<sal_uInt8>((nProp + 50) / 100);
        if( nLines < 1 )
            nLines = 1;
        else if( nLines > MAX_DROPCAP_LINES )
            nLines = MAX_DROPCAP_LINES;

        // Only when nLines>1, then the attribute also is set. Then
        // we don't need the font height in the character style.
        if( nLines > 1 )
        {
            rItemSet.ClearItem( RES_CHRATR_FONTSIZE );
            rItemSet.ClearItem( RES_CHRATR_CJK_FONTSIZE );
            rItemSet.ClearItem( RES_CHRATR_CTL_FONTSIZE );
        }
    }

    // In case of hard attribution (pName==0) we can stop, if the Initial is
    // only one line.
    if( nLines<=1 )
        return;

    rDrop.SetLines(nLines);

    // a right border becomes the spacing to text!
    if (const SvxRightMarginItem *const pRightMargin = rItemSet.GetItemIfSet(RES_MARGIN_RIGHT, false))
    {
        rDrop.SetDistance(static_cast<sal_uInt16>(pRightMargin->ResolveRight({})));
        rItemSet.ClearItem(RES_MARGIN_RIGHT);
    }
    rItemSet.ClearItem(RES_MARGIN_FIRSTLINE);
    rItemSet.ClearItem(RES_MARGIN_TEXTLEFT);

    // for every other attribute create a character style
    if( !rItemSet.Count() )
        return;

    SwCharFormat *pCFormat = nullptr;
    UIName aName;
    if( pName )
    {
        aName = UIName(*pName + ".FL");   // first letter
        pCFormat = m_pDoc->FindCharFormatByName( aName );
    }
    else
    {
        do
        {
            aName = UIName("first-letter " + OUString::number( static_cast<sal_Int32>(++m_nDropCapCnt) ));
        }
        while( m_pDoc->FindCharFormatByName(aName) );
    }

    if( !pCFormat )
    {
        pCFormat = m_pDoc->MakeCharFormat( aName, m_pDoc->GetDfltCharFormat() );
        pCFormat->SetAuto(false);
    }
    SetCharFormatAttrs( pCFormat, rItemSet );

    // The character style needs only be set in the attribute, when
    // the attribute also is set.
    if( nLines > 1 )
        rDrop.SetCharFormat( pCFormat );
}

// specific CSS1 of SwHTMLParsers

HTMLAttr **SwHTMLParser::GetAttrTabEntry( sal_uInt16 nWhich )
{
    // find the table entry of the item ...
    HTMLAttr **ppAttr = nullptr;
    switch( nWhich )
    {
    case RES_CHRATR_BLINK:
        ppAttr = &m_xAttrTab->pBlink;
        break;
    case RES_CHRATR_CASEMAP:
        ppAttr = &m_xAttrTab->pCaseMap;
        break;
    case RES_CHRATR_COLOR:
        ppAttr = &m_xAttrTab->pFontColor;
        break;
    case RES_CHRATR_CROSSEDOUT:
        ppAttr = &m_xAttrTab->pStrike;
        break;
    case RES_CHRATR_ESCAPEMENT:
        ppAttr = &m_xAttrTab->pEscapement;
        break;
    case RES_CHRATR_FONT:
        ppAttr = &m_xAttrTab->pFont;
        break;
    case RES_CHRATR_CJK_FONT:
        ppAttr = &m_xAttrTab->pFontCJK;
        break;
    case RES_CHRATR_CTL_FONT:
        ppAttr = &m_xAttrTab->pFontCTL;
        break;
    case RES_CHRATR_FONTSIZE:
        ppAttr = &m_xAttrTab->pFontHeight;
        break;
    case RES_CHRATR_CJK_FONTSIZE:
        ppAttr = &m_xAttrTab->pFontHeightCJK;
        break;
    case RES_CHRATR_CTL_FONTSIZE:
        ppAttr = &m_xAttrTab->pFontHeightCTL;
        break;
    case RES_CHRATR_KERNING:
        ppAttr = &m_xAttrTab->pKerning;
        break;
    case RES_CHRATR_POSTURE:
        ppAttr = &m_xAttrTab->pItalic;
        break;
    case RES_CHRATR_CJK_POSTURE:
        ppAttr = &m_xAttrTab->pItalicCJK;
        break;
    case RES_CHRATR_CTL_POSTURE:
        ppAttr = &m_xAttrTab->pItalicCTL;
        break;
    case RES_CHRATR_UNDERLINE:
        ppAttr = &m_xAttrTab->pUnderline;
        break;
    case RES_CHRATR_WEIGHT:
        ppAttr = &m_xAttrTab->pBold;
        break;
    case RES_CHRATR_CJK_WEIGHT:
        ppAttr = &m_xAttrTab->pBoldCJK;
        break;
    case RES_CHRATR_CTL_WEIGHT:
        ppAttr = &m_xAttrTab->pBoldCTL;
        break;
    case RES_CHRATR_BACKGROUND:
        ppAttr = &m_xAttrTab->pCharBrush;
        break;
    case RES_CHRATR_BOX:
        ppAttr = &m_xAttrTab->pCharBox;
        break;

    case RES_PARATR_LINESPACING:
        ppAttr = &m_xAttrTab->pLineSpacing;
        break;
    case RES_PARATR_ADJUST:
        ppAttr = &m_xAttrTab->pAdjust;
        break;

    case RES_MARGIN_FIRSTLINE:
        ppAttr = &m_xAttrTab->pFirstLineIndent;
        break;
    case RES_MARGIN_TEXTLEFT:
        ppAttr = &m_xAttrTab->pTextLeftMargin;
        break;
    case RES_MARGIN_RIGHT:
        ppAttr = &m_xAttrTab->pRightMargin;
        break;
    case RES_UL_SPACE:
        ppAttr = &m_xAttrTab->pULSpace;
        break;
    case RES_BOX:
        ppAttr = &m_xAttrTab->pBox;
        break;
    case RES_BACKGROUND:
        ppAttr = &m_xAttrTab->pBrush;
        break;
    case RES_BREAK:
        ppAttr = &m_xAttrTab->pBreak;
        break;
    case RES_PAGEDESC:
        ppAttr = &m_xAttrTab->pPageDesc;
        break;
    case RES_PARATR_SPLIT:
        ppAttr = &m_xAttrTab->pSplit;
        break;
    case RES_PARATR_WIDOWS:
        ppAttr = &m_xAttrTab->pWidows;
        break;
    case RES_PARATR_ORPHANS:
        ppAttr = &m_xAttrTab->pOrphans;
        break;
    case RES_KEEP:
        ppAttr = &m_xAttrTab->pKeep;
        break;

    case RES_CHRATR_LANGUAGE:
        ppAttr = &m_xAttrTab->pLanguage;
        break;
    case RES_CHRATR_CJK_LANGUAGE:
        ppAttr = &m_xAttrTab->pLanguageCJK;
        break;
    case RES_CHRATR_CTL_LANGUAGE:
        ppAttr = &m_xAttrTab->pLanguageCTL;
        break;

    case RES_FRAMEDIR:
        ppAttr = &m_xAttrTab->pDirection;
        break;
    }

    return ppAttr;
}

void SwHTMLParser::NewStyle()
{
    OUString sType;

    const HTMLOptions& rOptions2 = GetOptions();
    for (size_t i = rOptions2.size(); i; )
    {
        const HTMLOption& rOption = rOptions2[--i];
        if( HtmlOptionId::TYPE == rOption.GetToken() )
            sType = rOption.GetString();
    }

    m_bIgnoreRawData = sType.getLength() && o3tl::getToken(sType, 0,';') != sCSS_mimetype;
}

void SwHTMLParser::EndStyle()
{
    m_bIgnoreRawData = false;

    if( !m_aStyleSource.isEmpty() )
    {
        m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
        m_aStyleSource.clear();
    }
}

bool SwHTMLParser::FileDownload( const OUString& rURL,
                                 OUString& rStr )
{
    // depose view (because of reschedule)
    SwViewShell *pOldVSh = CallEndAction();

    SfxMedium aDLMedium( rURL, StreamMode::READ | StreamMode::SHARE_DENYWRITE );

    SvStream* pStream = aDLMedium.GetInStream();
    if( pStream )
    {
        SvMemoryStream aStream;
        aStream.WriteStream( *pStream );

        rStr = OUString(static_cast<const char *>(aStream.GetData()), aStream.TellEnd(),
            GetSrcEncoding());
    }

    // was aborted?
    SwDocShell* pShell = m_xDoc->GetDocShell();
    if( ( pShell && pShell->IsAbortingImport() )
        || 1 == m_xDoc->getReferenceCount() )
    {
        // was the import aborted from SFX?
        eState = SvParserState::Error;
        pStream = nullptr;
    }

    // recreate View
    SwViewShell *const pVSh = CallStartAction( pOldVSh );
    OSL_ENSURE( pOldVSh == pVSh, "FileDownload: SwViewShell changed on us" );

    return pStream!=nullptr;
}

void SwHTMLParser::InsertLink()
{
    bool bFinishDownload = false;
    if( !m_vPendingStack.empty() )
    {
        OSL_ENSURE( ShouldFinishFileDownload(),
                "Pending-Stack without File-Download?" );

        m_vPendingStack.pop_back();
        assert( m_vPendingStack.empty() && "Where does the Pending-Stack come from?" );

        bFinishDownload = true;
    }
    else
    {
        OUString sRel, sHRef, sType;

        const HTMLOptions& rOptions2 = GetOptions();
        for (size_t i = rOptions2.size(); i; )
        {
            const HTMLOption& rOption = rOptions2[--i];
            switch( rOption.GetToken() )
            {
                case HtmlOptionId::REL:
                    sRel = rOption.GetString();
                    break;
                case HtmlOptionId::HREF:
                    sHRef = URIHelper::SmartRel2Abs( INetURLObject( m_sBaseURL ), rOption.GetString(), Link<OUString *, bool>(), false );
                    break;
                case HtmlOptionId::TYPE:
                    sType = rOption.GetString();
                    break;
                default: break;
            }
        }

        if( !sHRef.isEmpty() && sRel.equalsIgnoreAsciiCase( "STYLESHEET" ) &&
            ( sType.isEmpty() || o3tl::getToken(sType, 0,';') == sCSS_mimetype ) )
        {
            if( GetMedium() )
            {
                // start download of style source
                StartFileDownload(sHRef);
                if( IsParserWorking() )
                {
                    // The style was loaded synchronously and we can call it directly.
                    bFinishDownload = true;
                }
                else
                {
                    // The style was load asynchronously and is only available
                    // on the next continue call. Therefore we must create a
                    // Pending stack, so that we will return to here.
                    m_vPendingStack.emplace_back( HtmlTokenId::LINK );
                }
            }
            else
            {
                // load file synchronous
                OUString sSource;
                if( FileDownload( sHRef, sSource ) )
                    m_pCSS1Parser->ParseStyleSheet( sSource );
            }
        }
    }

    if( bFinishDownload )
    {
        OUString sSource;
        if( FinishFileDownload( sSource ) && !sSource.isEmpty() )
            m_pCSS1Parser->ParseStyleSheet( sSource );
    }
}

bool SwCSS1Parser::ParseStyleSheet( const OUString& rIn )
{
    if( !SvxCSS1Parser::ParseStyleSheet( rIn ) )
        return false;

    SwPageDesc *pMasterPageDesc =
        m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_HTML, false );

    SvxCSS1MapEntry* pPageEntry = GetPage(OUString(), false);
    if( pPageEntry )
    {
        // @page (affects all already existing pages)

        SetPageDescAttrs( pMasterPageDesc, pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );

        // For all other already existing page styles the attributes
        // must also be set

        SetPageDescAttrs( GetFirstPageDesc(), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );
        SetPageDescAttrs( GetLeftPageDesc(), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );
        SetPageDescAttrs( GetRightPageDesc(), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );

    }

    pPageEntry = GetPage( u"first"_ustr, true );
    if( pPageEntry )
    {
        SetPageDescAttrs( GetFirstPageDesc(true), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );
        m_bSetFirstPageDesc = true;
    }

    pPageEntry = GetPage( u"right"_ustr, true );
    if( pPageEntry )
    {
        SetPageDescAttrs( GetRightPageDesc(true), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );
        m_bSetRightPageDesc = true;
    }

    pPageEntry = GetPage( u"left"_ustr, true );
    if( pPageEntry )
        SetPageDescAttrs( GetLeftPageDesc(true), pPageEntry->GetItemSet(),
                          pPageEntry->GetPropertyInfo() );

    return true;
}

bool SwHTMLParser::ParseStyleOptions( const OUString &rStyle,
                                          const OUString &rId,
                                          const OUString &rClass,
                                          SfxItemSet &rItemSet,
                                          SvxCSS1PropertyInfo &rPropInfo,
                                          const OUString *pLang,
                                          const OUString *pDir )
{
    bool bRet = false;

    if( !rClass.isEmpty() )
    {
        OUString aClass( rClass );
        SwCSS1Parser::GetScriptFromClass( aClass );
        const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
        if( pClass )
        {
            SvxCSS1Parser::MergeStyles( pClass->GetItemSet(),
                                      pClass->GetPropertyInfo(),
                                      rItemSet, rPropInfo, false );
            bRet = true;
        }
    }

    if( !rId.isEmpty() )
    {
        const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
        if( pId )
            SvxCSS1Parser::MergeStyles( pId->GetItemSet(),
                                      pId->GetPropertyInfo(),
                                      rItemSet, rPropInfo, !rClass.isEmpty() );
        rPropInfo.m_aId = rId;
        bRet = true;
    }

    if( !rStyle.isEmpty() )
    {
        m_pCSS1Parser->ParseStyleOption( rStyle, rItemSet, rPropInfo );
        bRet = true;
    }

    if( bRet )
        rPropInfo.SetBoxItem( rItemSet, MIN_BORDER_DIST );

    if( pLang && !pLang->isEmpty() )
    {
        LanguageType eLang = LanguageTag::convertToLanguageTypeWithFallback( *pLang );
        if( LANGUAGE_DONTKNOW != eLang )
        {
            SvxLanguageItem aLang( eLang, RES_CHRATR_LANGUAGE );
            rItemSet.Put( aLang );
            aLang.SetWhich( RES_CHRATR_CJK_LANGUAGE );
            rItemSet.Put( aLang );
            aLang.SetWhich( RES_CHRATR_CTL_LANGUAGE );
            rItemSet.Put( aLang );

            bRet = true;
        }
    }
    if( pDir && !pDir->isEmpty() )
    {
        OUString aValue( *pDir );
        SvxFrameDirection eDir = SvxFrameDirection::Environment;
        if (aValue.equalsIgnoreAsciiCase("LTR"))
            eDir = SvxFrameDirection::Horizontal_LR_TB;
        else if (aValue.equalsIgnoreAsciiCase("RTL"))
            eDir = SvxFrameDirection::Horizontal_RL_TB;

        if( SvxFrameDirection::Environment != eDir )
        {
            SvxFrameDirectionItem aDir( eDir, RES_FRAMEDIR );
            rItemSet.Put( aDir );

            bRet = true;
        }
    }

    return bRet;
}

void SwHTMLParser::SetAnchorAndAdjustment( const SvxCSS1PropertyInfo &rPropInfo,
                                           SfxItemSet &rFrameItemSet )
{
    SwFormatAnchor aAnchor;

    sal_Int16 eHoriOri = text::HoriOrientation::NONE;
    sal_Int16 eVertOri = text::VertOrientation::NONE;
    sal_Int16 eHoriRel = text::RelOrientation::FRAME;
    sal_Int16 eVertRel = text::RelOrientation::FRAME;
    SwTwips nHoriPos = 0, nVertPos = 0;
    css::text::WrapTextMode eSurround = css::text::WrapTextMode_THROUGH;
    if( SVX_CSS1_POS_ABSOLUTE == rPropInfo.m_ePosition )
    {
        if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType &&
            SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eTopType )
        {
            // Absolute positioned objects are page-bound, when they
            // aren't in a frame and otherwise frame-bound.
            const SwStartNode *pFlySttNd =
                m_pPam->GetPoint()->GetNode().FindFlyStartNode();
            if( pFlySttNd )
            {
                aAnchor.SetType( RndStdIds::FLY_AT_FLY );
                SwPosition aPos( *pFlySttNd );
                aAnchor.SetAnchor( &aPos );
            }
            else
            {
                aAnchor.SetType( RndStdIds::FLY_AT_PAGE );
                aAnchor.SetPageNum( 1 );
            }
            nHoriPos = rPropInfo.m_nLeft;
            nVertPos = rPropInfo.m_nTop;
        }
        else
        {
            aAnchor.SetType( RndStdIds::FLY_AT_PARA );
            aAnchor.SetAnchor( m_pPam->GetPoint() );
            eVertOri = text::VertOrientation::TOP;
            eVertRel = text::RelOrientation::CHAR;
            if( SVX_CSS1_LTYPE_TWIP == rPropInfo.m_eLeftType )
            {
                eHoriOri = text::HoriOrientation::NONE;
                eHoriRel = text::RelOrientation::PAGE_FRAME;
                nHoriPos = rPropInfo.m_nLeft;
            }
            else
            {
                eHoriOri = text::HoriOrientation::LEFT;
                eHoriRel = text::RelOrientation::FRAME;   // to be changed later
            }
        }
    }
    else
    {
        // Flowing object are inserted as paragraph-bound, when the paragraph is
        // still empty and otherwise auto-bound.
        // Auto-bound frames for the time being inserted at the previous position
        // and later moved.
        const sal_Int32 nContent = m_pPam->GetPoint()->GetContentIndex();
        if( nContent )
        {
            aAnchor.SetType( RndStdIds::FLY_AT_CHAR );
            m_pPam->Move( fnMoveBackward );
            eVertOri = text::VertOrientation::CHAR_BOTTOM;
            eVertRel = text::RelOrientation::CHAR;
        }
        else
        {
            aAnchor.SetType( RndStdIds::FLY_AT_PARA );
            eVertOri = text::VertOrientation::TOP;
            eVertRel = text::RelOrientation::PRINT_AREA;
        }

        aAnchor.SetAnchor( m_pPam->GetPoint() );

        if( nContent )
            m_pPam->Move( fnMoveForward );

        sal_uInt16 nLeftSpace = 0, nRightSpace = 0;
        short nIndent = 0;
        GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );

        if( SvxAdjust::Right==rPropInfo.m_eFloat )
        {
            eHoriOri = text::HoriOrientation::RIGHT;
            eHoriRel = nRightSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
            eSurround = css::text::WrapTextMode_LEFT;
        }
        else
        {
            eHoriOri = text::HoriOrientation::LEFT;
            eHoriRel = nLeftSpace ? text::RelOrientation::PRINT_AREA : text::RelOrientation::FRAME;
            eSurround = css::text::WrapTextMode_RIGHT;
        }
    }
    rFrameItemSet.Put( aAnchor );

    // positioned absolutely with wrap
    rFrameItemSet.Put( SwFormatHoriOrient( nHoriPos, eHoriOri, eHoriRel ) );
    rFrameItemSet.Put( SwFormatVertOrient( nVertPos, eVertOri, eVertRel ) );
    rFrameItemSet.Put( SwFormatSurround( eSurround ) );
}

void SwHTMLParser::SetVarSize( SvxCSS1PropertyInfo const &rPropInfo,
                               SfxItemSet &rFrameItemSet,
                               SwTwips nDfltWidth, sal_uInt8 nDfltPrcWidth )
{
    SwTwips nWidth = nDfltWidth, nHeight = MINFLY;
    sal_uInt8 nPercentWidth = nDfltPrcWidth, nPercentHeight = 0;
    switch( rPropInfo.m_eWidthType )
    {
    case SVX_CSS1_LTYPE_PERCENTAGE:
        nPercentWidth = rPropInfo.m_nWidth > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nWidth) : 1;
        nWidth = MINFLY;
        break;
    case SVX_CSS1_LTYPE_TWIP:
        nWidth = std::max<tools::Long>(rPropInfo.m_nWidth, MINFLY);
        nPercentWidth = 0;
        break;
    default:
        ;
    }
    switch( rPropInfo.m_eHeightType )
    {
    case SVX_CSS1_LTYPE_PERCENTAGE:
        nPercentHeight = rPropInfo.m_nHeight > 0 ? static_cast<sal_uInt8>(rPropInfo.m_nHeight) : 1;
        break;
    case SVX_CSS1_LTYPE_TWIP:
        // Netscape and MS-IE interpreting the height incorrectly as minimum height,
        // therefore we are doing the same.
        nHeight = std::max<tools::Long>(rPropInfo.m_nHeight, MINFLY);
        break;
    default:
        ;
    }

    SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, nWidth, nHeight );
    aFrameSize.SetWidthPercent( nPercentWidth );
    aFrameSize.SetHeightPercent( nPercentHeight );
    rFrameItemSet.Put( aFrameSize );
}

void SwHTMLParser::SetFrameFormatAttrs( SfxItemSet &rItemSet,
                                   HtmlFrameFormatFlags nFlags,
                                   SfxItemSet &rFrameItemSet )
{
    const SvxBoxItem *pBoxItem;
    if( (nFlags & HtmlFrameFormatFlags::Box) &&
        (pBoxItem = rItemSet.GetItemIfSet( RES_BOX )) )
    {
        if( nFlags & HtmlFrameFormatFlags::Padding )
        {
            SvxBoxItem aBoxItem( *pBoxItem );
            // reset all 4 sides to 0
            aBoxItem.SetAllDistances(0);
            rFrameItemSet.Put( aBoxItem );
        }
        else
        {
            rFrameItemSet.Put( *pBoxItem );
        }
        rItemSet.ClearItem( RES_BOX );
    }

    const SvxBrushItem* pBrushItem;
    if( (nFlags & HtmlFrameFormatFlags::Background) &&
        (pBrushItem = rItemSet.GetItemIfSet( RES_BACKGROUND )) )
    {
        rFrameItemSet.Put( *pBrushItem );
        rItemSet.ClearItem( RES_BACKGROUND );
    }

    const SvxFrameDirectionItem* pFrameDirectionItem;
    if( (nFlags & HtmlFrameFormatFlags::Direction) &&
        (pFrameDirectionItem = rItemSet.GetItemIfSet( RES_FRAMEDIR )) )
    {
        rFrameItemSet.Put( *pFrameDirectionItem );
        rItemSet.ClearItem( RES_FRAMEDIR );
    }
}

std::unique_ptr<HTMLAttrContext> SwHTMLParser::PopContext( HtmlTokenId nToken )
{
    std::unique_ptr<HTMLAttrContext> xCntxt;

    HTMLAttrContexts::size_type nPos = m_aContexts.size();
    if( nPos <= m_nContextStMin )
        return nullptr;

    bool bFound = HtmlTokenId::NONE == nToken;
    if( nToken != HtmlTokenId::NONE )
    {
        // search for stack entry of token ...
        while( nPos > m_nContextStMin )
        {
            HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
            if( nCntxtToken == nToken )
            {
                bFound = true;
                break;
            }
            else if( nCntxtToken == HtmlTokenId::NONE ) // zero as token doesn't occur
            {
                break;
            }
        }
    }
    else
    {
        nPos--;
    }

    if( bFound )
    {
        xCntxt = std::move(m_aContexts[nPos]);
        m_aContexts.erase( m_aContexts.begin() + nPos );
    }

    return xCntxt;
}

void SwHTMLParser::GetMarginsFromContext( sal_uInt16& nLeft,
                                          sal_uInt16& nRight,
                                          short& nIndent,
                                          bool bIgnoreTopContext ) const
{
    HTMLAttrContexts::size_type nPos = m_aContexts.size();
    if( bIgnoreTopContext )
    {
        if( !nPos )
            return;
        else
            nPos--;
    }

    while( nPos > m_nContextStAttrMin )
    {
        const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
        if( pCntxt->IsLRSpaceChanged() )
        {
            pCntxt->GetMargins( nLeft, nRight, nIndent );
            return;
        }
    }
}

void SwHTMLParser::GetMarginsFromContextWithNumberBullet( sal_uInt16& nLeft,
                                                    sal_uInt16& nRight,
                                                    short& nIndent ) const
{
    GetMarginsFromContext( nLeft, nRight, nIndent );
    const SwHTMLNumRuleInfo& rInfo = const_cast<SwHTMLParser*>(this)->GetNumInfo();
    if( rInfo.GetDepth() )
    {
        sal_uInt8 nLevel = static_cast<sal_uInt8>( (rInfo.GetDepth() <= MAXLEVEL ? rInfo.GetDepth()
                                                            : MAXLEVEL) - 1 );
        const SwNumFormat& rNumFormat = rInfo.GetNumRule()->Get(nLevel);
        nLeft = nLeft + rNumFormat.GetAbsLSpace(); //TODO: overflow
        nIndent = rNumFormat.GetFirstLineOffset(); //TODO: overflow
    }
}

void SwHTMLParser::GetULSpaceFromContext( sal_uInt16& nUpper,
                                          sal_uInt16& nLower ) const
{
    sal_uInt16 nDefaultColl = 0;
    OUString aDefaultClass;

    HTMLAttrContexts::size_type nPos = m_aContexts.size();
    while( nPos > m_nContextStAttrMin )
    {
        const HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
        if( pCntxt->IsULSpaceChanged() )
        {
            pCntxt->GetULSpace( nUpper, nLower );
            return;
        }
        else if (!nDefaultColl)
        {
            nDefaultColl = pCntxt->GetDefaultTextFormatColl();
            if (nDefaultColl)
                aDefaultClass = pCntxt->GetClass();
        }
    }

    if (!nDefaultColl)
        nDefaultColl = RES_POOLCOLL_TEXT;

    const SwTextFormatColl *pColl =
        m_pCSS1Parser->GetTextFormatColl(nDefaultColl, aDefaultClass);
    const SvxULSpaceItem& rULSpace = pColl->GetULSpace();
    nUpper = rULSpace.GetUpper();
    nLower = rULSpace.GetLower();
}

void SwHTMLParser::EndContextAttrs( HTMLAttrContext *pContext )
{
    HTMLAttrs &rAttrs = pContext->GetAttrs();
    for( auto pAttr : rAttrs )
    {
        if( RES_PARATR_DROP==pAttr->GetItem().Which() )
        {
            // Set the number of characters for DropCaps. If it's zero at the
            // end, the attribute is set to invalid and then isn't set from SetAttr.
            sal_Int32 nChars = m_pPam->GetPoint()->GetContentIndex();
            if( nChars < 1 )
                pAttr->Invalidate();
            else if( nChars > MAX_DROPCAP_CHARS )
                nChars = MAX_DROPCAP_CHARS;
            static_cast<SwFormatDrop&>(pAttr->GetItem()).SetChars(static_cast<sal_uInt8>(nChars));
        }

        EndAttr( pAttr );
    }
}

void SwHTMLParser::InsertParaAttrs( const SfxItemSet& rItemSet )
{
    SfxItemIter aIter( rItemSet );

    for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
    {
        // search for the table entry of the item...
        sal_uInt16 nWhich = pItem->Which();
        HTMLAttr **ppAttr = GetAttrTabEntry( nWhich );

        if( ppAttr )
        {
            NewAttr(m_xAttrTab, ppAttr, *pItem);
            if( RES_PARATR_BEGIN > nWhich )
                (*ppAttr)->SetLikePara();
            m_aParaAttrs.push_back( *ppAttr );
            bool bSuccess = EndAttr( *ppAttr, false );
            if (!bSuccess)
                m_aParaAttrs.pop_back();
        }
    }
}

static void lcl_swcss1_setEncoding( SwFormat& rFormat, rtl_TextEncoding eEnc )
{
    if( RTL_TEXTENCODING_DONTKNOW == eEnc )
        return;

    const SfxItemSet& rItemSet = rFormat.GetAttrSet();
    static const TypedWhichId<SvxFontItem> aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
                                             RES_CHRATR_CTL_FONT };
    for (auto const & i : aWhichIds)
    {
        const SvxFontItem *pFontItem = rItemSet.GetItemIfSet(i, false);
        if (!pFontItem)
            continue;
        if (RTL_TEXTENCODING_SYMBOL == pFontItem->GetCharSet())
            continue;
        if (eEnc == pFontItem->GetCharSet())
            continue;
        SvxFontItem aFont(pFontItem->GetFamily(), pFontItem->GetFamilyName(),
                          pFontItem->GetStyleName(), pFontItem->GetPitch(),
                          eEnc, i);
        rFormat.SetFormatAttr(aFont);
    }
}

void SwCSS1Parser::SetDfltEncoding( rtl_TextEncoding eEnc )
{
    if( eEnc == GetDfltEncoding() )
        return;

    if( m_bIsNewDoc )
    {
        // Set new encoding as pool default
        static const sal_uInt16 aWhichIds[3] = { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
                                       RES_CHRATR_CTL_FONT };
        for(sal_uInt16 i : aWhichIds)
        {
            const SvxFontItem& rDfltFont =
                static_cast<const SvxFontItem&>(m_pDoc->GetDefault( i));
            SvxFontItem aFont( rDfltFont.GetFamily(),
                               rDfltFont.GetFamilyName(),
                               rDfltFont.GetStyleName(),
                               rDfltFont.GetPitch(),
                               eEnc, i );
            m_pDoc->SetDefault( aFont );
        }

        // Change all paragraph styles that do specify a font.
        for( auto pTextFormatColl : *m_pDoc->GetTextFormatColls() )
            lcl_swcss1_setEncoding( *pTextFormatColl, eEnc );

        // Change all character styles that do specify a font.
        for( auto pCharFormat : *m_pDoc->GetCharFormats() )
            lcl_swcss1_setEncoding( *pCharFormat, eEnc );
    }

    SvxCSS1Parser::SetDfltEncoding( eEnc );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
