1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
23 #include <algorithm>
24 #include <memory>
25 
26 #include <fmtornt.hxx>
27 #include <fmtfsize.hxx>
28 #include <frmfmt.hxx>
29 #include <ndtxt.hxx>
30 #include <doc.hxx>
31 #include <IDocumentLayoutAccess.hxx>
32 #include <swtable.hxx>
33 #include <rootfrm.hxx>
34 #include <flyfrm.hxx>
35 #include <poolfmt.hxx>
36 #include <viewsh.hxx>
37 #include <tabfrm.hxx>
38 #include <viewopt.hxx>
39 #include <htmltbl.hxx>
40 #include <calbck.hxx>
41 #include <o3tl/numeric.hxx>
42 #ifdef DBG_UTIL
43 #include <tblrwcl.hxx>
44 #endif
45 
46 using namespace ::com::sun::star;
47 
48 #define COLFUZZY 20
49 #define MAX_TABWIDTH (USHRT_MAX - 2001)
50 
51 class SwHTMLTableLayoutConstraints
52 {
53     sal_uInt16 const nRow;                    // start row
54     sal_uInt16 const nCol;                    // start column
55     sal_uInt16 const nColSpan;                // the column's COLSPAN
56 
57     std::unique_ptr<SwHTMLTableLayoutConstraints> pNext;        // the next constraint
58 
59     sal_uLong nMinNoAlign, nMaxNoAlign; // provisional result of AL-Pass 1
60 
61 public:
62     SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow,
63                                 sal_uInt16 nCol, sal_uInt16 nColSp );
64 
GetMinNoAlign() const65     sal_uLong GetMinNoAlign() const { return nMinNoAlign; }
GetMaxNoAlign() const66     sal_uLong GetMaxNoAlign() const { return nMaxNoAlign; }
67 
68     SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt );
GetNext() const69     SwHTMLTableLayoutConstraints* GetNext() const { return pNext.get(); }
70 
GetColSpan() const71     sal_uInt16 GetColSpan() const { return nColSpan; }
GetColumn() const72     sal_uInt16 GetColumn() const { return nCol; }
73 };
74 
SwHTMLTableLayoutCnts(const SwStartNode * pSttNd,std::shared_ptr<SwHTMLTableLayout> const & rTab,bool bNoBrTag,std::shared_ptr<SwHTMLTableLayoutCnts> const & rNxt)75 SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd,
76                                              std::shared_ptr<SwHTMLTableLayout> const& rTab,
77                                              bool bNoBrTag,
78                                              std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) :
79     xNext( rNxt ), pBox( nullptr ), xTable( rTab ), pStartNode( pSttNd ),
80     nPass1Done( 0 ), nWidthSet( 0 ), bNoBreakTag( bNoBrTag )
81 {}
82 
GetStartNode() const83 const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const
84 {
85     return pBox ? pBox->GetSttNd() : pStartNode;
86 }
87 
SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const & rCnts,sal_uInt16 nRSpan,sal_uInt16 nCSpan,sal_uInt16 nWidth,bool bPrcWidth,bool bNWrapOpt)88 SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts,
89                                           sal_uInt16 nRSpan, sal_uInt16 nCSpan,
90                                           sal_uInt16 nWidth, bool bPrcWidth,
91                                           bool bNWrapOpt ) :
92     xContents(rCnts),
93     nRowSpan( nRSpan ), nColSpan( nCSpan ),
94     nWidthOption( nWidth ), bPrcWidthOption( bPrcWidth ),
95     bNoWrapOption( bNWrapOpt )
96 {}
97 
SwHTMLTableLayoutColumn(sal_uInt16 nWidth,bool bRelWidth,bool bLBorder)98 SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth,
99                                                   bool bRelWidth,
100                                                   bool bLBorder ) :
101     nMinNoAlign(MINLAY), nMaxNoAlign(MINLAY), nAbsMinNoAlign(MINLAY),
102     nMin(0), nMax(0),
103     nAbsColWidth(0), nRelColWidth(0),
104     nWidthOption( nWidth ), bRelWidthOption( bRelWidth ),
105     bLeftBorder( bLBorder )
106 {}
107 
SwHTMLTableLayoutConstraints(sal_uLong nMin,sal_uLong nMax,sal_uInt16 nRw,sal_uInt16 nColumn,sal_uInt16 nColSp)108 SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(
109     sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRw, sal_uInt16 nColumn, sal_uInt16 nColSp ):
110     nRow( nRw ), nCol( nColumn ), nColSpan( nColSp ),
111     nMinNoAlign( nMin ), nMaxNoAlign( nMax )
112 {}
113 
InsertNext(SwHTMLTableLayoutConstraints * pNxt)114 SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext(
115     SwHTMLTableLayoutConstraints *pNxt )
116 {
117     SwHTMLTableLayoutConstraints *pPrev = nullptr;
118     SwHTMLTableLayoutConstraints *pConstr = this;
119     while( pConstr )
120     {
121         if( pConstr->nRow > pNxt->nRow ||
122             pConstr->GetColumn() > pNxt->GetColumn() )
123             break;
124         pPrev = pConstr;
125         pConstr = pConstr->GetNext();
126     }
127 
128     if( pPrev )
129     {
130         pNxt->pNext = std::move(pPrev->pNext);
131         pPrev->pNext.reset( pNxt );
132         pConstr = this;
133     }
134     else
135     {
136         pNxt->pNext.reset( this );
137         pConstr = pNxt;
138     }
139 
140     return pConstr;
141 }
142 
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)143 SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable,
144                                       sal_uInt16 nRws, sal_uInt16 nCls,
145                                       bool bColsOpt, bool bColTgs,
146                                       sal_uInt16 nWdth, bool bPrcWdth,
147                                       sal_uInt16 nBorderOpt, sal_uInt16 nCellPad,
148                                       sal_uInt16 nCellSp, SvxAdjust eAdjust,
149                                       sal_uInt16 nLMargin, sal_uInt16 nRMargin,
150                                       sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth,
151                                       sal_uInt16 nRightBWidth )
152     : m_aColumns( nCls )
153     , m_aCells( static_cast<size_t>(nRws)*nCls )
154     , m_pSwTable( pTable )
155     , m_nMin( 0 )
156     , m_nMax( 0 )
157     , m_nRows( nRws )
158     , m_nCols( nCls )
159     , m_nLeftMargin( nLMargin )
160     , m_nRightMargin( nRMargin )
161     , m_nInhAbsLeftSpace( 0 )
162     , m_nInhAbsRightSpace( 0 )
163     , m_nRelLeftFill( 0 )
164     , m_nRelRightFill( 0 )
165     , m_nRelTabWidth( 0 )
166     , m_nWidthOption( nWdth )
167     , m_nCellPadding( nCellPad )
168     , m_nCellSpacing( nCellSp )
169     , m_nBorder( nBorderOpt )
170     , m_nLeftBorderWidth( nLeftBWidth )
171     , m_nRightBorderWidth( nRightBWidth )
172     , m_nInhLeftBorderWidth( 0 )
173     , m_nInhRightBorderWidth( 0 )
174     , m_nBorderWidth( nBWidth )
175     , m_nDelayedResizeAbsAvail( 0 )
176     , m_nLastResizeAbsAvail( 0 )
177     , m_nPass1Done( 0 )
178     , m_nWidthSet( 0 )
179     , m_eTableAdjust( eAdjust )
180     , m_bColsOption( bColsOpt )
181     , m_bColTags( bColTgs )
182     , m_bPrcWidthOption( bPrcWdth )
183     , m_bUseRelWidth( false )
184     , m_bMustResize( true )
185     , m_bExportable( true )
186     , m_bBordersChanged( false )
187     , m_bMayBeInFlyFrame( false )
188     , m_bDelayedResizeRecalc( false)
189     , m_bMustNotResize( false )
190     , m_bMustNotRecalc( false )
191 {
192     m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout,
193                                              DelayedResize_Impl ) );
194 }
195 
~SwHTMLTableLayout()196 SwHTMLTableLayout::~SwHTMLTableLayout()
197 {
198 }
199 
200 /// The border widths are calculated like in Netscape:
201 /// Outer border: BORDER + CELLSPACING + CELLPADDING
202 /// Inner border: CELLSPACING + CELLPADDING
203 /// However, we respect the border widths in SW if bSwBorders is set,
204 /// so that we don't wrap wrongly.
205 /// We also need to respect the distance to the content. Even if
206 /// only the opposite side has a border.
GetLeftCellSpace(sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const207 sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
208                                             bool bSwBorders ) const
209 {
210     sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
211 
212     if( nCol == 0 )
213     {
214         nSpace = nSpace + m_nBorder;
215 
216         if( bSwBorders && nSpace < m_nLeftBorderWidth )
217             nSpace = m_nLeftBorderWidth;
218     }
219     else if( bSwBorders )
220     {
221         if( GetColumn(nCol)->HasLeftBorder() )
222         {
223             if( nSpace < m_nBorderWidth )
224                 nSpace = m_nBorderWidth;
225         }
226         else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth &&
227                  nSpace < MIN_BORDER_DIST )
228         {
229             OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" );
230             // If the opposite side has a border we need to respect at
231             // least the minimum distance to the content.
232             // Additionally, we could also use nCellPadding for this.
233             nSpace = MIN_BORDER_DIST;
234         }
235     }
236 
237     return nSpace;
238 }
239 
GetRightCellSpace(sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const240 sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
241                                              bool bSwBorders ) const
242 {
243     sal_uInt16 nSpace = m_nCellPadding;
244 
245     if( nCol+nColSpan == m_nCols )
246     {
247         nSpace += m_nBorder + m_nCellSpacing;
248         if( bSwBorders && nSpace < m_nRightBorderWidth )
249             nSpace = m_nRightBorderWidth;
250     }
251     else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() &&
252              nSpace < MIN_BORDER_DIST )
253     {
254         OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" );
255         // If the opposite side has a border we need to respect at
256         // least the minimum distance to the content.
257         // Additionally, we could also use nCellPadding for this.
258         nSpace = MIN_BORDER_DIST;
259     }
260 
261     return nSpace;
262 }
263 
AddBorderWidth(sal_uLong & rMin,sal_uLong & rMax,sal_uLong & rAbsMin,sal_uInt16 nCol,sal_uInt16 nColSpan,bool bSwBorders) const264 void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax,
265                                         sal_uLong &rAbsMin,
266                                         sal_uInt16 nCol, sal_uInt16 nColSpan,
267                                         bool bSwBorders ) const
268 {
269     sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) +
270                  GetRightCellSpace( nCol, nColSpan, bSwBorders );
271 
272     rMin += nAdd;
273     rMax += nAdd;
274     rAbsMin += nAdd;
275 }
276 
SetBoxWidth(SwTableBox * pBox,sal_uInt16 nCol,sal_uInt16 nColSpan) const277 void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol,
278                              sal_uInt16 nColSpan ) const
279 {
280     SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();
281 
282     // calculate the box's width
283     SwTwips nFrameWidth = 0;
284     while( nColSpan-- )
285         nFrameWidth += GetColumn( nCol++ )->GetRelColWidth();
286 
287     // and reset
288     pFrameFormat->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nFrameWidth, 0 ));
289 }
290 
GetAvail(sal_uInt16 nCol,sal_uInt16 nColSpan,sal_uInt16 & rAbsAvail,sal_uInt16 & rRelAvail) const291 void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan,
292                                   sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const
293 {
294     rAbsAvail = 0;
295     rRelAvail = 0;
296     for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ )
297     {
298         const SwHTMLTableLayoutColumn *pColumn = GetColumn(i);
299         rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth();
300         rRelAvail = rRelAvail + pColumn->GetRelColWidth();
301     }
302 }
303 
GetBrowseWidthByVisArea(const SwDoc & rDoc)304 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc )
305 {
306     SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
307     if( pVSh )
308     {
309         return static_cast<sal_uInt16>(pVSh->GetBrowseWidth());
310     }
311 
312     return 0;
313 }
314 
GetBrowseWidth(const SwDoc & rDoc)315 sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc )
316 {
317     // If we have a layout, we can get the width from there.
318     const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
319     if( pRootFrame )
320     {
321         const SwFrame *pPageFrame = pRootFrame->GetLower();
322         if( pPageFrame )
323             return static_cast<sal_uInt16>(pPageFrame->getFramePrintArea().Width());
324     }
325 
326     // #i91658#
327     // Assertion removed which state that no browse width is available.
328     // Investigation reveals that all calls can handle the case that no browse
329     // width is provided.
330     return GetBrowseWidthByVisArea( rDoc );
331 }
332 
GetBrowseWidthByTabFrame(const SwTabFrame & rTabFrame) const333 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame(
334     const SwTabFrame& rTabFrame ) const
335 {
336     SwTwips nWidth = 0;
337 
338     const SwFrame *pUpper = rTabFrame.GetUpper();
339     if( MayBeInFlyFrame() && pUpper->IsFlyFrame() &&
340         static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() )
341     {
342         // If the table is located within a self-created frame, the anchor's
343         // width is relevant not the frame's width.
344         // For paragraph-bound frames we don't respect paragraph indents.
345         const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame();
346         if( pAnchor->IsTextFrame() )
347             nWidth = pAnchor->getFrameArea().Width();
348         else
349             nWidth = pAnchor->getFramePrintArea().Width();
350     }
351     else
352     {
353         nWidth = pUpper->getFramePrintArea().Width();
354     }
355 
356     SwTwips nUpperDummy = 0;
357     long nRightOffset = 0,
358          nLeftOffset  = 0;
359     rTabFrame.CalcFlyOffsets( nUpperDummy, nLeftOffset, nRightOffset );
360     nWidth -= (nLeftOffset + nRightOffset);
361 
362     return static_cast<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16)));
363 }
364 
GetBrowseWidthByTable(const SwDoc & rDoc) const365 sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const
366 {
367     sal_uInt16 nBrowseWidth = 0;
368     SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First();
369     if( pFrame )
370     {
371         nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame );
372     }
373     else
374     {
375         nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc );
376     }
377 
378     return nBrowseWidth;
379 }
380 
GetAnyBoxStartNode() const381 const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const
382 {
383     const SwStartNode *pBoxSttNd;
384 
385     const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0];
386     while( nullptr == (pBoxSttNd = pBox->GetSttNd()) )
387     {
388         OSL_ENSURE( !pBox->GetTabLines().empty(),
389                 "Box without start node and lines" );
390         OSL_ENSURE( !pBox->GetTabLines().front()->GetTabBoxes().empty(),
391                 "Line without boxes" );
392         pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
393     }
394 
395     return pBoxSttNd;
396 }
397 
FindFlyFrameFormat() const398 SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const
399 {
400     const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode();
401     OSL_ENSURE( pTableNd, "No Table-Node?" );
402     return pTableNd->GetFlyFormat();
403 }
404 
lcl_GetMinMaxSize(sal_uLong & rMinNoAlignCnts,sal_uLong & rMaxNoAlignCnts,sal_uLong & rAbsMinNoAlignCnts,SwTextNode const * pTextNd,sal_uLong nIdx,bool bNoBreak)405 static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts,
406                         sal_uLong& rAbsMinNoAlignCnts,
407                         SwTextNode const *pTextNd, sal_uLong nIdx, bool bNoBreak )
408 {
409     pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts,
410                            rAbsMinNoAlignCnts );
411     OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts,
412             "GetMinMaxSize: absmin > min" );
413     OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts,
414             "GetMinMaxSize: max > min" );
415 
416     // The maximal width for a <PRE> paragraph is the minimal width
417     const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
418     while( pColl && !pColl->IsDefault() &&
419             (USER_FMT & pColl->GetPoolFormatId()) )
420     {
421         pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom());
422     }
423 
424     // <NOBR> in the whole cell apply to text but not to tables.
425     // Netscape only considers this for graphics.
426     if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak )
427     {
428         rMinNoAlignCnts = rMaxNoAlignCnts;
429         rAbsMinNoAlignCnts = rMaxNoAlignCnts;
430     }
431 }
432 
AutoLayoutPass1()433 void SwHTMLTableLayout::AutoLayoutPass1()
434 {
435     m_nPass1Done++;
436 
437     m_nMin = m_nMax = 0; // clear pass1 info
438 
439     bool bFixRelWidths = false;
440     sal_uInt16 i;
441 
442     std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints;
443 
444     for( i=0; i<m_nCols; i++ )
445     {
446         SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
447         pColumn->ClearPass1Info( !HasColTags() );
448         sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to
449         sal_uInt16 nColSkip = USHRT_MAX;    // How many columns need to be skipped
450 
451         for( sal_uInt16 j=0; j<m_nRows; j++ )
452         {
453             SwHTMLTableLayoutCell *pCell = GetCell(j,i);
454             SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get();
455 
456             // We need to examine all rows in order to
457             // get the column that should be calculated next.
458             sal_uInt16 nColSpan = pCell->GetColSpan();
459             if( nColSpan < nColSkip )
460                 nColSkip = nColSpan;
461 
462             if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) )
463             {
464                 // The cell is empty or it's content was not edited
465                 if( nColSpan < nMinColSpan )
466                     nMinColSpan = nColSpan;
467 
468                 sal_uLong nMinNoAlignCell = 0;
469                 sal_uLong nMaxNoAlignCell = 0;
470                 sal_uLong nAbsMinNoAlignCell = 0;
471                 sal_uLong nMaxTableCell = 0;
472                 sal_uLong nAbsMinTableCell = 0;
473 
474                 while( pCnts )
475                 {
476                     const SwStartNode *pSttNd = pCnts->GetStartNode();
477                     if( pSttNd )
478                     {
479                         const SwDoc *pDoc = pSttNd->GetDoc();
480                         sal_uLong nIdx = pSttNd->GetIndex();
481                         while( !(pDoc->GetNodes()[nIdx])->IsEndNode() )
482                         {
483                             SwTextNode *pTextNd = (pDoc->GetNodes()[nIdx])->GetTextNode();
484                             if( pTextNd )
485                             {
486                                 sal_uLong nMinNoAlignCnts = 0;
487                                 sal_uLong nMaxNoAlignCnts = 0;
488                                 sal_uLong nAbsMinNoAlignCnts = 0;
489 
490                                 lcl_GetMinMaxSize( nMinNoAlignCnts,
491                                                    nMaxNoAlignCnts,
492                                                    nAbsMinNoAlignCnts,
493                                                    pTextNd, nIdx,
494                                                    pCnts->HasNoBreakTag() );
495 
496                                 if( nMinNoAlignCnts > nMinNoAlignCell )
497                                     nMinNoAlignCell = nMinNoAlignCnts;
498                                 if( nMaxNoAlignCnts > nMaxNoAlignCell )
499                                     nMaxNoAlignCell = nMaxNoAlignCnts;
500                                 if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell )
501                                     nAbsMinNoAlignCell = nAbsMinNoAlignCnts;
502                             }
503                             else
504                             {
505                                 SwTableNode *pTabNd = (pDoc->GetNodes()[nIdx])->GetTableNode();
506                                 if( pTabNd )
507                                 {
508                                     SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout();
509                                     if( pChild )
510                                     {
511                                         pChild->AutoLayoutPass1();
512                                         sal_uLong nMaxTableCnts = pChild->m_nMax;
513                                         sal_uLong nAbsMinTableCnts = pChild->m_nMin;
514 
515                                         // A fixed table width is taken over as minimum and
516                                         // maximum at the same time
517                                         if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
518                                         {
519                                             sal_uLong nTabWidth = pChild->m_nWidthOption;
520                                             if( nTabWidth >= nAbsMinTableCnts  )
521                                             {
522                                                 nMaxTableCnts = nTabWidth;
523                                                 nAbsMinTableCnts = nTabWidth;
524                                             }
525                                             else
526                                             {
527                                                 nMaxTableCnts = nAbsMinTableCnts;
528                                             }
529                                         }
530 
531                                         if( nMaxTableCnts > nMaxTableCell )
532                                             nMaxTableCell = nMaxTableCnts;
533                                         if( nAbsMinTableCnts > nAbsMinTableCell )
534                                             nAbsMinTableCell = nAbsMinTableCnts;
535                                     }
536                                     nIdx = pTabNd->EndOfSectionNode()->GetIndex();
537                                 }
538                             }
539                             nIdx++;
540                         }
541                     }
542                     else if (SwHTMLTableLayout *pChild = pCnts->GetTable())
543                     {
544                         OSL_ENSURE( false, "Sub tables in HTML import?" );
545                         pChild->AutoLayoutPass1();
546                         sal_uLong nMaxTableCnts = pChild->m_nMax;
547                         sal_uLong nAbsMinTableCnts = pChild->m_nMin;
548 
549                         // A fixed table width is taken over as minimum and
550                         // maximum at the same time
551                         if( !pChild->m_bPrcWidthOption && pChild->m_nWidthOption )
552                         {
553                             sal_uLong nTabWidth = pChild->m_nWidthOption;
554                             if( nTabWidth >= nAbsMinTableCnts  )
555                             {
556                                 nMaxTableCnts = nTabWidth;
557                                 nAbsMinTableCnts = nTabWidth;
558                             }
559                             else
560                             {
561                                 nMaxTableCnts = nAbsMinTableCnts;
562                             }
563                         }
564 
565                         if( nMaxTableCnts > nMaxTableCell )
566                             nMaxTableCell = nMaxTableCnts;
567                         if( nAbsMinTableCnts > nAbsMinTableCell )
568                             nAbsMinTableCell = nAbsMinTableCnts;
569                     }
570                     pCnts->SetPass1Done( m_nPass1Done );
571                     pCnts = pCnts->GetNext().get();
572                 }
573 
574 // This code previously came after AddBorderWidth
575                 // If a table's width is wider in a cell than what we've calculated
576                 // for the other content we need to use the table's width.
577                 if( nMaxTableCell > nMaxNoAlignCell )
578                     nMaxNoAlignCell = nMaxTableCell;
579                 if( nAbsMinTableCell > nAbsMinNoAlignCell )
580                 {
581                     nAbsMinNoAlignCell = nAbsMinTableCell;
582                     if( nMinNoAlignCell < nAbsMinNoAlignCell )
583                         nMinNoAlignCell = nAbsMinNoAlignCell;
584                     if( nMaxNoAlignCell < nMinNoAlignCell )
585                         nMaxNoAlignCell = nMinNoAlignCell;
586                 }
587 // This code previously came after AddBorderWidth
588 
589                 bool bRelWidth = pCell->IsPrcWidthOption();
590                 sal_uInt16 nWidth = pCell->GetWidthOption();
591 
592                 // A NOWRAP option applies to text and tables, but is
593                 // not applied for fixed cell width.
594                 // Instead, the stated cell width behaves like a minimal
595                 // width.
596                 if( pCell->HasNoWrapOption() )
597                 {
598                     if( nWidth==0 || bRelWidth )
599                     {
600                         nMinNoAlignCell = nMaxNoAlignCell;
601                         nAbsMinNoAlignCell = nMaxNoAlignCell;
602                     }
603                     else
604                     {
605                         if( nWidth>nMinNoAlignCell )
606                             nMinNoAlignCell = nWidth;
607                         if( nWidth>nAbsMinNoAlignCell )
608                             nAbsMinNoAlignCell = nWidth;
609                     }
610                 }
611 
612                 // Respect minimum width for content
613                 if( nMinNoAlignCell < MINLAY )
614                     nMinNoAlignCell = MINLAY;
615                 if( nMaxNoAlignCell < MINLAY )
616                     nMaxNoAlignCell = MINLAY;
617                 if( nAbsMinNoAlignCell < MINLAY )
618                     nAbsMinNoAlignCell = MINLAY;
619 
620                 // Respect the border and distance to the content
621                 AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell,
622                                 nAbsMinNoAlignCell, i, nColSpan );
623 
624                 if( 1==nColSpan )
625                 {
626                     // take over the values directly
627                     pColumn->MergeMinMaxNoAlign( nMinNoAlignCell,
628                                                  nMaxNoAlignCell,
629                                                  nAbsMinNoAlignCell );
630 
631                     // the widest WIDTH wins
632                     if( !HasColTags() )
633                         pColumn->MergeCellWidthOption( nWidth, bRelWidth );
634                 }
635                 else
636                 {
637                     // Process the data line by line from left to right at the end
638 
639                     // When which values is taken over will be explained further down.
640                     if( !HasColTags() && nWidth && !bRelWidth )
641                     {
642                         sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0;
643                         AddBorderWidth( nAbsWidth, nDummy, nDummy2,
644                                         i, nColSpan, false );
645 
646                         if( nAbsWidth >= nMinNoAlignCell )
647                         {
648                             nMaxNoAlignCell = nAbsWidth;
649                             if( HasColsOption() )
650                                 nMinNoAlignCell = nAbsWidth;
651                         }
652                         else if( nAbsWidth >= nAbsMinNoAlignCell )
653                         {
654                             nMaxNoAlignCell = nAbsWidth;
655                             nMinNoAlignCell = nAbsWidth;
656                         }
657                         else
658                         {
659                             nMaxNoAlignCell = nAbsMinNoAlignCell;
660                             nMinNoAlignCell = nAbsMinNoAlignCell;
661                         }
662                     }
663                     else if( HasColsOption() || HasColTags() )
664                         nMinNoAlignCell = nAbsMinNoAlignCell;
665 
666                     SwHTMLTableLayoutConstraints *pConstr =
667                         new SwHTMLTableLayoutConstraints( nMinNoAlignCell,
668                             nMaxNoAlignCell, j, i, nColSpan );
669                     if (xConstraints)
670                     {
671                         SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr);
672                         xConstraints.release();
673                         xConstraints.reset(pConstraints);
674                     }
675                     else
676                         xConstraints.reset(pConstr);
677                 }
678             }
679         }
680 
681         OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan,
682                 "Layout pass 1: Columns are being forgotten!" );
683         OSL_ENSURE( nMinColSpan!=USHRT_MAX,
684                 "Layout pass 1: unnecessary pass through the loop or a bug" );
685 
686         if( 1==nMinColSpan )
687         {
688             // There are cells with COLSPAN 1 and therefore also useful
689             // values in pColumn
690 
691             // Take over values according to the following table (Netscape 4.0 pv 3):
692 
693             // WIDTH:           no COLS         COLS
694 
695             // none             min = min       min = absmin
696             //                  max = max       max = max
697 
698             // >= min           min = min       min = width
699             //                  max = width     max = width
700 
701             // >= absmin        min = width(*)  min = width
702             //                  max = width     max = width
703 
704             // < absmin         min = absmin    min = absmin
705             //                  max = absmin    max = absmin
706 
707             // (*) Netscape uses the minimum width without a break before
708             //     the last graphic here. We don't have that (yet?),
709             //     so we leave it set to width.
710 
711             if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() )
712             {
713                 // Take over absolute widths as minimal and maximal widths.
714                 sal_uLong nAbsWidth = pColumn->GetWidthOption();
715                 sal_uLong nDummy = 0, nDummy2 = 0;
716                 AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false );
717 
718                 if( nAbsWidth >= pColumn->GetMinNoAlign() )
719                 {
720                     pColumn->SetMinMax( HasColsOption() ? nAbsWidth
721                                                    : pColumn->GetMinNoAlign(),
722                                         nAbsWidth );
723                 }
724                 else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() )
725                 {
726                     pColumn->SetMinMax( nAbsWidth, nAbsWidth );
727                 }
728                 else
729                 {
730                     pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(),
731                                         pColumn->GetAbsMinNoAlign() );
732                 }
733             }
734             else
735             {
736                 pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign()
737                                                : pColumn->GetMinNoAlign(),
738                                     pColumn->GetMaxNoAlign() );
739             }
740         }
741         else if( USHRT_MAX!=nMinColSpan )
742         {
743             // Can be anything != 0, because it is altered by the constraints.
744             pColumn->SetMinMax( MINLAY, MINLAY );
745 
746             // the next columns need not to be processed
747             i += (nColSkip-1);
748         }
749 
750         m_nMin += pColumn->GetMin();
751         m_nMax += pColumn->GetMax();
752         if (pColumn->IsRelWidthOption()) bFixRelWidths = true;
753     }
754 
755     // Now process the constraints
756     SwHTMLTableLayoutConstraints *pConstr = xConstraints.get();
757     while( pConstr )
758     {
759         // At first we need to process the width in the same way
760         // as the column widths
761         sal_uInt16 nCol = pConstr->GetColumn();
762         sal_uInt16 nColSpan = pConstr->GetColSpan();
763         sal_uLong nConstrMin = pConstr->GetMinNoAlign();
764         sal_uLong nConstrMax = pConstr->GetMaxNoAlign();
765 
766         // We get the hitherto width of the spanned columns
767         sal_uLong nColsMin = 0;
768         sal_uLong nColsMax = 0;
769         for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ )
770         {
771             SwHTMLTableLayoutColumn *pColumn = GetColumn( j );
772             nColsMin += pColumn->GetMin();
773             nColsMax += pColumn->GetMax();
774         }
775 
776         if( nColsMin<nConstrMin )
777         {
778             // Proportionately distribute the minimum value to the columns
779             sal_uLong nMinD = nConstrMin-nColsMin;
780 
781             if( nConstrMin > nColsMax )
782             {
783                 // Proportional according to the minimum widths
784                 sal_uInt16 nEndCol = nCol+nColSpan;
785                 sal_uLong nDiff = nMinD;
786                 for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ )
787                 {
788                     SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
789 
790                     sal_uLong nColMin = pColumn->GetMin();
791                     sal_uLong nColMax = pColumn->GetMax();
792 
793                     m_nMin -= nColMin;
794                     sal_uLong nAdd;
795                     if (ic < nEndCol-1)
796                     {
797                         if (nColsMin == 0)
798                             throw o3tl::divide_by_zero();
799                         nAdd = (nColMin * nMinD) / nColsMin;
800                     }
801                     else
802                     {
803                         nAdd = nDiff;
804                     }
805                     nColMin += nAdd;
806                     m_nMin += nColMin;
807                     OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" );
808                     nDiff -= nAdd;
809 
810                     if( nColMax < nColMin )
811                     {
812                         m_nMax -= nColMax;
813                         nColsMax -= nColMax;
814                         nColMax = nColMin;
815                         m_nMax += nColMax;
816                         nColsMax += nColMax;
817                     }
818 
819                     pColumn->SetMinMax( nColMin, nColMax );
820                 }
821             }
822             else
823             {
824                 // Proportional according to the difference of max and min
825                 for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
826                 {
827                     SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
828 
829                     sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin();
830                     if( nMinD < nDiff )
831                         nDiff = nMinD;
832 
833                     pColumn->AddToMin( nDiff );
834 
835                     OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
836                             "Why is the Column suddenly too narrow?" );
837 
838                     m_nMin += nDiff;
839                     nMinD -= nDiff;
840                 }
841             }
842         }
843 
844         if( !HasColTags() && nColsMax<nConstrMax )
845         {
846             sal_uLong nMaxD = nConstrMax-nColsMax;
847 
848             for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
849             {
850                 SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
851 
852                 m_nMax -= pColumn->GetMax();
853 
854                 pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax );
855 
856                 m_nMax += pColumn->GetMax();
857             }
858         }
859 
860         pConstr = pConstr->GetNext();
861     }
862 
863     if( bFixRelWidths )
864     {
865         if( HasColTags() )
866         {
867             // To adapt the relative widths, in a first step we multiply the
868             // minimum width of all affected cells with the relative width
869             // of the column.
870             // Thus, the width ratio among the columns is correct.
871 
872             // Furthermore, a factor is calculated that says by how much the
873             // cell has gotten wider than the minimum width.
874 
875             // In the second step the calculated widths are divided by this
876             // factor.  Thereby a cell's width is preserved and serves as a
877             // basis for the other cells.
878             // We only change the maximum widths here!
879 
880             sal_uLong nAbsMin = 0;  // absolute minimum width of all widths with relative width
881             sal_uLong nRel = 0;     // sum of all relative widths of all columns
882             for( i=0; i<m_nCols; i++ )
883             {
884                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
885                 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
886                 {
887                     nAbsMin += pColumn->GetMin();
888                     nRel += pColumn->GetWidthOption();
889                 }
890             }
891 
892             sal_uLong nQuot = ULONG_MAX;
893             for( i=0; i<m_nCols; i++ )
894             {
895                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
896                 if( pColumn->IsRelWidthOption() )
897                 {
898                     m_nMax -= pColumn->GetMax();
899                     if( pColumn->GetWidthOption() && pColumn->GetMin() )
900                     {
901                         pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
902                         sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
903                         if( nColQuot<nQuot )
904                             nQuot = nColQuot;
905                     }
906                 }
907             }
908             OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX,
909                     "Where did the relative columns go?" );
910             for( i=0; i<m_nCols; i++ )
911             {
912                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
913                 if( pColumn->IsRelWidthOption() )
914                 {
915                     if( pColumn->GetWidthOption() )
916                         pColumn->SetMax( pColumn->GetMax() / nQuot );
917                     else
918                         pColumn->SetMax( pColumn->GetMin() );
919                     OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
920                             "Maximum column width is lower than the minimum column width" );
921                     m_nMax += pColumn->GetMax();
922                 }
923             }
924         }
925         else
926         {
927             sal_uInt16 nRel = 0;        // sum of the relative widths of all columns
928             sal_uInt16 nRelCols = 0;    // count of the columns with a relative setting
929             sal_uLong nRelMax = 0;      // fraction of the maximum of this column
930             for( i=0; i<m_nCols; i++ )
931             {
932                 OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" );
933                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
934                 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
935                 {
936                     // Make sure that the relative widths don't go above 100%
937                     sal_uInt16 nColWidth = pColumn->GetWidthOption();
938                     if( nRel+nColWidth > 100 )
939                     {
940                         nColWidth = 100 - nRel;
941                         pColumn->SetWidthOption( nColWidth );
942                     }
943                     nRelMax += pColumn->GetMax();
944                     nRel = nRel + nColWidth;
945                     nRelCols++;
946                 }
947                 else if( !pColumn->GetMin() )
948                 {
949                     // The column is empty (so it was solely created by
950                     // COLSPAN) and therefore must not be assigned a % width.
951                     nRelCols++;
952                 }
953             }
954 
955             // If there are percentages left we distribute them to the columns
956             // that don't have a width setting. Like in Netscape we distribute
957             // the remaining percentages according to the ratio of the maximum
958             // width of the affected columns.
959             // For the maximum widths we also take the fixed-width columns
960             // into account.  Is that correct?
961             sal_uLong nFixMax = 0;
962             if( nRel < 100 && nRelCols < m_nCols )
963             {
964                 nFixMax = m_nMax - nRelMax;
965                 SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max");
966             }
967             if (nFixMax)
968             {
969                 sal_uInt16 nRelLeft = 100 - nRel;
970                 for( i=0; i<m_nCols; i++ )
971                 {
972                     SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
973                     if( !pColumn->IsRelWidthOption() &&
974                         !pColumn->GetWidthOption() &&
975                         pColumn->GetMin() )
976                     {
977                         // the next column gets the rest
978                         sal_uInt16 nColWidth =
979                             static_cast<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax);
980                         pColumn->SetWidthOption( nColWidth );
981                     }
982                 }
983             }
984 
985             // adjust the maximum widths now accordingly
986             sal_uLong nQuotMax = ULONG_MAX;
987             sal_uLong nOldMax = m_nMax;
988             m_nMax = 0;
989             for( i=0; i<m_nCols; i++ )
990             {
991                 // Columns with a % setting are adapted accordingly.
992                 // Columns, that
993                 // - do not have a % setting and are located within a tables
994                 // with COLS and WIDTH, or
995                 // - their width is 0%
996                 // get set to the minimum width.
997                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
998                 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
999                 {
1000                     sal_uLong nNewMax;
1001                     sal_uLong nColQuotMax;
1002                     if( !m_nWidthOption )
1003                     {
1004                         nNewMax = nOldMax * pColumn->GetWidthOption();
1005                         nColQuotMax = nNewMax / pColumn->GetMax();
1006                     }
1007                     else
1008                     {
1009                         nNewMax = m_nMin * pColumn->GetWidthOption();
1010                         nColQuotMax = nNewMax / pColumn->GetMin();
1011                     }
1012                     pColumn->SetMax( nNewMax );
1013                     if( nColQuotMax < nQuotMax )
1014                         nQuotMax = nColQuotMax;
1015                 }
1016                 else if( HasColsOption() || m_nWidthOption ||
1017                          (pColumn->IsRelWidthOption() &&
1018                           !pColumn->GetWidthOption()) )
1019                     pColumn->SetMax( pColumn->GetMin() );
1020             }
1021             // and divide by the quotient
1022             SAL_WARN_IF(!nQuotMax, "sw.core", "Where did the relative columns go?");
1023             for (i = 0; i < m_nCols; ++i)
1024             {
1025                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1026                 if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax)
1027                 {
1028                     pColumn->SetMax( pColumn->GetMax() / nQuotMax );
1029                     OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
1030                             "Minimum width is one column bigger than maximum" );
1031                     if( pColumn->GetMax() < pColumn->GetMin() )
1032                         pColumn->SetMax( pColumn->GetMin() );
1033                 }
1034                 m_nMax += pColumn->GetMax();
1035             }
1036         }
1037     }
1038 }
1039 
1040 //TODO: provide documentation
1041 /**
1042 
1043     @param nAbsAvail available space in TWIPS.
1044     @param nRelAvail available space related to USHRT_MAX or 0
1045     @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
1046                      cell for the border and the distance to the paragraph.
1047 */
AutoLayoutPass2(sal_uInt16 nAbsAvail,sal_uInt16 nRelAvail,sal_uInt16 nAbsLeftSpace,sal_uInt16 nAbsRightSpace,sal_uInt16 nParentInhAbsSpace)1048 void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail,
1049                                          sal_uInt16 nAbsLeftSpace,
1050                                          sal_uInt16 nAbsRightSpace,
1051                                          sal_uInt16 nParentInhAbsSpace )
1052 {
1053     // For a start we do a lot of plausibility tests
1054 
1055     // An absolute width always has to be passed
1056     OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" );
1057 
1058     // A relative width must only be passed for tables within tables (?)
1059     OSL_ENSURE( IsTopTable() == (nRelAvail==0),
1060             "AutoLayout pass 2: Relative width at table in table or the other way around" );
1061 
1062     // The table's minimum width must not be bigger than its maximum width
1063     OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" );
1064 
1065     // Remember the available width for which the table was calculated.
1066     // This is a good place as we pass by here for the initial calculation
1067     // of the table in the parser and for each Resize_ call.
1068     m_nLastResizeAbsAvail = nAbsAvail;
1069 
1070     // Step 1: The available space is readjusted for the left/right border,
1071     // possibly existing filler cells and distances.
1072 
1073     // Distance to the content and border
1074     sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0;
1075     if( !IsTopTable() &&
1076         GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail )
1077     {
1078         nAbsLeftFill = nAbsLeftSpace;
1079         nAbsRightFill = nAbsRightSpace;
1080     }
1081 
1082     // Left and right distance
1083     if( m_nLeftMargin || m_nRightMargin )
1084     {
1085         if( IsTopTable() )
1086         {
1087             // For the top table we always respect the borders, because we
1088             // never go below the table's minimum width.
1089             nAbsAvail -= (m_nLeftMargin + m_nRightMargin);
1090         }
1091         else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail )
1092         {
1093             // Else, we only respect the borders if there's space available
1094             // for them (nMin has already been calculated!)
1095             nAbsLeftFill = nAbsLeftFill + m_nLeftMargin;
1096             nAbsRightFill = nAbsRightFill + m_nRightMargin;
1097         }
1098     }
1099 
1100     // Read just the available space
1101     m_nRelLeftFill = 0;
1102     m_nRelRightFill = 0;
1103     if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) )
1104     {
1105         sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;
1106 
1107         m_nRelLeftFill = static_cast<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail);
1108         m_nRelRightFill = static_cast<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail);
1109 
1110         nAbsAvail -= (nAbsLeftFill + nAbsRightFill);
1111         if( nRelAvail )
1112             nRelAvail -= (m_nRelLeftFill + m_nRelRightFill);
1113     }
1114 
1115     // Step 2: Calculate the absolute table width.
1116     sal_uInt16 nAbsTabWidth = 0;
1117     m_bUseRelWidth = false;
1118     if( m_nWidthOption )
1119     {
1120         if( m_bPrcWidthOption )
1121         {
1122             OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" );
1123             if( m_nWidthOption > 100 )
1124                 m_nWidthOption = 100;
1125 
1126             // The absolute width is equal to the given percentage of
1127             // the available width.
1128             // Top tables only get a relative width if the available space
1129             // is *strictly larger* than the minimum width.
1130 
1131             // CAUTION: We need the "strictly larger" because changing from a
1132             // relative width to an absolute width by resizing would lead
1133             // to an infinite loop.
1134 
1135             // Because we do not call resize for tables in frames if the
1136             // frame has a non-relative width, we cannot play such games.
1137 
1138             // Let's play such games now anyway. We had a graphic in a 1% wide
1139             // table and it didn't fit in of course.
1140             nAbsTabWidth = static_cast<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 );
1141             if( IsTopTable() &&
1142                 ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) )
1143             {
1144                 nRelAvail = USHRT_MAX;
1145                 m_bUseRelWidth = true;
1146             }
1147         }
1148         else
1149         {
1150             nAbsTabWidth = m_nWidthOption;
1151             if( nAbsTabWidth > MAX_TABWIDTH )
1152                 nAbsTabWidth = MAX_TABWIDTH;
1153 
1154             // Tables within tables must never get wider than the available
1155             // space.
1156             if( !IsTopTable() && nAbsTabWidth > nAbsAvail )
1157                 nAbsTabWidth = nAbsAvail;
1158         }
1159     }
1160 
1161     OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail,
1162             "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
1163     OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail,
1164             "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );
1165 
1166     // Catch for the two asserts above (we never know!)
1167     if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail )
1168         nAbsTabWidth = nAbsAvail;
1169 
1170     // Step 3: Identify the column width and, if applicable, the absolute
1171     // and relative table widths.
1172     if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) ||
1173         m_nMin > MAX_TABWIDTH )
1174     {
1175         // If
1176         // - an inner table's minimum is larger than the available space, or
1177         // - a top table's minimum is larger than USHORT_MAX the table
1178         // has to be adapted to the available space or USHORT_MAX.
1179         // We preserve the widths' ratio amongst themselves, however.
1180 
1181         nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail;
1182         m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth );
1183 
1184         // First of all, we check whether we can fit the layout constrains,
1185         // which are: Every cell's width excluding the borders must be at least
1186         // MINLAY:
1187 
1188         sal_uLong nRealMin = 0;
1189         for( sal_uInt16 i=0; i<m_nCols; i++ )
1190         {
1191             sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
1192             AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
1193             nRealMin += nRealColMin;
1194         }
1195         if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) )
1196         {
1197             // "Rien ne va plus": we cannot get the minimum column widths
1198             // the layout wants to have.
1199 
1200             sal_uInt16 nAbs = 0, nRel = 0;
1201             SwHTMLTableLayoutColumn *pColumn;
1202             for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1203             {
1204                 pColumn = GetColumn( i );
1205                 sal_uLong nColMin = pColumn->GetMin();
1206                 if( nColMin <= USHRT_MAX )
1207                 {
1208                     pColumn->SetAbsColWidth(
1209                         static_cast<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) );
1210                     pColumn->SetRelColWidth(
1211                         static_cast<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) );
1212                 }
1213                 else
1214                 {
1215                     double nColMinD = nColMin;
1216                     pColumn->SetAbsColWidth(
1217                         static_cast<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) );
1218                     pColumn->SetRelColWidth(
1219                         static_cast<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) );
1220                 }
1221 
1222                 nAbs = nAbs + pColumn->GetAbsColWidth();
1223                 nRel = nRel + pColumn->GetRelColWidth();
1224             }
1225             pColumn = GetColumn( m_nCols-1 );
1226             pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
1227             pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
1228         }
1229         else
1230         {
1231             sal_uLong nDistAbs = nAbsTabWidth - nRealMin;
1232             sal_uLong nDistRel = m_nRelTabWidth - nRealMin;
1233             sal_uLong nDistMin = m_nMin - nRealMin;
1234             sal_uInt16 nAbs = 0, nRel = 0;
1235             SwHTMLTableLayoutColumn *pColumn;
1236             for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1237             {
1238                 pColumn = GetColumn( i );
1239                 sal_uLong nColMin = pColumn->GetMin();
1240                 sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
1241                 AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
1242 
1243                 if( nColMin <= USHRT_MAX )
1244                 {
1245                     pColumn->SetAbsColWidth(
1246                         static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
1247                     pColumn->SetRelColWidth(
1248                         static_cast<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
1249                 }
1250                 else
1251                 {
1252                     double nColMinD = nColMin;
1253                     pColumn->SetAbsColWidth(
1254                         static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
1255                     pColumn->SetRelColWidth(
1256                         static_cast<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
1257                 }
1258 
1259                 nAbs = nAbs + pColumn->GetAbsColWidth();
1260                 nRel = nRel + pColumn->GetRelColWidth();
1261             }
1262             pColumn = GetColumn( m_nCols-1 );
1263             pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
1264             pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
1265         }
1266     }
1267     else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) )
1268     {
1269         // If
1270         // - the table has a fixed width and the table's maximum is
1271         //   smaller, or
1272         //- the maximum is smaller than the available space,
1273         // we can take over the maximum as it is. Respectively
1274         // the table can only be adapted to the fixed width by
1275         // respecting the maximum.
1276 
1277         // No fixed width, use the maximum.
1278         if( !nAbsTabWidth )
1279             nAbsTabWidth = static_cast<sal_uInt16>(m_nMax);
1280 
1281         // A top table may also get wider then the available space.
1282         if( nAbsTabWidth > nAbsAvail )
1283         {
1284             OSL_ENSURE( IsTopTable(),
1285                     "Table in table should get wider than the surrounding cell." );
1286             nAbsAvail = nAbsTabWidth;
1287         }
1288 
1289         // Only use the relative widths' fraction, that is used for the
1290         // absolute width.
1291         sal_uLong nAbsTabWidthL = nAbsTabWidth;
1292         if (nRelAvail)
1293         {
1294             if (nAbsAvail == 0)
1295                 throw o3tl::divide_by_zero();
1296             m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
1297         }
1298         else
1299             m_nRelTabWidth = nAbsTabWidth;
1300 
1301         // Are there columns width a percentage setting and some without one?
1302         sal_uLong nFixMax = m_nMax;
1303         for( sal_uInt16 i=0; i<m_nCols; i++ )
1304         {
1305             const SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1306             if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 )
1307                 nFixMax -= pColumn->GetMax();
1308         }
1309 
1310         if( nFixMax > 0 && nFixMax < m_nMax )
1311         {
1312             // Yes, distribute the to-be-distributed space only to the
1313             // columns with a percentage setting.
1314 
1315             // In this case (and in this case only) there are columns
1316             // that exactly keep their maximum width, that is they neither
1317             // get smaller nor wider. When calculating the absolute width
1318             // from the relative width we can get rounding errors.
1319             // To correct this, we first make the fixed widths compensate for
1320             // this error. We then fix the relative widths the same way.
1321 
1322             sal_uInt16 nAbs = 0, nRel = 0;
1323             sal_uInt16 nFixedCols = 0;
1324             sal_uInt16 i;
1325 
1326             for( i = 0; i < m_nCols; i++ )
1327             {
1328                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1329                 if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() )
1330                 {
1331                     // The column keeps its width.
1332                     nFixedCols++;
1333                     sal_uLong nColMax = pColumn->GetMax();
1334                     pColumn->SetAbsColWidth( static_cast<sal_uInt16>(nColMax) );
1335 
1336                     sal_uLong nRelColWidth =
1337                         (nColMax * m_nRelTabWidth) / nAbsTabWidth;
1338                     sal_uLong nChkWidth =
1339                         (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth;
1340                     if( nChkWidth < nColMax )
1341                         nRelColWidth++;
1342                     else if( nChkWidth > nColMax )
1343                         nRelColWidth--;
1344                     pColumn->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );
1345 
1346                     nAbs = nAbs + static_cast<sal_uInt16>(nColMax);
1347                     nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
1348                 }
1349             }
1350 
1351             // The to-be-distributed percentage of the maximum, the
1352             // relative and absolute widths. Here, nFixMax corresponds
1353             // to nAbs, so that we could've called it nAbs.
1354             // The code is, however, more readable like that.
1355             OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" );
1356             sal_uLong nDistMax = m_nMax - nFixMax;
1357             sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs;
1358             sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel;
1359 
1360             for( i=0; i<m_nCols; i++ )
1361             {
1362                 SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
1363                 if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 )
1364                 {
1365                     // The column gets proportionately wider.
1366                     nFixedCols++;
1367                     if( nFixedCols == m_nCols )
1368                     {
1369                         pColumn->SetAbsColWidth( nAbsTabWidth-nAbs );
1370                         pColumn->SetRelColWidth( m_nRelTabWidth-nRel );
1371                     }
1372                     else
1373                     {
1374                         sal_uLong nColMax = pColumn->GetMax();
1375                         pColumn->SetAbsColWidth(
1376                             static_cast<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) );
1377                         pColumn->SetRelColWidth(
1378                             static_cast<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) );
1379                     }
1380                     nAbs = nAbs + pColumn->GetAbsColWidth();
1381                     nRel = nRel + pColumn->GetRelColWidth();
1382                 }
1383             }
1384             OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" );
1385         }
1386         else if (m_nCols > 0)
1387         {
1388             if (m_nMax == 0)
1389                 throw o3tl::divide_by_zero();
1390             // No. So distribute the space regularly among all columns.
1391             for (sal_uInt16 i=0; i < m_nCols; ++i)
1392             {
1393                 sal_uLong nColMax = GetColumn( i )->GetMax();
1394                 GetColumn( i )->SetAbsColWidth(
1395                     static_cast<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) );
1396                 GetColumn( i )->SetRelColWidth(
1397                     static_cast<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) );
1398             }
1399         }
1400     }
1401     else
1402     {
1403         // Proportionately distribute the space that extends over the minimum
1404         // width among the columns.
1405         if( !nAbsTabWidth )
1406             nAbsTabWidth = nAbsAvail;
1407         if( nAbsTabWidth < m_nMin )
1408             nAbsTabWidth = static_cast<sal_uInt16>(m_nMin);
1409 
1410         if( nAbsTabWidth > nAbsAvail )
1411         {
1412             OSL_ENSURE( IsTopTable(),
1413                     "A nested table should become wider than the available space." );
1414             nAbsAvail = nAbsTabWidth;
1415         }
1416 
1417         sal_uLong nAbsTabWidthL = nAbsTabWidth;
1418         if (nRelAvail)
1419         {
1420             if (nAbsAvail == 0)
1421                 throw o3tl::divide_by_zero();
1422             m_nRelTabWidth = static_cast<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
1423         }
1424         else
1425             m_nRelTabWidth = nAbsTabWidth;
1426         double nW = nAbsTabWidth - m_nMin;
1427         double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin);
1428         sal_uInt16 nAbs = 0, nRel = 0;
1429         for( sal_uInt16 i=0; i<m_nCols-1; i++ )
1430         {
1431             double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin();
1432             sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD);
1433             sal_uLong nRelColWidth = nRelAvail
1434                                     ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
1435                                     : nAbsColWidth;
1436 
1437             GetColumn( i )->SetAbsColWidth( static_cast<sal_uInt16>(nAbsColWidth) );
1438             GetColumn( i )->SetRelColWidth( static_cast<sal_uInt16>(nRelColWidth) );
1439             nAbs = nAbs + static_cast<sal_uInt16>(nAbsColWidth);
1440             nRel = nRel + static_cast<sal_uInt16>(nRelColWidth);
1441         }
1442         GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs );
1443         GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel );
1444 
1445     }
1446 
1447     // Step 4: For nested tables we can have balancing cells on the
1448     // left or right. Here we calculate their width.
1449     m_nInhAbsLeftSpace = 0;
1450     m_nInhAbsRightSpace = 0;
1451     if( !IsTopTable() && (m_nRelLeftFill>0 || m_nRelRightFill>0 ||
1452                           nAbsTabWidth<nAbsAvail) )
1453     {
1454         // Calculate the width of additional cells we use for
1455         // aligning inner tables.
1456         sal_uInt16 nAbsDist = static_cast<sal_uInt16>(nAbsAvail-nAbsTabWidth);
1457         sal_uInt16 nRelDist = static_cast<sal_uInt16>(nRelAvail-m_nRelTabWidth);
1458         sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0;
1459 
1460         // Calculate the size and position of the additional cells.
1461         switch( m_eTableAdjust )
1462         {
1463         case SvxAdjust::Right:
1464             nAbsLeftFill = nAbsLeftFill + nAbsDist;
1465             m_nRelLeftFill = m_nRelLeftFill + nRelDist;
1466             nParentInhAbsLeftSpace = nParentInhAbsSpace;
1467             break;
1468         case SvxAdjust::Center:
1469             {
1470                 sal_uInt16 nAbsLeftDist = nAbsDist / 2;
1471                 nAbsLeftFill = nAbsLeftFill + nAbsLeftDist;
1472                 nAbsRightFill += nAbsDist - nAbsLeftDist;
1473                 sal_uInt16 nRelLeftDist = nRelDist / 2;
1474                 m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist;
1475                 m_nRelRightFill += nRelDist - nRelLeftDist;
1476                 nParentInhAbsLeftSpace = nParentInhAbsSpace / 2;
1477                 nParentInhAbsRightSpace = nParentInhAbsSpace -
1478                                           nParentInhAbsLeftSpace;
1479             }
1480             break;
1481         case SvxAdjust::Left:
1482         default:
1483             nAbsRightFill = nAbsRightFill + nAbsDist;
1484             m_nRelRightFill = m_nRelRightFill + nRelDist;
1485             nParentInhAbsRightSpace = nParentInhAbsSpace;
1486             break;
1487         }
1488 
1489         // Filler widths are added to the outer columns, if there are no boxes
1490         // for them after the first pass (nWidth>0) or their width would become
1491         // too small or if there are COL tags and the filler width corresponds
1492         // to the border width.
1493         // In the last case we probably exported the table ourselves.
1494         if( m_nRelLeftFill &&
1495             ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth ||
1496               (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) )
1497         {
1498             SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 );
1499             pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
1500             pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
1501             m_nRelLeftFill = 0;
1502             m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
1503         }
1504         if( m_nRelRightFill &&
1505             ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth ||
1506               (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) )
1507         {
1508             SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 );
1509             pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
1510             pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
1511             m_nRelRightFill = 0;
1512             m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
1513         }
1514     }
1515 }
1516 
1517 static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth );
1518 
lcl_ResizeBox(const SwTableBox * pBox,SwTwips * pWidth)1519 static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth )
1520 {
1521     if( !pBox->GetSttNd() )
1522     {
1523         SwTwips nWidth = 0;
1524         for( const SwTableLine *pLine : pBox->GetTabLines() )
1525             lcl_ResizeLine( pLine, &nWidth );
1526         pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( ATT_VAR_SIZE, nWidth, 0 ));
1527         *pWidth = *pWidth + nWidth;
1528     }
1529     else
1530     {
1531         *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
1532     }
1533 }
1534 
lcl_ResizeLine(const SwTableLine * pLine,SwTwips * pWidth)1535 static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth )
1536 {
1537     SwTwips nOldWidth = *pWidth;
1538     *pWidth = 0;
1539     for( const SwTableBox* pBox : pLine->GetTabBoxes() )
1540         lcl_ResizeBox(pBox, pWidth );
1541 
1542     SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core",
1543                  "A box's rows have all a different length" );
1544 }
1545 
SetWidths(bool bCallPass2,sal_uInt16 nAbsAvail,sal_uInt16 nRelAvail,sal_uInt16 nAbsLeftSpace,sal_uInt16 nAbsRightSpace,sal_uInt16 nParentInhAbsSpace)1546 void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail,
1547                                    sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
1548                                    sal_uInt16 nAbsRightSpace,
1549                                    sal_uInt16 nParentInhAbsSpace )
1550 {
1551     // SetWidth must have been passed through once more for every cell in the
1552     // end.
1553     m_nWidthSet++;
1554 
1555     // Step 0: If necessary, we call the layout algorithm of Pass2.
1556     if( bCallPass2 )
1557         AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace,
1558                          nParentInhAbsSpace );
1559 
1560     // Step 1: Set the new width in all content boxes.
1561     // Because the boxes don't know anything about the HTML table structure,
1562     // we iterate over the HTML table structure.
1563     // For tables in tables in tables we call SetWidth recursively.
1564     for( sal_uInt16 i=0; i<m_nRows; i++ )
1565     {
1566         for( sal_uInt16 j=0; j<m_nCols; j++ )
1567         {
1568             SwHTMLTableLayoutCell *pCell = GetCell( i, j );
1569 
1570             SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get();
1571             while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
1572             {
1573                 SwTableBox *pBox = pContents->GetTableBox();
1574                 if( pBox )
1575                 {
1576                     SetBoxWidth( pBox, j, pCell->GetColSpan() );
1577                 }
1578                 else if (SwHTMLTableLayout *pTable = pContents->GetTable())
1579                 {
1580                     sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0,
1581                            nInhSpace = 0;
1582                     if( bCallPass2 )
1583                     {
1584                         sal_uInt16 nColSpan = pCell->GetColSpan();
1585                         GetAvail( j, nColSpan, nAbs, nRel );
1586                         nLSpace = GetLeftCellSpace( j, nColSpan );
1587                         nRSpace = GetRightCellSpace( j, nColSpan );
1588                         nInhSpace = GetInhCellSpace( j, nColSpan );
1589                     }
1590                     pTable->SetWidths( bCallPass2, nAbs, nRel,
1591                                                     nLSpace, nRSpace,
1592                                                     nInhSpace );
1593                 }
1594 
1595                 pContents->SetWidthSet( m_nWidthSet );
1596                 pContents = pContents->GetNext().get();
1597             }
1598         }
1599     }
1600 
1601     // Step 2: If we have a top table, we adapt the formats of the
1602     // non-content-boxes. Because they are not known in the HTML table
1603     // due to garbage collection there, we need the iterate over the
1604     // whole table.
1605     // We also adapt the table frame format. For nested tables we set the
1606     // filler cell's width instead.
1607     if( IsTopTable() )
1608     {
1609         SwTwips nCalcTabWidth = 0;
1610         for( const SwTableLine *pLine : m_pSwTable->GetTabLines() )
1611             lcl_ResizeLine( pLine, &nCalcTabWidth );
1612         SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core",
1613                      "Table width is not equal to the row width" );
1614 
1615         // Lock the table format when altering it, or else the box formats
1616         // are altered again.
1617         // Also, we need to preserve a percent setting if it exists.
1618         SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
1619         const_cast<SwTable *>(m_pSwTable)->LockModify();
1620         SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
1621         aFrameSize.SetWidth( m_nRelTabWidth );
1622         bool bRel = m_bUseRelWidth &&
1623                     text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
1624         aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) );
1625         pFrameFormat->SetFormatAttr( aFrameSize );
1626         const_cast<SwTable *>(m_pSwTable)->UnlockModify();
1627 
1628         // If the table is located in a frame, we also need to adapt the
1629         // frame's width.
1630         if( MayBeInFlyFrame() )
1631         {
1632             SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat();
1633             if( pFlyFrameFormat )
1634             {
1635                 SwFormatFrameSize aFlyFrameSize( ATT_VAR_SIZE, m_nRelTabWidth, MINLAY );
1636 
1637                 if( m_bUseRelWidth )
1638                 {
1639                     // For percentage settings we set the width to the minimum.
1640                     aFlyFrameSize.SetWidth(  m_nMin > USHRT_MAX ? USHRT_MAX
1641                                                             : m_nMin );
1642                     aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) );
1643                 }
1644                 pFlyFrameFormat->SetFormatAttr( aFlyFrameSize );
1645             }
1646         }
1647 
1648 #ifdef DBG_UTIL
1649         {
1650             // check if the tables have correct widths
1651             SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth();
1652             const SwTableLines& rLines = m_pSwTable->GetTabLines();
1653             for (size_t n = 0; n < rLines.size(); ++n)
1654             {
1655                 CheckBoxWidth( *rLines[ n ], nSize );
1656             }
1657         }
1658 #endif
1659 
1660     }
1661 }
1662 
Resize_(sal_uInt16 nAbsAvail,bool bRecalc)1663 void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc )
1664 {
1665     // If bRecalc is set, the table's content changed.
1666     // We need to execute pass 1 again.
1667     if( bRecalc )
1668         AutoLayoutPass1();
1669 
1670     SwRootFrame *pRoot = GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
1671     if ( pRoot && pRoot->IsCallbackActionEnabled() )
1672         pRoot->StartAllAction();
1673 
1674     // Else we can set the widths, in which we have to run Pass 2 in each case.
1675     SetWidths( true, nAbsAvail );
1676 
1677     if ( pRoot && pRoot->IsCallbackActionEnabled() )
1678         pRoot->EndAllAction( true );    //True per VirDev (browsing is calmer)
1679 }
1680 
IMPL_LINK_NOARG(SwHTMLTableLayout,DelayedResize_Impl,Timer *,void)1681 IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void )
1682 {
1683     m_aResizeTimer.Stop();
1684     Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc );
1685 }
1686 
Resize(sal_uInt16 nAbsAvail,bool bRecalc,bool bForce,sal_uLong nDelay)1687 bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc,
1688                                 bool bForce, sal_uLong nDelay )
1689 {
1690     if( 0 == nAbsAvail )
1691         return false;
1692     OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );
1693 
1694     // May the table be resized at all? Or is it forced?
1695     if( m_bMustNotResize && !bForce )
1696         return false;
1697 
1698     // May the table be recalculated? Or is it forced?
1699     if( m_bMustNotRecalc && !bForce )
1700         bRecalc = false;
1701 
1702     const SwDoc *pDoc = GetDoc();
1703 
1704     // If there is a layout, the root frame's size instead of the
1705     // VisArea's size was potentially passed.
1706     // If we're not in a frame we need to calculate the table for the VisArea,
1707     // because switching from relative to absolute wouldn't work.
1708     if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
1709     {
1710         const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( *pDoc );
1711         if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() )
1712             nAbsAvail = nVisAreaWidth;
1713     }
1714 
1715     if( nDelay==0 && m_aResizeTimer.IsActive() )
1716     {
1717         m_nDelayedResizeAbsAvail = nAbsAvail;
1718         return false;
1719     }
1720 
1721     // Optimisation:
1722     // If the minimum or maximum should not be recalculated and
1723     // - the table's width never needs to be recalculated, or
1724     // - the table was already calculated for the passed width, or
1725     // - the available space is less or equal to the minimum width
1726     //   and the table already has the minimum width, or
1727     // - the available space is larger than the maximum width and
1728     //   the table already has the maximum width
1729     // nothing will happen to the table.
1730     if( !bRecalc && ( !m_bMustResize ||
1731                       (m_nLastResizeAbsAvail==nAbsAvail) ||
1732                       (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) ||
1733                       (!m_bPrcWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) )
1734         return false;
1735 
1736     if( nDelay==HTMLTABLE_RESIZE_NOW )
1737     {
1738         if( m_aResizeTimer.IsActive() )
1739             m_aResizeTimer.Stop();
1740         Resize_( nAbsAvail, bRecalc );
1741     }
1742     else if( nDelay > 0 )
1743     {
1744         m_nDelayedResizeAbsAvail = nAbsAvail;
1745         m_bDelayedResizeRecalc = bRecalc;
1746         m_aResizeTimer.SetTimeout( nDelay );
1747         m_aResizeTimer.Start();
1748     }
1749     else
1750     {
1751         Resize_( nAbsAvail, bRecalc );
1752     }
1753 
1754     return true;
1755 }
1756 
BordersChanged(sal_uInt16 nAbsAvail)1757 void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail )
1758 {
1759     m_bBordersChanged = true;
1760 
1761     Resize( nAbsAvail, true/*bRecalc*/ );
1762 }
1763 
1764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1765