    /* -*- 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 <sal/log.hxx>

    #include <algorithm>
    #include <memory>
    #include <hintids.hxx>

    #include <vcl/wrkwin.hxx>
    #include <vcl/svapp.hxx>
    #include <sot/storage.hxx>
    #include <fmtornt.hxx>
    #include <fmtfsize.hxx>
    #include <frmfmt.hxx>
    #include <docary.hxx>
    #include <ndtxt.hxx>
    #include <doc.hxx>
    #include <IDocumentLayoutAccess.hxx>
    #include <swtable.hxx>
    #include <rootfrm.hxx>
    #include <docsh.hxx>
    #include <flyfrm.hxx>
    #include <poolfmt.hxx>
    #include <viewsh.hxx>
    #include <tabfrm.hxx>
    #include <viewopt.hxx>
    #include <htmltbl.hxx>
    #include <ndindex.hxx>
    #include <calbck.hxx>
    #include <o3tl/numeric.hxx>
    #ifdef DBG_UTIL
    #include <tblrwcl.hxx>
    #endif

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

    #define COLFUZZY 20
    #define MAX_TABWIDTH (USHRT_MAX - 2001)

    class SwHTMLTableLayoutConstraints
    {
        sal_uInt16 nRow;                    // start row
        sal_uInt16 nCol;                    // start column
        sal_uInt16 nColSpan;                // the column's COLSPAN

        std::unique_ptr<SwHTMLTableLayoutConstraints> pNext;        // the next constraint

        sal_uLong nMinNoAlign, nMaxNoAlign; // provisional result of AL-Pass 1

    public:
        SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow,
                                    sal_uInt16 nCol, sal_uInt16 nColSp );

        sal_uLong GetMinNoAlign() const { return nMinNoAlign; }
        sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; }

        SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt );
        SwHTMLTableLayoutConstraints* GetNext() const { return pNext.get(); }

        sal_uInt16 GetColSpan() const { return nColSpan; }
        sal_uInt16 GetColumn() const { return nCol; }
    };

    SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd,
                                                 std::shared_ptr<SwHTMLTableLayout> const& rTab,
                                                 bool bNoBrTag,
                                                 std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) :
        xNext( rNxt ), pBox( nullptr ), xTable( rTab ), pStartNode( pSttNd ),
        nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag )
    {}

    const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const
    {
        return pBox ? pBox->GetSttNd() : pStartNode;
    }

    SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts,
                                              sal_uInt16 nRSpan, sal_uInt16 nCSpan,
                                              sal_uInt16 nWidth, bool bPrcWidth,
                                              bool bNWrapOpt ) :
        xContents(rCnts),
        nRowSpan( nRSpan ), nColSpan( nCSpan ),
        nWidthOption( nWidth ), bPrcWidthOption( bPrcWidth ),
        bNoWrapOption( bNWrapOpt )
    {}

    SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth,
                                                      bool bRelWidth,
                                                      bool bLBorder ) :
        nMinNoAlign(MINLAY), nMaxNoAlign(MINLAY), nAbsMinNoAlign(MINLAY),
        nMin(0), nMax(0),
        nAbsColWidth(0), nRelColWidth(0),
        nWidthOption( nWidth ), bRelWidthOption( bRelWidth ),
        bLeftBorder( bLBorder )
    {}

    SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(
        sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRw, sal_uInt16 nColumn, sal_uInt16 nColSp ):
        nRow( nRw ), nCol( nColumn ), nColSpan( nColSp ),
        nMinNoAlign( nMin ), nMaxNoAlign( nMax )
    {}

    SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext(
        SwHTMLTableLayoutConstraints *pNxt )
    {
        SwHTMLTableLayoutConstraints *pPrev = nullptr;
        SwHTMLTableLayoutConstraints *pConstr = this;
        while( pConstr )
        {
            if( pConstr->nRow > pNxt->nRow ||
                pConstr->GetColumn() > pNxt->GetColumn() )
                break;
            pPrev = pConstr;
            pConstr = pConstr->GetNext();
        }

        if( pPrev )
        {
            pNxt->pNext = std::move(pPrev->pNext);
            pPrev->pNext.reset( pNxt );
            pConstr = this;
        }
        else
        {
            pNxt->pNext.reset( this );
            pConstr = pNxt;
        }

        return pConstr;
    }

    SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable,
                                          sal_uInt16 nRws, sal_uInt16 nCls,
                                          bool bColsOpt, bool bColTgs,
                                          sal_uInt16 nWdth, bool bPrcWdth,
                                          sal_uInt16 nBorderOpt, sal_uInt16 nCellPad,
                                          sal_uInt16 nCellSp, SvxAdjust eAdjust,
                                          sal_uInt16 nLMargin, sal_uInt16 nRMargin,
                                          sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth,
                                          sal_uInt16 nRightBWidth )
        : m_aColumns( nCls )
        , m_aCells( static_cast<size_t>(nRws)*nCls )
        , m_pSwTable( pTable )
        , m_nMin( 0 )
        , m_nMax( 0 )
        , m_nRows( nRws )
        , m_nCols( nCls )
        , m_nLeftMargin( nLMargin )
        , m_nRightMargin( nRMargin )
        , m_nInhAbsLeftSpace( 0 )
        , m_nInhAbsRightSpace( 0 )
        , m_nRelLeftFill( 0 )
        , m_nRelRightFill( 0 )
        , m_nRelTabWidth( 0 )
        , m_nWidthOption( nWdth )
        , m_nCellPadding( nCellPad )
        , m_nCellSpacing( nCellSp )
        , m_nBorder( nBorderOpt )
        , m_nLeftBorderWidth( nLeftBWidth )
        , m_nRightBorderWidth( nRightBWidth )
        , m_nInhLeftBorderWidth( 0 )
        , m_nInhRightBorderWidth( 0 )
        , m_nBorderWidth( nBWidth )
        , m_nDelayedResizeAbsAvail( 0 )
        , m_nLastResizeAbsAvail( 0 )
        , m_nPass1Done( 0 )
        , m_nWidthSet( 0 )
        , m_eTableAdjust( eAdjust )
        , m_bColsOption( bColsOpt )
        , m_bColTags( bColTgs )
        , m_bPrcWidthOption( bPrcWdth )
        , m_bUseRelWidth( false )
        , m_bMustResize( true )
        , m_bExportable( true )
        , m_bBordersChanged( false )
        , m_bMayBeInFlyFrame( false )
        , m_bDelayedResizeRecalc( false)
        , m_bMustNotResize( false )
        , m_bMustNotRecalc( false )
    {
        m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout,
                                                 DelayedResize_Impl ) );
    }

    SwHTMLTableLayout::~SwHTMLTableLayout()
    {
    }

    /// The border widths are calculated like in Netscape:
    /// Outer border: BORDER + CELLSPACING + CELLPADDING
    /// Inner border: CELLSPACING + CELLPADDING
    /// However, we respect the border widths in SW if bSwBorders is set,
    /// so that we don't wrap wrongly.
    /// We also need to respect the distance to the content. Even if
    /// only the opposite side has a border.
    sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
                                                bool bSwBorders ) const
    {
        sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;

        if( nCol == 0 )
        {
            nSpace = nSpace + m_nBorder;

            if( bSwBorders && nSpace < m_nLeftBorderWidth )
                nSpace = m_nLeftBorderWidth;
        }
        else if( bSwBorders )
        {
            if( GetColumn(nCol)->HasLeftBorder() )
            {
                if( nSpace < m_nBorderWidth )
                    nSpace = m_nBorderWidth;
            }
            else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth &&
                     nSpace < MIN_BORDER_DIST )
            {
                OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" );
                // If the opposite side has a border we need to respect at
                // least the minimum distance to the content.
                // Additionally, we could also use nCellPadding for this.
                nSpace = MIN_BORDER_DIST;
            }
        }

        return nSpace;
    }

    sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
                                                 bool bSwBorders ) const
    {
        sal_uInt16 nSpace = m_nCellPadding;

        if( nCol+nColSpan == m_nCols )
        {
            nSpace += m_nBorder + m_nCellSpacing;
            if( bSwBorders && nSpace < m_nRightBorderWidth )
                nSpace = m_nRightBorderWidth;
        }
        else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() &&
                 nSpace < MIN_BORDER_DIST )
        {
            OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" );
            // If the opposite side has a border we need to respect at
            // least the minimum distance to the content.
            // Additionally, we could also use nCellPadding for this.
            nSpace = MIN_BORDER_DIST;
        }

        return nSpace;
    }

    void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax,
                                            sal_uLong &rAbsMin,
                                            sal_uInt16 nCol, sal_uInt16 nColSpan,
                                            bool bSwBorders ) const
    {
        sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) +
                     GetRightCellSpace( nCol, nColSpan, bSwBorders );

        rMin += nAdd;
        rMax += nAdd;
        rAbsMin += nAdd;
    }

    void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol,
                                 sal_uInt16 nColSpan ) const
    {
        SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();

        // calculate the box's width
        SwTwips nFrameWidth = 0;
        while( nColSpan-- )
            nFrameWidth += GetColumn( nCol++ )->GetRelColWidth();

        // and reset
        pFrameFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nFrameWidth, 0 ));
    }

    void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan,
                                      sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const
    {
        rAbsAvail = 0;
        rRelAvail = 0;
        for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ )
        {
            const SwHTMLTableLayoutColumn *pColumn = GetColumn(i);
            rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth();
            rRelAvail = rRelAvail + pColumn->GetRelColWidth();
        }
    }

    sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc )
    {
        SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
        if( pVSh )
        {
            return static_cast<sal_uInt16>(pVSh->GetBrowseWidth());
        }

        return 0;
    }

    sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc )
    {
        // If we have a layout, we can get the width from there.
        const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
        if( pRootFrame )
        {
            const SwFrame *pPageFrame = pRootFrame->GetLower();
            if( pPageFrame )
                return static_cast<sal_uInt16>(pPageFrame->getFramePrintArea().Width());
        }

        // #i91658#
        // Assertion removed which state that no browse width is available.
        // Investigation reveals that all calls can handle the case that no browse
        // width is provided.
        return GetBrowseWidthByVisArea( rDoc );
    }

    sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame(
        const SwTabFrame& rTabFrame ) const
    {
        SwTwips nWidth = 0;

        const SwFrame *pUpper = rTabFrame.GetUpper();
        if( MayBeInFlyFrame() && pUpper->IsFlyFrame() &&
            static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() )
        {
            // If the table is located within a self-created frame, the anchor's
            // width is relevant not the frame's width.
            // For paragraph-bound frames we don't respect paragraph indents.
            const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame();
            if( pAnchor->IsTextFrame() )
                nWidth = pAnchor->getFrameArea().Width();
            else
                nWidth = pAnchor->getFramePrintArea().Width();
        }
        else
        {
            nWidth = pUpper->getFramePrintArea().Width();
        }

        SwTwips nUpperDummy = 0;
        long nRightOffset = 0,
             nLeftOffset  = 0;
        rTabFrame.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset );
        nWidth -= (nLeftOffset + nRightOffset);

        return static_cast<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16)));
    }

    sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const
    {
        sal_uInt16 nBrowseWidth = 0;
        SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First();
        if( pFrame )
        {
            nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame );
        }
        else
        {
            nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc );
        }

        return nBrowseWidth;
    }

    const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const
    {
        const SwStartNode *pBoxSttNd;

        const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0];
        while( nullptr == (pBoxSttNd = pBox->GetSttNd()) )
        {
            OSL_ENSURE( pBox->GetTabLines().size() > 0,
                    "Box without start node and lines" );
            OSL_ENSURE( pBox->GetTabLines().front()->GetTabBoxes().size() > 0,
                    "Line without boxes" );
            pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
        }

        return pBoxSttNd;
    }

    SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const
    {
        const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode();
        OSL_ENSURE( pTableNd, "No Table-Node?" );
        return pTableNd->GetFlyFormat();
    }

    static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts,
                            sal_uLong& rAbsMinNoAlignCnts,
                            SwTextNode const *pTextNd, sal_uLong nIdx, bool bNoBreak )
    {
        pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts,
                               rAbsMinNoAlignCnts );
        OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts,
                "GetMinMaxSize: absmin > min" );
        OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts,
                "GetMinMaxSize: max > min" );

        // The maximal width for a <PRE> paragraph is the minimal width
        const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
        while( pColl && !pColl->IsDefault() &&
                (USER_FMT & pColl->GetPoolFormatId()) )
        {
            pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom());
        }

        // <NOBR> in the whole cell apply to text but not to tables.
        // Netscape only considers this for graphics.
        if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak )
        {
            rMinNoAlignCnts = rMaxNoAlignCnts;
            rAbsMinNoAlignCnts = rMaxNoAlignCnts;
        }
    }

    void SwHTMLTableLayout::AutoLayoutPass1()
    {
        m_nPass1Done++;

        m_nMin = m_nMax = 0; // clear pass1 info

        bool bFixRelWidths = false;
        sal_uInt16 i;

        std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints;

        for( i=0; i<m_nCols; i++ )
        {
            SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
            pColumn->ClearPass1Info( !HasColTags() );
            sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to
            sal_uInt16 nColSkip = USHRT_MAX;    // How many columns need to be skipped

            for( sal_uInt16 j=0; j<m_nRows; j++ )
            {
                SwHTMLTableLayoutCell *pCell = GetCell(j,i);
                SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get();

                // We need to examine all rows in order to
                // get the column that should be calculated next.
                sal_uInt16 nColSpan = pCell->GetColSpan();
                if( nColSpan < nColSkip )
                    nColSkip = nColSpan;

                if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) )
                {
                    // The cell is empty or it's content was not edited
                    if( nColSpan < nMinColSpan )
                        nMinColSpan = nColSpan;

                    sal_uLong nMinNoAlignCell = 0;
                    sal_uLong nMaxNoAlignCell = 0;
                    sal_uLong nAbsMinNoAlignCell = 0;
                    sal_uLong nMaxTableCell = 0;
                    sal_uLong nAbsMinTableCell = 0;

                    while( pCnts )
                    {
                        const SwStartNode *pSttNd = pCnts->GetStartNode();
                        if( pSttNd )
                        {
                            const SwDoc *pDoc = pSttNd->GetDoc();
                            sal_uLong nIdx = pSttNd->GetIndex();
                            while( !(pDoc->GetNodes()[nIdx])->IsEndNode() )
                            {
                                SwTextNode *pTextNd = (pDoc->GetNodes()[nIdx])->GetTextNode();
                                if( pTextNd )
                                {
                                    sal_uLong nMinNoAlignCnts = 0;
                                    sal_uLong nMaxNoAlignCnts = 0;
                                    sal_uLong nAbsMinNoAlignCnts = 0;

                                    lcl_GetMinMaxSize( nMinNoAlignCnts,
                                                       nMaxNoAlignCnts,
                                                       nAbsMinNoAlignCnts,
                                                       pTextNd, nIdx,
                                                       pCnts->HasNoBreakTag() );

                                    if( nMinNoAlignCnts > nMinNoAlignCell )
                                        nMinNoAlignCell = nMinNoAlignCnts;
                                    if( nMaxNoAlignCnts > nMaxNoAlignCell )
                                        nMaxNoAlignCell = nMaxNoAlignCnts;
                                    if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell )
                                        nAbsMinNoAlignCell = nAbsMinNoAlignCnts;
                                }
                                else
                                {
                                    SwTableNode *pTabNd = (pDoc->GetNodes()[nIdx])->GetTableNode();
                                    if( pTabNd )
                                    {
                                        SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout();
                                        if( pChild )
                                        {
                                            pChild->AutoLayoutPass1();
                                            sal_uLong nMaxTableCnts = pChild->m_nMax;
                                            sal_uLong nAbsMinTableCnts = pChild->m_nMin;

                                            // A fixed table width is taken over as minimum and
                                            // maximum at the same time
                                            if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
                                            {
                                                sal_uLong nTabWidth = pChild->m_nWidthOption;
                                                if( nTabWidth >= nAbsMinTableCnts  )
                                                {
                                                    nMaxTableCnts = nTabWidth;
                                                    nAbsMinTableCnts = nTabWidth;
                                                }
                                                else
                                                {
                                                    nMaxTableCnts = nAbsMinTableCnts;
                                                }
                                            }

                                            if( nMaxTableCnts > nMaxTableCell )
                                                nMaxTableCell = nMaxTableCnts;
                                            if( nAbsMinTableCnts > nAbsMinTableCell )
                                                nAbsMinTableCell = nAbsMinTableCnts;
                                        }
                                        nIdx = pTabNd->EndOfSectionNode()->GetIndex();
                                    }
                                }
                                nIdx++;
                            }
                        }
                        else if (SwHTMLTableLayout *pChild = pCnts->GetTable())
                        {
                            OSL_ENSURE( false, "Sub tables in HTML import?" );
                            pChild->AutoLayoutPass1();
                            sal_uLong nMaxTableCnts = pChild->m_nMax;
                            sal_uLong nAbsMinTableCnts = pChild->m_nMin;

                            // A fixed table width is taken over as minimum and
                            // maximum at the same time
                            if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
                            {
                                sal_uLong nTabWidth = pChild->m_nWidthOption;
                                if( nTabWidth >= nAbsMinTableCnts  )
                                {
                                    nMaxTableCnts = nTabWidth;
                                    nAbsMinTableCnts = nTabWidth;
                                }
                                else
                                {
                                    nMaxTableCnts = nAbsMinTableCnts;
                                }
                            }

                            if( nMaxTableCnts > nMaxTableCell )
                                nMaxTableCell = nMaxTableCnts;
                            if( nAbsMinTableCnts > nAbsMinTableCell )
                                nAbsMinTableCell = nAbsMinTableCnts;
                        }
                        pCnts->SetPass1Done( m_nPass1Done );
                        pCnts = pCnts->GetNext().get();
                    }

    // This code previously came after AddBorderWidth
                    // If a table's width is wider in a cell than what we've calculated
                    // for the other content we need to use the table's width.
                    if( nMaxTableCell > nMaxNoAlignCell )
                        nMaxNoAlignCell = nMaxTableCell;
                    if( nAbsMinTableCell > nAbsMinNoAlignCell )
                    {
                        nAbsMinNoAlignCell = nAbsMinTableCell;
                        if( nMinNoAlignCell < nAbsMinNoAlignCell )
                            nMinNoAlignCell = nAbsMinNoAlignCell;
                        if( nMaxNoAlignCell < nMinNoAlignCell )
                            nMaxNoAlignCell = nMinNoAlignCell;
                    }
    // This code previously came after AddBorderWidth

                    bool bRelWidth = pCell->IsPrcWidthOption();
                    sal_uInt16 nWidth = pCell->GetWidthOption();

                    // A NOWRAP option applies to text and tables, but is
                    // not applied for fixed cell width.
                    // Instead, the stated cell width behaves like a minimal
                    // width.
                    if( pCell->HasNoWrapOption() )
                    {
                        if( nWidth==0 || bRelWidth )
                        {
                            nMinNoAlignCell = nMaxNoAlignCell;
                            nAbsMinNoAlignCell = nMaxNoAlignCell;
                        }
                        else
                        {
                            if( nWidth>nMinNoAlignCell )
                                nMinNoAlignCell = nWidth;
                            if( nWidth>nAbsMinNoAlignCell )
                                nAbsMinNoAlignCell = nWidth;
                        }
                    }

                    // Respect minimum width for content
                    if( nMinNoAlignCell < MINLAY )
                        nMinNoAlignCell = MINLAY;
                    if( nMaxNoAlignCell < MINLAY )
                        nMaxNoAlignCell = MINLAY;
                    if( nAbsMinNoAlignCell < MINLAY )
                        nAbsMinNoAlignCell = MINLAY;

                    // Respect the border and distance to the content
                    AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell,
                                    nAbsMinNoAlignCell, i, nColSpan );

                    if( 1==nColSpan )
                    {
                        // take over the values directly
                        pColumn->MergeMinMaxNoAlign( nMinNoAlignCell,
                                                     nMaxNoAlignCell,
                                                     nAbsMinNoAlignCell );

                        // the widest WIDTH wins
                        if( !HasColTags() )
                            pColumn->MergeCellWidthOption( nWidth, bRelWidth );
                    }
                    else
                    {
                        // Process the data line by line from left to right at the end

                        // When which values is taken over will be explained further down.
                        if( !HasColTags() && nWidth && !bRelWidth )
                        {
                            sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0;
                            AddBorderWidth( nAbsWidth, nDummy, nDummy2,
                                            i, nColSpan, false );

                            if( nAbsWidth >= nMinNoAlignCell )
                            {
                                nMaxNoAlignCell = nAbsWidth;
                                if( HasColsOption() )
                                    nMinNoAlignCell = nAbsWidth;
                            }
                            else if( nAbsWidth >= nAbsMinNoAlignCell )
                            {
                                nMaxNoAlignCell = nAbsWidth;
                                nMinNoAlignCell = nAbsWidth;
                            }
                            else
                            {
                                nMaxNoAlignCell = nAbsMinNoAlignCell;
                                nMinNoAlignCell = nAbsMinNoAlignCell;
                            }
                        }
                        else if( HasColsOption() || HasColTags() )
                            nMinNoAlignCell = nAbsMinNoAlignCell;

                        SwHTMLTableLayoutConstraints *pConstr =
                            new SwHTMLTableLayoutConstraints( nMinNoAlignCell,
                                nMaxNoAlignCell, j, i, nColSpan );
                        if (xConstraints)
                        {
                            SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr);
                            xConstraints.release();
                            xConstraints.reset(pConstraints);
                        }
                        else
                            xConstraints.reset(pConstr);
                    }
                }
            }

            OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan,
                    "Layout pass 1: Columns are being forgotten!" );
            OSL_ENSURE( nMinColSpan!=USHRT_MAX,
                    "Layout pass 1: unnecessary pass through the loop or a bug" );

            if( 1==nMinColSpan )
            {
                // There are cells with COLSPAN 1 and therefore also useful
                // values in pColumn

                // Take over values according to the following table (Netscape 4.0 pv 3):

                // WIDTH:           no COLS         COLS

                // none             min = min       min = absmin
                //                  max = max       max = max

                // >= min           min = min       min = width
                //                  max = width     max = width

                // >= absmin        min = width(*)  min = width
                //                  max = width     max = width

                // < absmin         min = absmin    min = absmin
                //                  max = absmin    max = absmin

                // (*) Netscape uses the minimum width without a break before
                //     the last graphic here. We don't have that (yet?),
                //     so we leave it set to width.

                if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() )
                {
                    // Take over absolute widths as minimal and maximal widths.
                    sal_uLong nAbsWidth = pColumn->GetWidthOption();
                    sal_uLong nDummy = 0, nDummy2 = 0;
                    AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false );

                    if( nAbsWidth >= pColumn->GetMinNoAlign() )
                    {
                        pColumn->SetMinMax( HasColsOption() ? nAbsWidth
                                                       : pColumn->GetMinNoAlign(),
                                            nAbsWidth );
                    }
                    else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() )
                    {
                        pColumn->SetMinMax( nAbsWidth, nAbsWidth );
                    }
                    else
                    {
                        pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(),
                                            pColumn->GetAbsMinNoAlign() );
                    }
                }
                else
                {
                    pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign()
                                                   : pColumn->GetMinNoAlign(),
                                        pColumn->GetMaxNoAlign() );
                }
            }
            else if( USHRT_MAX!=nMinColSpan )
            {
                // Can be anything != 0, because it is altered by the constraints.
                pColumn->SetMinMax( MINLAY, MINLAY );

                // the next columns need not to be processed
                i += (nColSkip-1);
            }

            m_nMin += pColumn->GetMin();
            m_nMax += pColumn->GetMax();
            if (pColumn->IsRelWidthOption()) bFixRelWidths = true;
        }

        // Now process the constraints
        SwHTMLTableLayoutConstraints *pConstr = xConstraints.get();
        while( pConstr )
        {
            // At first we need to process the width in the same way
            // as the column widths
            sal_uInt16 nCol = pConstr->GetColumn();
            sal_uInt16 nColSpan = pConstr->GetColSpan();
            sal_uLong nConstrMin = pConstr->GetMinNoAlign();
            sal_uLong nConstrMax = pConstr->GetMaxNoAlign();

            // We get the hitherto width of the spanned columns
            sal_uLong nColsMin = 0;
            sal_uLong nColsMax = 0;
            for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ )
            {
                SwHTMLTableLayoutColumn *pColumn = GetColumn( j );
                nColsMin += pColumn->GetMin();
                nColsMax += pColumn->GetMax();
            }

            if( nColsMin<nConstrMin )
            {
                // Proportionately distribute the minimum value to the columns
                sal_uLong nMinD = nConstrMin-nColsMin;

                if( nConstrMin > nColsMax )
                {
                    // Proportional according to the minimum widths
                    sal_uInt16 nEndCol = nCol+nColSpan;
                    sal_uLong nDiff = nMinD;
                    for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ )
                    {
                        SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );

                        sal_uLong nColMin = pColumn->GetMin();
                        sal_uLong nColMax = pColumn->GetMax();

                        m_nMin -= nColMin;
                        sal_uLong nAdd;
                        if (ic < nEndCol-1)
                        {
                            if (nColsMin == 0)
                                throw o3tl::divide_by_zero();
                            nAdd = (nColMin * nMinD) / nColsMin;
                        }
                        else
                        {
                            nAdd = nDiff;
                        }
                        nColMin += nAdd;
                        m_nMin += nColMin;
                        OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" );
                        nDiff -= nAdd;

                        if( nColMax < nColMin )
                        {
                            m_nMax -= nColMax;
                            nColsMax -= nColMax;
                            nColMax = nColMin;
                            m_nMax += nColMax;
                            nColsMax += nColMax;
                        }

                        pColumn->SetMinMax( nColMin, nColMax );
                    }
                }
                else
                {
                    // Proportional according to the difference of max and min
                    for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
                    {
                        SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );

                        sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin();
                        if( nMinD < nDiff )
                            nDiff = nMinD;

                        pColumn->AddToMin( nDiff );

                        OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
                                "Why is the Column suddenly too narrow?" );

                        m_nMin += nDiff;
                        nMinD -= nDiff;
                    }
                }
            }

            if( !HasColTags() && nColsMax<nConstrMax )
            {
                sal_uLong nMaxD = nConstrMax-nColsMax;

                for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );

                    m_nMax -= pColumn->GetMax();

                    pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax );

                    m_nMax += pColumn->GetMax();
                }
            }

            pConstr = pConstr->GetNext();
        }

        if( bFixRelWidths )
        {
            if( HasColTags() )
            {
                // To adapt the relative widths, in a first step we multiply the
                // minimum width of all affected cells with the relative width
                // of the column.
                // Thus, the width ratio among the columns is correct.

                // Furthermore, a factor is calculated that says by how much the
                // cell has gotten wider than the minimum width.

                // In the second step the calculated widths are divided by this
                // factor.  Thereby a cell's width is preserved and serves as a
                // basis for the other cells.
                // We only change the maximum widths here!

                sal_uLong nAbsMin = 0;  // absolute minimum width of all widths with relative width
                sal_uLong nRel = 0;     // sum of all relative widths of all columns
                for( i=0; i<m_nCols; i++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
                    {
                        nAbsMin += pColumn->GetMin();
                        nRel += pColumn->GetWidthOption();
                    }
                }

                sal_uLong nQuot = ULONG_MAX;
                for( i=0; i<m_nCols; i++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() )
                    {
                        m_nMax -= pColumn->GetMax();
                        if( pColumn->GetWidthOption() && pColumn->GetMin() )
                        {
                            pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
                            sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
                            if( nColQuot<nQuot )
                                nQuot = nColQuot;
                        }
                    }
                }
                OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX,
                        "Where did the relative columns go?" );
                for( i=0; i<m_nCols; i++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() )
                    {
                        if( pColumn->GetWidthOption() )
                            pColumn->SetMax( pColumn->GetMax() / nQuot );
                        else
                            pColumn->SetMax( pColumn->GetMin() );
                        OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
                                "Maximum column width is lower than the minimum column width" );
                        m_nMax += pColumn->GetMax();
                    }
                }
            }
            else
            {
                sal_uInt16 nRel = 0;        // sum of the relative widths of all columns
                sal_uInt16 nRelCols = 0;    // count of the columns with a relative setting
                sal_uLong nRelMax = 0;      // fraction of the maximum of this column
                for( i=0; i<m_nCols; i++ )
                {
                    OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" );
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
                    {
                        // Make sure that the relative widths don't go above 100%
                        sal_uInt16 nColWidth = pColumn->GetWidthOption();
                        if( nRel+nColWidth > 100 )
                        {
                            nColWidth = 100 - nRel;
                            pColumn->SetWidthOption( nColWidth );
                        }
                        nRelMax += pColumn->GetMax();
                        nRel = nRel + nColWidth;
                        nRelCols++;
                    }
                    else if( !pColumn->GetMin() )
                    {
                        // The column is empty (so it was solely created by
                        // COLSPAN) and therefore must not be assigned a % width.
                        nRelCols++;
                    }
                }

                // If there are percentages left we distribute them to the columns
                // that don't have a width setting. Like in Netscape we distribute
                // the remaining percentages according to the ratio of the maximum
                // width of the affected columns.
                // For the maximum widths we also take the fixed-width columns
                // into account.  Is that correct?
                sal_uLong nFixMax = 0;
                if( nRel < 100 && nRelCols < m_nCols )
                {
                    nFixMax = m_nMax - nRelMax;
                    SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max");
                }
                if (nFixMax)
                {
                    sal_uInt16 nRelLeft = 100 - nRel;
                    for( i=0; i<m_nCols; i++ )
                    {
                        SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                        if( !pColumn->IsRelWidthOption() &&
                            !pColumn->GetWidthOption() &&
                            pColumn->GetMin() )
                        {
                            // the next column gets the rest
                            sal_uInt16 nColWidth =
                                static_cast<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax);
                            pColumn->SetWidthOption( nColWidth );
                        }
                    }
                }

                // adjust the maximum widths now accordingly
                sal_uLong nQuotMax = ULONG_MAX;
                sal_uLong nOldMax = m_nMax;
                m_nMax = 0;
                for( i=0; i<m_nCols; i++ )
                {
                    // Columns with a % setting are adapted accordingly.
                    // Columns, that
                    // - do not have a % setting and are located within a tables
                    // with COLS and WIDTH, or
                    // - their width is 0%
                    // get set to the minimum width.
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
                    {
                        sal_uLong nNewMax;
                        sal_uLong nColQuotMax;
                        if( !m_nWidthOption )
                        {
                            nNewMax = nOldMax * pColumn->GetWidthOption();
                            nColQuotMax = nNewMax / pColumn->GetMax();
                        }
                        else
                        {
                            nNewMax = m_nMin * pColumn->GetWidthOption();
                            nColQuotMax = nNewMax / pColumn->GetMin();
                        }
                        pColumn->SetMax( nNewMax );
                        if( nColQuotMax < nQuotMax )
                            nQuotMax = nColQuotMax;
                    }
                    else if( HasColsOption() || m_nWidthOption ||
                             (pColumn->IsRelWidthOption() &&
                              !pColumn->GetWidthOption()) )
                        pColumn->SetMax( pColumn->GetMin() );
                }
                // and divide by the quotient
                SAL_WARN_IF(nQuotMax != ULONG_MAX && !nQuotMax, "sw.core", "Where did the relative columns go?");
                for (i = 0; i < m_nCols; ++i)
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax)
                    {
                        if( pColumn->GetWidthOption() )
                        {
                            pColumn->SetMax( pColumn->GetMax() / nQuotMax );
                            OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
                                    "Minimum width is one column bigger than maximum" );
                            if( pColumn->GetMax() < pColumn->GetMin() )
                                pColumn->SetMax( pColumn->GetMin() );
                        }
                    }
                    m_nMax += pColumn->GetMax();
                }
            }
        }
    }

    //TODO: provide documentation
    /**

        @param nAbsAvail available space in TWIPS.
        @param nRelAvail available space related to USHRT_MAX or 0
        @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
                         cell for the border and the distance to the paragraph.
    */
    void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail,
                                             sal_uInt16 nAbsLeftSpace,
                                             sal_uInt16 nAbsRightSpace,
                                             sal_uInt16 nParentInhAbsSpace )
    {
        // For a start we do a lot of plausibility tests

        // An absolute width always has to be passed
        OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" );

        // A relative width must only be passed for tables within tables (?)
        OSL_ENSURE( IsTopTable() == (nRelAvail==0),
                "AutoLayout pass 2: Relative width at table in table or the other way around" );

        // The table's minimum width must not be bigger than its maximum width
        OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" );

        // Remember the available width for which the table was calculated.
        // This is a good place as we pass by here for the initial calculation
        // of the table in the parser and for each Resize_ call.
        m_nLastResizeAbsAvail = nAbsAvail;

        // Step 1: The available space is readjusted for the left/right border,
        // possibly existing filler cells and distances.

        // Distance to the content and border
        sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0;
        if( !IsTopTable() &&
            GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail )
        {
            nAbsLeftFill = nAbsLeftSpace;
            nAbsRightFill = nAbsRightSpace;
        }

        // Left and right distance
        if( m_nLeftMargin || m_nRightMargin )
        {
            if( IsTopTable() )
            {
                // For the top table we always respect the borders, because we
                // never go below the table's minimum width.
                nAbsAvail -= (m_nLeftMargin + m_nRightMargin);
            }
            else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail )
            {
                // Else, we only respect the borders if there's space available
                // for them (nMin has already been calculated!)
                nAbsLeftFill = nAbsLeftFill + m_nLeftMargin;
                nAbsRightFill = nAbsRightFill + m_nRightMargin;
            }
        }

        // Read just the available space
        m_nRelLeftFill = 0;
        m_nRelRightFill = 0;
        if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) )
        {
            sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;

            m_nRelLeftFill = static_cast<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail);
            m_nRelRightFill = static_cast<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail);

            nAbsAvail -= (nAbsLeftFill + nAbsRightFill);
            if( nRelAvail )
                nRelAvail -= (m_nRelLeftFill + m_nRelRightFill);
        }

        // Step 2: Calculate the absolute table width.
        sal_uInt16 nAbsTabWidth = 0;
        m_bUseRelWidth = false;
        if( m_nWidthOption )
        {
            if( m_bPrcWidthOption )
            {
                OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" );
                if( m_nWidthOption > 100 )
                    m_nWidthOption = 100;

                // The absolute width is equal to the given percentage of
                // the available width.
                // Top tables only get a relative width if the available space
                // is *strictly larger* than the minimum width.

                // CAUTION: We need the "strictly larger" because changing from a
                // relative width to an absolute width by resizing would lead
                // to an infinite loop.

                // Because we do not call resize for tables in frames if the
                // frame has a non-relative width, we cannot play such games.

                // Let's play such games now anyway. We had a graphic in a 1% wide
                // table and it didn't fit in of course.
                nAbsTabWidth = static_cast<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 );
                if( IsTopTable() &&
                    ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) )
                {
                    nRelAvail = USHRT_MAX;
                    m_bUseRelWidth = true;
                }
            }
            else
            {
                nAbsTabWidth = m_nWidthOption;
                if( nAbsTabWidth > MAX_TABWIDTH )
                    nAbsTabWidth = MAX_TABWIDTH;

                // Tables within tables must never get wider than the available
                // space.
                if( !IsTopTable() && nAbsTabWidth > nAbsAvail )
                    nAbsTabWidth = nAbsAvail;
            }
        }

        OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail,
                "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
        OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail,
                "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );

        // Catch for the two asserts above (we never know!)
        if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail )
            nAbsTabWidth = nAbsAvail;

        // Step 3: Identify the column width and, if applicable, the absolute
        // and relative table widths.
        if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) ||
            m_nMin > MAX_TABWIDTH )
        {
            // If
            // - a inner table's minimum is larger than the available space, or
            // - a top table's minimum is larger than USHORT_MAX the table
            // has to be adapted to the available space or USHORT_MAX.
            // We preserve the widths' ratio amongst themselves, however.

            nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail;
            m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth );

            // First of all, we check whether we can fit the layout constrains,
            // which are: Every cell's width excluding the borders must be at least
            // MINLAY:

            sal_uLong nRealMin = 0;
            for( sal_uInt16 i=0; i<m_nCols; i++ )
            {
                sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
                AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
                nRealMin += nRealColMin;
            }
            if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) )
            {
                // "Rien ne va plus": we cannot get the minimum column widths
                // the layout wants to have.

                sal_uInt16 nAbs = 0, nRel = 0;
                SwHTMLTableLayoutColumn *pColumn;
                for( sal_uInt16 i=0; i<m_nCols-1; i++ )
                {
                    pColumn = GetColumn( i );
                    sal_uLong nColMin = pColumn->GetMin();
                    if( nColMin <= USHRT_MAX )
                    {
                        pColumn->SetAbsColWidth(
                            static_cast<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) );
                        pColumn->SetRelColWidth(
                            static_cast<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) );
                    }
                    else
                    {
                        double nColMinD = nColMin;
                        pColumn->SetAbsColWidth(
                            static_cast<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) );
                        pColumn->SetRelColWidth(
                            static_cast<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) );
                    }

                    nAbs = nAbs + pColumn->GetAbsColWidth();
                    nRel = nRel + pColumn->GetRelColWidth();
                }
                pColumn = GetColumn( m_nCols-1 );
                pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
                pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
            }
            else
            {
                sal_uLong nDistAbs = nAbsTabWidth - nRealMin;
                sal_uLong nDistRel = m_nRelTabWidth - nRealMin;
                sal_uLong nDistMin = m_nMin - nRealMin;
                sal_uInt16 nAbs = 0, nRel = 0;
                SwHTMLTableLayoutColumn *pColumn;
                for( sal_uInt16 i=0; i<m_nCols-1; i++ )
                {
                    pColumn = GetColumn( i );
                    sal_uLong nColMin = pColumn->GetMin();
                    sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
                    AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );

                    if( nColMin <= USHRT_MAX )
                    {
                        pColumn->SetAbsColWidth(
                            static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
                        pColumn->SetRelColWidth(
                            static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
                    }
                    else
                    {
                        double nColMinD = nColMin;
                        pColumn->SetAbsColWidth(
                            static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
                        pColumn->SetRelColWidth(
                            static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
                    }

                    nAbs = nAbs + pColumn->GetAbsColWidth();
                    nRel = nRel + pColumn->GetRelColWidth();
                }
                pColumn = GetColumn( m_nCols-1 );
                pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
                pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
            }
        }
        else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) )
        {
            // If
            // - the table has a fixed width and the table's maximum is
            //   smaller, or
            //- the maximum is smaller than the available space,
            // we can take over the maximum as it is. Respectively
            // the table can only be adapted to the fixed width by
            // respecting the maximum.

            // No fixed width, use the maximum.
            if( !nAbsTabWidth )
                nAbsTabWidth = static_cast<sal_uInt16>(m_nMax);

            // A top table may also get wider then the available space.
            if( nAbsTabWidth > nAbsAvail )
            {
                OSL_ENSURE( IsTopTable(),
                        "Table in table should get wider than the surrounding cell." );
                nAbsAvail = nAbsTabWidth;
            }

            // Only use the relative widths' fraction, that is used for the
            // absolute width.
            sal_uLong nAbsTabWidthL = nAbsTabWidth;
            if (nRelAvail)
            {
                if (nAbsAvail == 0)
                    throw o3tl::divide_by_zero();
                m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
            }
            else
                m_nRelTabWidth = nAbsTabWidth;

            // Are there columns width a percentage setting and some without one?
            sal_uLong nFixMax = m_nMax;
            for( sal_uInt16 i=0; i<m_nCols; i++ )
            {
                const SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 )
                    nFixMax -= pColumn->GetMax();
            }

            if( nFixMax > 0 && nFixMax < m_nMax )
            {
                // Yes, distribute the to-be-distributed space only to the
                // columns with a percentage setting.

                // In this case (and in this case only) there are columns
                // that exactly keep their maximum width, that is they neither
                // get smaller nor wider. When calculating the absolute width
                // from the relative width we can get rounding errors.
                // To correct this, we first make the fixed widths compensate for
                // this error. We then fix the relative widths the same way.

                sal_uInt16 nAbs = 0, nRel = 0;
                sal_uInt16 nFixedCols = 0;
                sal_uInt16 i;

                for( i = 0; i < m_nCols; i++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() )
                    {
                        // The column keeps its width.
                        nFixedCols++;
                        sal_uLong nColMax = pColumn->GetMax();
                        pColumn->SetAbsColWidth( static_cast<sal_uInt16>(nColMax) );

                        sal_uLong nRelColWidth =
                            (nColMax * m_nRelTabWidth) / nAbsTabWidth;
                        sal_uLong nChkWidth =
                            (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth;
                        if( nChkWidth < nColMax )
                            nRelColWidth++;
                        else if( nChkWidth > nColMax )
                            nRelColWidth--;
                        pColumn->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );

                        nAbs = nAbs + static_cast<sal_uInt16>(nColMax);
                        nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
                    }
                }

                // The to-be-distributed percentage of the maximum, the
                // relative and absolute widths. Here, nFixMax corresponds
                // to nAbs, so that we could've called it nAbs.
                // The code is, however, more readable like that.
                OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" );
                sal_uLong nDistMax = m_nMax - nFixMax;
                sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs;
                sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel;

                for( i=0; i<m_nCols; i++ )
                {
                    SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
                    if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 )
                    {
                        // The column gets proportionately wider.
                        nFixedCols++;
                        if( nFixedCols == m_nCols )
                        {
                            pColumn->SetAbsColWidth( nAbsTabWidth-nAbs );
                            pColumn->SetRelColWidth( m_nRelTabWidth-nRel );
                        }
                        else
                        {
                            sal_uLong nColMax = pColumn->GetMax();
                            pColumn->SetAbsColWidth(
                                static_cast<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) );
                            pColumn->SetRelColWidth(
                                static_cast<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) );
                        }
                        nAbs = nAbs + pColumn->GetAbsColWidth();
                        nRel = nRel + pColumn->GetRelColWidth();
                    }
                }
                OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" );
            }
            else if (m_nCols > 0)
            {
                if (m_nMax == 0)
                    throw o3tl::divide_by_zero();
                // No. So distribute the space regularly among all columns.
                for (sal_uInt16 i=0; i < m_nCols; ++i)
                {
                    sal_uLong nColMax = GetColumn( i )->GetMax();
                    GetColumn( i )->SetAbsColWidth(
                        static_cast<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) );
                    GetColumn( i )->SetRelColWidth(
                        static_cast<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) );
                }
            }
        }
        else
        {
            // Proportionately distribute the space that extends over the minimum
            // width among the columns.
            if( !nAbsTabWidth )
                nAbsTabWidth = nAbsAvail;
            if( nAbsTabWidth < m_nMin )
                nAbsTabWidth = static_cast<sal_uInt16>(m_nMin);

            if( nAbsTabWidth > nAbsAvail )
            {
                OSL_ENSURE( IsTopTable(),
                        "A nested table should become wider than the available space." );
                nAbsAvail = nAbsTabWidth;
            }

            sal_uLong nAbsTabWidthL = nAbsTabWidth;
            if (nRelAvail)
            {
                if (nAbsAvail == 0)
                    throw o3tl::divide_by_zero();
                m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
            }
            else
                m_nRelTabWidth = nAbsTabWidth;
            double nW = nAbsTabWidth - m_nMin;
            double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin);
            sal_uInt16 nAbs = 0, nRel = 0;
            for( sal_uInt16 i=0; i<m_nCols-1; i++ )
            {
                double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin();
                sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD);
                sal_uLong nRelColWidth = nRelAvail
                                        ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
                                        : nAbsColWidth;

                GetColumn( i )->SetAbsColWidth( static_cast<sal_uInt16>(nAbsColWidth) );
                GetColumn( i )->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );
                nAbs = nAbs + static_cast<sal_uInt16>(nAbsColWidth);
                nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
            }
            GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs );
            GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel );

        }

        // Step 4: For nested tables we can have balancing cells on the
        // left or right. Here we calculate their width.
        m_nInhAbsLeftSpace = 0;
        m_nInhAbsRightSpace = 0;
        if( !IsTopTable() && (m_nRelLeftFill>0 || m_nRelRightFill>0 ||
                              nAbsTabWidth<nAbsAvail) )
        {
            // Calculate the width of additional cells we use for
            // aligning inner tables.
            sal_uInt16 nAbsDist = static_cast<sal_uInt16>(nAbsAvail-nAbsTabWidth);
            sal_uInt16 nRelDist = static_cast<sal_uInt16>(nRelAvail-m_nRelTabWidth);
            sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0;

            // Calculate the size and position of the additional cells.
            switch( m_eTableAdjust )
            {
            case SvxAdjust::Right:
                nAbsLeftFill = nAbsLeftFill + nAbsDist;
                m_nRelLeftFill = m_nRelLeftFill + nRelDist;
                nParentInhAbsLeftSpace = nParentInhAbsSpace;
                break;
            case SvxAdjust::Center:
                {
                    sal_uInt16 nAbsLeftDist = nAbsDist / 2;
                    nAbsLeftFill = nAbsLeftFill + nAbsLeftDist;
                    nAbsRightFill += nAbsDist - nAbsLeftDist;
                    sal_uInt16 nRelLeftDist = nRelDist / 2;
                    m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist;
                    m_nRelRightFill += nRelDist - nRelLeftDist;
                    nParentInhAbsLeftSpace = nParentInhAbsSpace / 2;
                    nParentInhAbsRightSpace = nParentInhAbsSpace -
                                              nParentInhAbsLeftSpace;
                }
                break;
            case SvxAdjust::Left:
            default:
                nAbsRightFill = nAbsRightFill + nAbsDist;
                m_nRelRightFill = m_nRelRightFill + nRelDist;
                nParentInhAbsRightSpace = nParentInhAbsSpace;
                break;
            }

            // Filler widths are added to the outer columns, if there are no boxes
            // for them after the first pass (nWidth>0) or their width would become
            // too small or if there are COL tags and the filler width corresponds
            // to the border width.
            // In the last case we probably exported the table ourselves.
            if( m_nRelLeftFill &&
                ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth ||
                  (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) )
            {
                SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 );
                pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
                pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
                m_nRelLeftFill = 0;
                m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
            }
            if( m_nRelRightFill &&
                ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth ||
                  (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) )
            {
                SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 );
                pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
                pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
                m_nRelRightFill = 0;
                m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
            }
        }
    }

    static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth );

    static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth )
    {
        if( !pBox->GetSttNd() )
        {
            SwTwips nWidth = 0;
            for( const SwTableLine *pLine : pBox->GetTabLines() )
                lcl_ResizeLine( pLine, &nWidth );
            pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nWidth, 0 ));
            *pWidth = *pWidth + nWidth;
        }
        else
        {
            *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
        }
    }

    static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth )
    {
        SwTwips nOldWidth = *pWidth;
        *pWidth = 0;
        for( const SwTableBox* pBox : pLine->GetTabBoxes() )
            lcl_ResizeBox(pBox, pWidth );

        SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core",
                     "A box's rows have all a different length" );
    }

    void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail,
                                       sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
                                       sal_uInt16 nAbsRightSpace,
                                       sal_uInt16 nParentInhAbsSpace )
    {
        // SetWidth must have been passed through once more for every cell in the
        // end.
        m_nWidthSet++;

        // Step 0: If necessary, we call the layout algorithm of Pass2.
        if( bCallPass2 )
            AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace,
                             nParentInhAbsSpace );

        // Step 1: Set the new width in all content boxes.
        // Because the boxes don't know anything about the HTML table structure,
        // we iterate over the HTML table structure.
        // For tables in tables in tables we call SetWidth recursively.
        for( sal_uInt16 i=0; i<m_nRows; i++ )
        {
            for( sal_uInt16 j=0; j<m_nCols; j++ )
            {
                SwHTMLTableLayoutCell *pCell = GetCell( i, j );

                SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get();
                while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
                {
                    SwTableBox *pBox = pContents->GetTableBox();
                    if( pBox )
                    {
                        SetBoxWidth( pBox, j, pCell->GetColSpan() );
                    }
                    else if (SwHTMLTableLayout *pTable = pContents->GetTable())
                    {
                        sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0,
                               nInhSpace = 0;
                        if( bCallPass2 )
                        {
                            sal_uInt16 nColSpan = pCell->GetColSpan();
                            GetAvail( j, nColSpan, nAbs, nRel );
                            nLSpace = GetLeftCellSpace( j, nColSpan );
                            nRSpace = GetRightCellSpace( j, nColSpan );
                            nInhSpace = GetInhCellSpace( j, nColSpan );
                        }
                        pTable->SetWidths( bCallPass2, nAbs, nRel,
                                                        nLSpace, nRSpace,
                                                        nInhSpace );
                    }

                    pContents->SetWidthSet( m_nWidthSet );
                    pContents = pContents->GetNext().get();
                }
            }
        }

        // Step 2: If we have a top table, we adapt the formats of the
        // non-content-boxes. Because they are not known in the HTML table
        // due to garbage collection there, we need the iterate over the
        // whole table.
        // We also adapt the table frame format. For nested tables we set the
        // filler cell's width instead.
        if( IsTopTable() )
        {
            SwTwips nCalcTabWidth = 0;
            for( const SwTableLine *pLine : m_pSwTable->GetTabLines() )
                lcl_ResizeLine( pLine, &nCalcTabWidth );
            SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core",
                         "Table width is not equal to the row width" );

            // Lock the table format when altering it, or else the box formats
            // are altered again.
            // Also, we need to preserve a percent setting if it exists.
            SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
            const_cast<SwTable *>(m_pSwTable)->LockModify();
            SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
            aFrameSize.SetWidth( m_nRelTabWidth );
            bool bRel = m_bUseRelWidth &&
                        text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
            aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) );
            pFrameFormat->SetFormatAttr( aFrameSize );
            const_cast<SwTable *>(m_pSwTable)->UnlockModify();

            // If the table is located in a frame, we also need to adapt the
            // frame's width.
            if( MayBeInFlyFrame() )
            {
                SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat();
                if( pFlyFrameFormat )
                {
                    SwFormatFrameSize aFlyFrameSize( ATT_VAR_SIZE, m_nRelTabWidth, MINLAY );

                    if( m_bUseRelWidth )
                    {
                        // For percentage settings we set the width to the minimum.
                        aFlyFrameSize.SetWidth(  m_nMin > USHRT_MAX ? USHRT_MAX
                                                                : m_nMin );
                        aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) );
                    }
                    pFlyFrameFormat->SetFormatAttr( aFlyFrameSize );
                }
            }

    #ifdef DBG_UTIL
            {
                // check if the tables have correct widths
                SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth();
                const SwTableLines& rLines = m_pSwTable->GetTabLines();
                for (size_t n = 0; n < rLines.size(); ++n)
                {
                    CheckBoxWidth( *rLines[ n ], nSize );
                }
            }
    #endif

        }
    }

    void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc )
    {
        // If bRecalc is set, the table's content changed.
        // We need to execute pass 1 again.
        if( bRecalc )
            AutoLayoutPass1();

        SwRootFrame *pRoot = GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
        if ( pRoot && pRoot->IsCallbackActionEnabled() )
            pRoot->StartAllAction();

        // Else we can set the widths, in which we have to run Pass 2 in each case.
        SetWidths( true, nAbsAvail );

        if ( pRoot && pRoot->IsCallbackActionEnabled() )
            pRoot->EndAllAction( true );    //True per VirDev (browsing is calmer)
    }

    IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void )
    {
        m_aResizeTimer.Stop();
        Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc );
    }

    bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc,
                                    bool bForce, sal_uLong nDelay )
    {
        if( 0 == nAbsAvail )
            return false;
        OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );

        // May the table be resized at all? Or is it forced?
        if( m_bMustNotResize && !bForce )
            return false;

        // May the table be recalculated? Or is it forced?
        if( m_bMustNotRecalc && !bForce )
            bRecalc = false;

        const SwDoc *pDoc = GetDoc();

        // If there is a layout, the root frame's size instead of the
        // VisArea's size was potentially passed.
        // If we're not in a frame we need to calculate the table for the VisArea,
        // because switching from relative to absolute wouldn't work.
        if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
        {
            const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( *pDoc );
            if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() )
                nAbsAvail = nVisAreaWidth;
        }

        if( nDelay==0 && m_aResizeTimer.IsActive() )
        {
            m_nDelayedResizeAbsAvail = nAbsAvail;
            return false;
        }

        // Optimisation:
        // If the minimum or maximum should not be recalculated and
        // - the table's width never needs to be recalculated, or
        // - the table was already calculated for the passed width, or
        // - the available space is less or equal to the minimum width
        //   and the table already has the minimum width, or
        // - the available space is larger than the maximum width and
        //   the table already has the maximum width
        // nothing will happen to the table.
        if( !bRecalc && ( !m_bMustResize ||
                          (m_nLastResizeAbsAvail==nAbsAvail) ||
                          (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) ||
                          (!m_bPrcWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) )
            return false;

        if( nDelay==HTMLTABLE_RESIZE_NOW )
        {
            if( m_aResizeTimer.IsActive() )
                m_aResizeTimer.Stop();
            Resize_( nAbsAvail, bRecalc );
        }
        else if( nDelay > 0 )
        {
            m_nDelayedResizeAbsAvail = nAbsAvail;
            m_bDelayedResizeRecalc = bRecalc;
            m_aResizeTimer.SetTimeout( nDelay );
            m_aResizeTimer.Start();
        }
        else
        {
            Resize_( nAbsAvail, bRecalc );
        }

        return true;
    }

    void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail )
    {
        m_bBordersChanged = true;

        Resize( nAbsAvail, true/*bRecalc*/ );
    }

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

↑ V571 Recurring check. The 'pColumn->GetWidthOption()' condition was already verified in line 1033.