/* -*- 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 #include #include #include #include #include #include #include "ndtxt.hxx" #include "doc.hxx" #include #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 #include #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 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 ); ~SwHTMLTableLayoutConstraints(); sal_uLong GetMinNoAlign() const { return nMinNoAlign; } sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; } SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt ); SwHTMLTableLayoutConstraints* GetNext() const { return pNext; } sal_uInt16 GetRow() const { return nRow; } sal_uInt16 GetColSpan() const { return nColSpan; } sal_uInt16 GetColumn() const { return nCol; } }; SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts( const SwStartNode *pSttNd, SwHTMLTableLayout* pTab, bool bNoBrTag, SwHTMLTableLayoutCnts* pNxt ) : pNext( pNxt ), pBox( nullptr ), pTable( pTab ), pStartNode( pSttNd ), nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag ) {} SwHTMLTableLayoutCnts::~SwHTMLTableLayoutCnts() { delete pNext; delete pTable; } const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const { return pBox ? pBox->GetSttNd() : pStartNode; } SwHTMLTableLayoutCell::SwHTMLTableLayoutCell( SwHTMLTableLayoutCnts *pCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan, sal_uInt16 nWidth, bool bPrcWidth, bool bNWrapOpt ) : pContents( pCnts ), nRowSpan( nRSpan ), nColSpan( nCSpan ), nWidthOption( nWidth ), bPrcWidthOption( bPrcWidth ), bNoWrapOption( bNWrapOpt ) {} SwHTMLTableLayoutCell::~SwHTMLTableLayoutCell() { if( nRowSpan==1 && nColSpan==1 ) { delete pContents; } } 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 ), pNext( nullptr ), nMinNoAlign( nMin ), nMaxNoAlign( nMax ) {} SwHTMLTableLayoutConstraints::~SwHTMLTableLayoutConstraints() { delete pNext; } SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext( SwHTMLTableLayoutConstraints *pNxt ) { SwHTMLTableLayoutConstraints *pPrev = nullptr; SwHTMLTableLayoutConstraints *pConstr = this; while( pConstr ) { if( pConstr->GetRow() > pNxt->GetRow() || pConstr->GetColumn() > pNxt->GetColumn() ) break; pPrev = pConstr; pConstr = pConstr->GetNext(); } if( pPrev ) { pNxt->pNext = pPrev->GetNext(); pPrev->pNext = pNxt; pConstr = this; } else { pNxt->pNext = 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, sal_uInt16 nInhLeftBWidth, sal_uInt16 nInhRightBWidth ) : m_aColumns( new SwHTMLTableLayoutColumn*[nCls] ) , m_aCells( new SwHTMLTableLayoutCell*[static_cast(nRws)*nCls] ) , m_pSwTable( pTable ) , m_pLeftFillerBox( nullptr ) , m_pRightFillerBox( nullptr ) , 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( nInhLeftBWidth ) , m_nInhRightBorderWidth( nInhRightBWidth ) , 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.SetTimeoutHdl( LINK( this, SwHTMLTableLayout, DelayedResize_Impl ) ); } SwHTMLTableLayout::~SwHTMLTableLayout() { sal_uInt16 i; for( i = 0; i < m_nCols; i++ ) delete m_aColumns[i]; delete[] m_aColumns; sal_uInt16 nCount = m_nRows*m_nCols; for( i=0; iHasLeftBorder() ) { 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; iGetAbsColWidth(); rRelAvail = rRelAvail + pColumn->GetRelColWidth(); } } sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc ) { SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell(); if( pVSh ) { return (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 (sal_uInt16)pPageFrame->Prt().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(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(pUpper)->GetAnchorFrame(); if( pAnchor->IsTextFrame() ) nWidth = pAnchor->Frame().Width(); else nWidth = pAnchor->Prt().Width(); } else { nWidth = pUpper->Prt().Width(); } SwTwips nUpperDummy = 0; long nRightOffset = 0, nLeftOffset = 0; rTabFrame.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset ); nWidth -= (nLeftOffset + nRightOffset); return nWidth < USHRT_MAX ? static_cast(nWidth) : USHRT_MAX; } sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const { sal_uInt16 nBrowseWidth = 0; SwTabFrame* pFrame = SwIterator( *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, "Kein Table-Node?" ); return pTableNd->GetFlyFormat(); } static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts, sal_uLong& rAbsMinNoAlignCnts, SwTextNode *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
 paragraph is the minimal width
    const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
    while( pColl && !pColl->IsDefault() &&
            (USER_FMT & pColl->GetPoolFormatId()) )
    {
        pColl = static_cast(pColl->DerivedFrom());
    }

    //  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;

    SwHTMLTableLayoutConstraints *pConstraints = nullptr;

    for( i=0; iClearPass1Info( !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; jGetContents();

            // 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
                    {
                        OSL_ENSURE( false, "Sub tables in HTML import?" );
                        SwHTMLTableLayout *pChild = pCnts->GetTable();
                        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();
                }

// 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( pConstraints )
                        pConstraints = pConstraints->InsertNext( pConstr );
                    else
                        pConstraints = 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 = pConstraints;
    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; jGetMin();
            nColsMax += pColumn->GetMax();
        }

        if( nColsMin nColsMax )
            {
                // Proportional according to the minimum widths
                sal_uInt16 nEndCol = nCol+nColSpan;
                sal_uLong nDiff = nMinD;
                for( sal_uInt16 ic=nCol; icGetMin();
                    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; icGetMax()-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() && nColsMaxGetMax();

                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; iIsRelWidthOption() && pColumn->GetWidthOption() )
                {
                    nAbsMin += pColumn->GetMin();
                    nRel += pColumn->GetWidthOption();
                }
            }

            sal_uLong nQuot = ULONG_MAX;
            for( i=0; iIsRelWidthOption() )
                {
                    m_nMax -= pColumn->GetMax();
                    if( pColumn->GetWidthOption() && pColumn->GetMin() )
                    {
                        pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
                        sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
                        if( nColQuotIsRelWidthOption() )
                {
                    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 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?
            if( nRel < 100 && nRelCols < m_nCols )
            {
                sal_uInt16 nRelLeft = 100 - nRel;
                sal_uLong nFixMax = m_nMax - nRelMax;
                for( i=0; iIsRelWidthOption() &&
                        !pColumn->GetWidthOption() &&
                        pColumn->GetMin() )
                    {
                        // the next column gets the rest
                        sal_uInt16 nColWidth =
                            (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; iIsRelWidthOption() && 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
            OSL_ENSURE( nQuotMax!=ULONG_MAX, "Where did the relative columns go?" );
            for( i=0; iIsRelWidthOption() && pColumn->GetWidthOption() )
                {
                    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();
            }
        }
    }

    delete pConstraints;
}

//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 plausability 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;
        }
    }

    // Filler cells
    if( !IsTopTable() )
    {
        if( m_pLeftFillerBox && nAbsLeftFill0 || nAbsRightFill) )
    {
        sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;

        m_nRelLeftFill = (sal_uInt16)((nAbsLeftFillL * nRelAvail) / nAbsAvail);
        m_nRelRightFill = (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 = (sal_uInt16)( ((sal_uLong)nAbsAvail * m_nWidthOption) / 100 );
            if( IsTopTable() &&
                ( /*MayBeInFlyFrame() ||*/ (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 > (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= 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; iGetMin();
                if( nColMin <= USHRT_MAX )
                {
                    pColumn->SetAbsColWidth(
                        (sal_uInt16)((nColMin * nAbsTabWidth) / m_nMin) );
                    pColumn->SetRelColWidth(
                        (sal_uInt16)((nColMin * m_nRelTabWidth) / m_nMin) );
                }
                else
                {
                    double nColMinD = nColMin;
                    pColumn->SetAbsColWidth(
                        (sal_uInt16)((nColMinD * nAbsTabWidth) / m_nMin) );
                    pColumn->SetRelColWidth(
                        (sal_uInt16)((nColMinD * m_nRelTabWidth) / m_nMin) );
                }

                nAbs = nAbs + (sal_uInt16)pColumn->GetAbsColWidth();
                nRel = nRel + (sal_uInt16)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; iGetMin();
                sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
                AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );

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

                nAbs = nAbs + (sal_uInt16)pColumn->GetAbsColWidth();
                nRel = nRel + (sal_uInt16)pColumn->GetRelColWidth();
            }
            pColumn = GetColumn( m_nCols-1 );
            pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
            pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
        }
    }
    else if( m_nMax <= (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 = (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 = (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; iIsRelWidthOption() && 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( (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( (sal_uInt16)nRelColWidth );

                    nAbs = nAbs + (sal_uInt16)nColMax;
                    nRel = nRel + (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; iIsRelWidthOption() && 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(
                            (sal_uInt16)((nColMax * nDistAbsTabWidth) / nDistMax) );
                        pColumn->SetRelColWidth(
                            (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(
                    (sal_uInt16)((nColMax * nAbsTabWidth) / m_nMax) );
                GetColumn( i )->SetRelColWidth(
                    (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 = (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 = (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; iGetMax() - GetColumn( i )->GetMin();
            sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + (sal_uLong)((nd*nW)/nD);
            sal_uLong nRelColWidth = nRelAvail
                                    ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
                                    : nAbsColWidth;

            GetColumn( i )->SetAbsColWidth( (sal_uInt16)nAbsColWidth );
            GetColumn( i )->SetRelColWidth( (sal_uInt16)nRelColWidth );
            nAbs = nAbs + (sal_uInt16)nAbsColWidth;
            nRel = nRel + (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 ||
                          nAbsTabWidth0,
                "We don't have a width for the left filler box!" );
        OSL_ENSURE( !m_pRightFillerBox || m_nRelRightFill>0,
                "We don't have a width for the right filler box!" );

        // 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_pLeftFillerBox &&
            ( m_nWidthSet>0 || nAbsLeftFillSetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
            pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
            m_nRelLeftFill = 0;
            m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
        }
        if( m_nRelRightFill && !m_pRightFillerBox &&
            ( m_nWidthSet>0 || nAbsRightFillSetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
            pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
            m_nRelRightFill = 0;
            m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
        }
    }
}

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

static void lcl_ResizeBox( const SwTableBox* pBox, sal_uInt16* pWidth )
{
    if( !pBox->GetSttNd() )
    {
        sal_uInt16 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 + (sal_uInt16)pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
    }
}

static void lcl_ResizeLine( const SwTableLine* pLine, sal_uInt16 *pWidth )
{
    sal_uInt16 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; iGetContents();
            while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
            {
                SwTableBox *pBox = pContents->GetTableBox();
                if( pBox )
                {
                    SetBoxWidth( pBox, j, pCell->GetColSpan() );
                }
                else
                {
                    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 );
                    }
                    pContents->GetTable()->SetWidths( bCallPass2, nAbs, nRel,
                                                    nLSpace, nRSpace,
                                                    nInhSpace );
                }

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

    // 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() )
    {
        sal_uInt16 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(m_pSwTable)->LockModify();
        SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
        aFrameSize.SetWidth( m_nRelTabWidth );
        bool bRel = m_bUseRelWidth &&
                    text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
        aFrameSize.SetWidthPercent( (sal_uInt8)(bRel ? m_nWidthOption : 0) );
        pFrameFormat->SetFormatAttr( aFrameSize );
        const_cast(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( (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

    }
    else
    {
        if( m_pLeftFillerBox )
        {
            m_pLeftFillerBox->GetFrameFormat()->SetFormatAttr(
                SwFormatFrameSize( ATT_VAR_SIZE, m_nRelLeftFill, 0 ));
        }
        if( m_pRightFillerBox )
        {
            m_pRightFillerBox->GetFrameFormat()->SetFormatAttr(
                SwFormatFrameSize( ATT_VAR_SIZE, m_nRelRightFill, 0 ));
        }
    }
}

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 minimums or maximums 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: */