LibreOffice Module svx (master)  1
tablelayouter.cxx
Go to the documentation of this file.
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 
21 #include <com/sun/star/table/XMergeableCell.hpp>
22 
23 #include <tools/debug.hxx>
24 #include <tools/diagnose_ex.h>
25 #include <tools/gen.hxx>
26 #include <libxml/xmlwriter.h>
27 
28 #include <cell.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <tablemodel.hxx>
31 #include "tablelayouter.hxx"
32 #include <svx/svdotable.hxx>
33 #include <editeng/borderline.hxx>
34 #include <editeng/boxitem.hxx>
35 
36 using ::editeng::SvxBorderLine;
37 using namespace ::com::sun::star::uno;
38 using namespace ::com::sun::star::lang;
39 using namespace ::com::sun::star::container;
40 using namespace ::com::sun::star::beans;
41 using namespace ::com::sun::star::table;
42 using namespace ::com::sun::star::text;
43 
44 
45 namespace sdr::table {
46 
47 
48 static SvxBorderLine gEmptyBorder;
49 
50 constexpr OUStringLiteral gsSize( u"Size" );
51 
53 : mxTable( xTableModel )
54 {
55 }
56 
57 
59 {
61 }
62 
63 
64 basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
65 {
66  sal_Int32 width = 0;
67  sal_Int32 height = 0;
68 
69  try
70  {
71  if( xCell.is() && !xCell->isMerged() )
72  {
73  CellPos aPos( rPos );
74 
75  sal_Int32 nRowCount = getRowCount();
76  sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), sal_Int32(1) );
77  while( nRowSpan && (aPos.mnRow < nRowCount) )
78  {
79  if( static_cast<sal_Int32>(maRows.size()) <= aPos.mnRow )
80  break;
81 
82  height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize);
83  nRowSpan--;
84  }
85 
86  sal_Int32 nColCount = getColumnCount();
87  sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), sal_Int32(1) );
88  while( nColSpan && (aPos.mnCol < nColCount ) )
89  {
90  if( static_cast<sal_Int32>(maColumns.size()) <= aPos.mnCol )
91  break;
92 
93  width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize);
94  nColSpan--;
95  }
96  }
97  }
98  catch( Exception& )
99  {
100  TOOLS_WARN_EXCEPTION("svx", "");
101  }
102 
103  return basegfx::B2ITuple( width, height );
104 }
105 
106 
107 bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
108 {
109  try
110  {
111  if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
112  {
113  const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
114  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
115 
116  if( (rPos.mnCol < static_cast<sal_Int32>(maColumns.size())) && (rPos.mnRow < static_cast<sal_Int32>(maRows.size()) ) )
117  {
118  const sal_Int32 y = maRows[rPos.mnRow].mnPos;
119 
120  sal_Int32 endy;
121  if (o3tl::checked_add(y, aCellSize.getY(), endy))
122  return false;
123 
124  if(bRTL)
125  {
127  const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
128  sal_Int32 startx;
129  if (o3tl::checked_sub(x, aCellSize.getX(), startx))
130  return false;
131  rArea = basegfx::B2IRectangle(startx, y, x, endy);
132  }
133  else
134  {
135  const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
136  sal_Int32 endx;
137  if (o3tl::checked_add(x, aCellSize.getX(), endx))
138  return false;
139  rArea = basegfx::B2IRectangle(x, y, endx, endy);
140  }
141  return true;
142  }
143  }
144  }
145  catch( Exception& )
146  {
147  TOOLS_WARN_EXCEPTION("svx", "");
148  }
149  return false;
150 }
151 
152 
153 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
154 {
155  if( isValidRow(nRow) )
156  return maRows[nRow].mnSize;
157  else
158  return 0;
159 }
160 
161 
162 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
163 {
164  if( isValidColumn(nColumn) )
165  return maColumns[nColumn].mnSize;
166  else
167  return 0;
168 }
169 
170 sal_Int32 TableLayouter::calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const
171 {
172  sal_Int32 nRet = 0;
173  for ( sal_uInt32 nRow = 0; nRow < static_cast<sal_uInt32>(maRows.size()); ++nRow )
174  {
175  // Account for the space desired by spanned columns.
176  // Only the last spanned cell will try to ensure sufficient space,
177  // by looping through the previous columns, subtracting their portion.
178  sal_Int32 nWish = 0;
179  sal_Int32 nSpannedColumn = nColumn;
180  bool bFindSpan = true;
181  while ( bFindSpan && isValidColumn(nSpannedColumn) )
182  {
183  // recursive function call gets earlier portion of spanned column.
184  if ( nSpannedColumn < nColumn )
185  nWish -= calcPreferredColumnWidth( nSpannedColumn, aSize );
186 
187  CellRef xCell( getCell( CellPos( nSpannedColumn, nRow ) ) );
188  if ( xCell.is() && !xCell->isMerged()
189  && (xCell->getColumnSpan() == 1 || nSpannedColumn < nColumn) )
190  {
191  nWish += xCell->calcPreferredWidth(aSize);
192  bFindSpan = false;
193  }
194  else if ( xCell.is() && xCell->isMerged()
195  && nColumn == nSpannedColumn
196  && isValidColumn(nColumn + 1) )
197  {
198  xCell = getCell( CellPos( nColumn + 1, nRow ) );
199  bFindSpan = xCell.is() && !xCell->isMerged();
200  }
201  nSpannedColumn--;
202  }
203  nRet = std::max( nRet, nWish );
204  }
205  return nRet;
206 }
207 
208 
209 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
210 {
211  const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
212 
213  if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
214  (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
215  {
216  return rMap[nEdgeX][nEdgeY] != nullptr;
217  }
218  else
219  {
220  OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
221  }
222 
223  return false;
224 }
225 
226 
228 SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
229 {
230  SvxBorderLine* pLine = nullptr;
231 
232  const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
233 
234  if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
235  (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
236  {
237  pLine = rMap[nEdgeX][nEdgeY];
238  if( pLine == &gEmptyBorder )
239  pLine = nullptr;
240  }
241  else
242  {
243  OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
244  }
245 
246  return pLine;
247 }
248 
249 std::vector<EdgeInfo> TableLayouter::getHorizontalEdges()
250 {
251  std::vector<EdgeInfo> aReturn;
252  sal_Int32 nRowSize = sal_Int32(maRows.size());
253  for (sal_Int32 i = 0; i <= nRowSize; i++)
254  {
255  sal_Int32 nEdgeMin = 0;
256  sal_Int32 nEdgeMax = 0;
257  sal_Int32 nEdge = getHorizontalEdge(i, &nEdgeMin, &nEdgeMax);
258  nEdgeMin -= nEdge;
259  nEdgeMax -= nEdge;
260  aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
261  }
262  return aReturn;
263 }
264 
265 std::vector<EdgeInfo> TableLayouter::getVerticalEdges()
266 {
267  std::vector<EdgeInfo> aReturn;
268  sal_Int32 nColumnSize = sal_Int32(maColumns.size());
269  for (sal_Int32 i = 0; i <= nColumnSize; i++)
270  {
271  sal_Int32 nEdgeMin = 0;
272  sal_Int32 nEdgeMax = 0;
273  sal_Int32 nEdge = getVerticalEdge(i, &nEdgeMin, &nEdgeMax);
274  nEdgeMin -= nEdge;
275  nEdgeMax -= nEdge;
276  aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
277  }
278  return aReturn;
279 }
280 
281 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
282 {
283  sal_Int32 nRet = 0;
284  const sal_Int32 nRowCount = getRowCount();
285  if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
286  nRet = maRows[std::min(static_cast<sal_Int32>(nEdgeY),nRowCount-1)].mnPos;
287 
288  if( nEdgeY == nRowCount )
289  nRet += maRows[nEdgeY - 1].mnSize;
290 
291  if( pnMin )
292  {
293  if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
294  {
295  *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
296  }
297  else
298  {
299  *pnMin = nRet;
300  }
301  }
302 
303  if( pnMax )
304  {
305  *pnMax = 0x0fffffff;
306  }
307  return nRet;
308 }
309 
310 
311 sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
312 {
313  sal_Int32 nRet = 0;
314 
315  const sal_Int32 nColCount = getColumnCount();
316  if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
317  nRet = maColumns[std::min(static_cast<sal_Int32>(nEdgeX),nColCount-1)].mnPos;
318 
319  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
320  if( bRTL )
321  {
322  if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
323  nRet += maColumns[nEdgeX].mnSize;
324  }
325  else
326  {
327  if( nEdgeX == nColCount )
328  nRet += maColumns[nEdgeX - 1].mnSize;
329  }
330 
331  if( pnMin )
332  {
333  *pnMin = nRet;
334  if( bRTL )
335  {
336  if( nEdgeX < nColCount )
337  *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
338  }
339  else
340  {
341  if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
342  *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
343  }
344  }
345 
346  if( pnMax )
347  {
348  *pnMax = 0x0fffffff; // todo
349  if( bRTL )
350  {
351  if( nEdgeX > 0 )
352  *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
353  }
354  else
355  {
356  if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
357  *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
358  }
359  }
360 
361  return nRet;
362 }
363 
364 
365 static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
366 {
367  Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
368  if( xCell.is() && !xCell->isMerged() )
369  {
370  const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
371  const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
372  if( (nMergedX < nRight) && (nMergedY < nBottom) )
373  return true;
374 
375  bRunning = false;
376  }
377  return false;
378 }
379 
383 bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
384 {
385  rOriginX = nMergedX;
386  rOriginY = nMergedY;
387 
388  if( xTable.is() ) try
389  {
390  // check if this cell already the origin or not merged at all
391  Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
392  if( !xCell->isMerged() )
393  return true;
394 
395  bool bCheckVert = true;
396  bool bCheckHorz = true;
397 
398  sal_Int32 nMinCol = 0;
399  sal_Int32 nMinRow = 0;
400 
401  sal_Int32 nStep = 1, i;
402 
403  sal_Int32 nRow, nCol;
404  do
405  {
406  if( bCheckVert )
407  {
408  nRow = nMergedY - nStep;
409  if( nRow >= nMinRow )
410  {
411  nCol = nMergedX;
412  for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
413  {
414  if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
415  {
416  rOriginX = nCol; rOriginY = nRow;
417  return true;
418  }
419 
420  if( !bCheckVert )
421  {
422  if( nCol == nMergedX )
423  {
424  nMinRow = nRow+1;
425  }
426  else
427  {
428  bCheckVert = true;
429  }
430  break;
431  }
432  }
433  }
434  else
435  {
436  bCheckVert = false;
437  }
438  }
439 
440  if( bCheckHorz )
441  {
442  nCol = nMergedX - nStep;
443  if( nCol >= nMinCol )
444  {
445  nRow = nMergedY;
446  for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
447  {
448  if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
449  {
450  rOriginX = nCol; rOriginY = nRow;
451  return true;
452  }
453 
454  if( !bCheckHorz )
455  {
456  if( nRow == nMergedY )
457  {
458  nMinCol = nCol+1;
459  }
460  else
461  {
462  bCheckHorz = true;
463  }
464  break;
465  }
466  }
467  }
468  else
469  {
470  bCheckHorz = false;
471  }
472  }
473  nStep++;
474  }
475  while( bCheckVert || bCheckHorz );
476  }
477  catch( Exception& )
478  {
479  TOOLS_WARN_EXCEPTION("svx", "");
480  }
481  return false;
482 }
483 
484 
485 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
486 {
487  if( isValidColumn( nColumn ) )
488  {
489  return maColumns[nColumn].mnMinSize;
490  }
491  else
492  {
493  OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
494  return 0;
495  }
496 }
497 
498 
499 sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
500 {
501  // break loops after 100 runs to avoid freezing office due to developer error
502  sal_Int32 nSafe = 100;
503 
504  const std::size_t nCount = rLayouts.size();
505  std::size_t nIndex;
506 
507  bool bConstrainsBroken = false;
508 
509  do
510  {
511  bConstrainsBroken = false;
512 
513  // first enforce minimum size constrains on all entities
514  for( nIndex = 0; nIndex < nCount; ++nIndex )
515  {
516  Layout& rLayout = rLayouts[nIndex];
517  if( rLayout.mnSize < rLayout.mnMinSize )
518  {
519  sal_Int32 nDiff(0);
520  bConstrainsBroken |= o3tl::checked_sub(rLayout.mnMinSize, rLayout.mnSize, nDiff);
521  bConstrainsBroken |= o3tl::checked_sub(nDistribute, nDiff, nDistribute);
522  rLayout.mnSize = rLayout.mnMinSize;
523  }
524  }
525 
526  // calculate current width
527  // if nDistribute is < 0 (shrinking), entities that are already
528  // at minimum width are not counted
529  sal_Int32 nCurrentWidth = 0;
530  for( nIndex = 0; nIndex < nCount; ++nIndex )
531  {
532  Layout& rLayout = rLayouts[nIndex];
533  if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
534  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, rLayout.mnSize);
535  }
536 
537  // now distribute over entities
538  if( (nCurrentWidth != 0) && (nDistribute != 0) )
539  {
540  sal_Int32 nDistributed = nDistribute;
541  for( nIndex = 0; nIndex < nCount; ++nIndex )
542  {
543  Layout& rLayout = rLayouts[nIndex];
544  if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
545  {
546  sal_Int32 n(nDistributed); // for last entity use up rest
547  if (nIndex != (nCount-1))
548  {
549  bConstrainsBroken |= o3tl::checked_multiply(nDistribute, rLayout.mnSize, n);
550  n /= nCurrentWidth;
551  }
552 
553  bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize);
554  nDistributed -= n;
555 
556  if( rLayout.mnSize < rLayout.mnMinSize )
557  bConstrainsBroken = true;
558  }
559  }
560  }
561  } while( bConstrainsBroken && --nSafe );
562 
563  sal_Int32 nSize = 0;
564  for( nIndex = 0; nIndex < nCount; ++nIndex )
565  nSize = o3tl::saturating_add(nSize, rLayouts[nIndex].mnSize);
566 
567  return nSize;
568 }
569 
570 
571 typedef std::vector< CellRef > MergeableCellVector;
572 typedef std::vector< MergeableCellVector > MergeVector;
573 
574 
576 {
577  const sal_Int32 nColCount = getColumnCount();
578  const sal_Int32 nRowCount = getRowCount();
579  if( nColCount == 0 )
580  return;
581 
582  MergeVector aMergedCells( nColCount );
583  std::vector<sal_Int32> aOptimalColumns;
584 
585  static const OUStringLiteral sOptimalSize(u"OptimalSize");
586 
587  if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
588  maColumns.resize( nColCount );
589 
590  Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
591 
592  // first calculate current width and initial minimum width per column,
593  // merged cells will be counted later
594  sal_Int32 nCurrentWidth = 0;
595  sal_Int32 nCol = 0, nRow = 0;
596  for( nCol = 0; nCol < nColCount; nCol++ )
597  {
598  sal_Int32 nMinWidth = 0;
599 
600  bool bIsEmpty = true; // check if all cells in this column are merged
601 
602  for( nRow = 0; nRow < nRowCount; ++nRow )
603  {
604  CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
605  if( xCell.is() && !xCell->isMerged() )
606  {
607  bIsEmpty = false;
608 
609  sal_Int32 nColSpan = xCell->getColumnSpan();
610  if( nColSpan > 1 )
611  {
612  // merged cells will be evaluated later
613  aMergedCells[nCol+nColSpan-1].push_back( xCell );
614  }
615  else
616  {
617  nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
618  }
619  }
620  }
621 
622  maColumns[nCol].mnMinSize = nMinWidth;
623 
624  if( bIsEmpty )
625  {
626  maColumns[nCol].mnSize = 0;
627  }
628  else
629  {
630  sal_Int32 nColWidth = 0;
631  Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
632  bool bOptimal = false;
633  xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
634  if( bOptimal )
635  {
636  aOptimalColumns.push_back(nCol);
637  }
638  else
639  {
640  xColSet->getPropertyValue( gsSize ) >>= nColWidth;
641  }
642 
643  maColumns[nCol].mnSize = std::max( nColWidth, nMinWidth);
644 
645  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize);
646  }
647  }
648 
649  // if we have optimal sized rows, distribute what is given (left)
650  if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
651  {
652  sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
653  sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
654 
655  auto iter( aOptimalColumns.begin() );
656  while( iter != aOptimalColumns.end() )
657  {
658  sal_Int32 nOptCol = *iter++;
659  if( iter == aOptimalColumns.end() )
660  nDistribute = nLeft;
661 
662  maColumns[nOptCol].mnSize += nDistribute;
663  nLeft -= nDistribute;
664  }
665 
666  DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
667  }
668 
669  // now check if merged cells fit
670  for( nCol = 1; nCol < nColCount; ++nCol )
671  {
672  bool bChanges = false;
673 
674  const sal_Int32 nOldSize = maColumns[nCol].mnSize;
675 
676  for( const CellRef& xCell : aMergedCells[nCol] )
677  {
678  sal_Int32 nMinWidth = xCell->getMinimumWidth();
679 
680  for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
681  nMinWidth -= maColumns[nMCol].mnSize;
682 
683  if( nMinWidth > maColumns[nCol].mnMinSize )
684  maColumns[nCol].mnMinSize = nMinWidth;
685 
686  if( nMinWidth > maColumns[nCol].mnSize )
687  {
688  maColumns[nCol].mnSize = nMinWidth;
689  bChanges = true;
690  }
691  }
692 
693  if( bChanges )
694  {
695  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize - nOldSize);
696  }
697  }
698 
699  // now scale if wanted and needed
700  if( bFit && (nCurrentWidth != rArea.getWidth()) )
701  distribute( maColumns, rArea.getWidth() - nCurrentWidth );
702 
703  // last step, update left edges
704  sal_Int32 nNewWidth = 0;
705 
706  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
707  RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
708  while( coliter.next(nCol ) )
709  {
710  maColumns[nCol].mnPos = nNewWidth;
711  nNewWidth = o3tl::saturating_add(nNewWidth, maColumns[nCol].mnSize);
712  if( bFit )
713  {
714  Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
715  xColSet->setPropertyValue( gsSize, Any( maColumns[nCol].mnSize ) );
716  }
717  }
718 
719  rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
720  updateCells( rArea );
721 }
722 
723 
725 {
726  const sal_Int32 nColCount = getColumnCount();
727  const sal_Int32 nRowCount = getRowCount();
728 
729  if( nRowCount == 0 )
730  return;
731 
732  Reference< XTableRows > xRows( mxTable->getRows() );
733 
734  MergeVector aMergedCells( nRowCount );
735  std::vector<sal_Int32> aOptimalRows;
736 
737  static const OUStringLiteral sOptimalSize(u"OptimalSize");
738 
739  // first calculate current height and initial minimum size per column,
740  // merged cells will be counted later
741  sal_Int32 nCurrentHeight = 0;
742  sal_Int32 nCol, nRow;
743  for( nRow = 0; nRow < nRowCount; ++nRow )
744  {
745  Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
746  sal_Int32 nRowPropHeight = 0;
747  xRowSet->getPropertyValue( gsSize ) >>= nRowPropHeight;
748  sal_Int32 nMinHeight = 0;
749 
750  bool bIsEmpty = true; // check if all cells in this row are merged
751  bool bRowHasText = false;
752  bool bRowHasCellInEditMode = false;
753 
754  for( nCol = 0; nCol < nColCount; ++nCol )
755  {
756  CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
757  if( xCell.is() && !xCell->isMerged() )
758  {
759  bIsEmpty = false;
760 
761  sal_Int32 nRowSpan = xCell->getRowSpan();
762  if( nRowSpan > 1 )
763  {
764  // merged cells will be evaluated later
765  aMergedCells[nRow+nRowSpan-1].push_back( xCell );
766  }
767  else
768  {
769  // WARNING: tdf#144092 / tdf#139511 suggest this entire section is invalid.
770  // Empty cells should not be ignored in regards to row height,
771  // especially MS formats, despite this code being added to import MS files...
772  // The problem is getMinimumHeight can give wrong values for empty cells.
773 
774  bool bCellHasText = xCell->hasText();
775  bool bCellInEditMode = xCell->IsTextEditActive();
776 
777  if (!bRowHasCellInEditMode && bCellInEditMode)
778  bRowHasCellInEditMode = true;
779 
780  if ((bRowHasText == bCellHasText) || (bRowHasText && bCellInEditMode))
781  {
782  nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
783  }
784  else if ( !bRowHasText && bCellHasText )
785  {
786  bRowHasText = true;
787  nMinHeight = xCell->getMinimumHeight();
788  }
789 
790  // tdf#137949 We should consider "Height" property while calculating minimum height.
791  // This control decides when we use "Height" property value instead of calculated minimum height
792  // Case 1: * Row has "Height" property
793  // * Calculated minimum height is smaller than Height property value.
794  // Case 2: * Row has "Height" property
795  // * Calculated minimum height is bigger than Height property value and
796  // * Row has not any text of any cell in edit mode in the row (means completely empty)
797  if ((nMinHeight < nRowPropHeight && nRowPropHeight > 0 && (bRowHasText || bRowHasCellInEditMode)) ||
798  (nMinHeight > nRowPropHeight && nRowPropHeight > 0 && (!bRowHasText && !bRowHasCellInEditMode)))
799  {
800  nMinHeight = nRowPropHeight;
801  }
802  }
803  }
804  }
805 
806  maRows[nRow].mnMinSize = nMinHeight;
807 
808  if( bIsEmpty )
809  {
810  maRows[nRow].mnSize = 0;
811  }
812  else
813  {
814  sal_Int32 nRowHeight = 0;
815 
816  bool bOptimal = false;
817  xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
818  if( bOptimal )
819  {
820  aOptimalRows.push_back( nRow );
821  }
822  else
823  {
824  xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
825  }
826 
827  maRows[nRow].mnSize = nRowHeight;
828 
829  if( maRows[nRow].mnSize < nMinHeight )
830  maRows[nRow].mnSize = nMinHeight;
831 
832  nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize);
833  }
834  }
835 
836  // if we have optimal sized rows, distribute what is given (left)
837  if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
838  {
839  sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
840  sal_Int32 nDistribute = nLeft / aOptimalRows.size();
841 
842  auto iter( aOptimalRows.begin() );
843  while( iter != aOptimalRows.end() )
844  {
845  sal_Int32 nOptRow = *iter++;
846  if( iter == aOptimalRows.end() )
847  nDistribute = nLeft;
848 
849  maRows[nOptRow].mnSize += nDistribute;
850  nLeft -= nDistribute;
851 
852  }
853 
854  DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
855  }
856 
857  // now check if merged cells fit
858  for( nRow = 1; nRow < nRowCount; ++nRow )
859  {
860  bool bChanges = false;
861  sal_Int32 nOldSize = maRows[nRow].mnSize;
862 
863  for( const CellRef& xCell : aMergedCells[nRow] )
864  {
865  sal_Int32 nMinHeight = xCell->getMinimumHeight();
866 
867  for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
868  nMinHeight -= maRows[nMRow].mnSize;
869 
870  if( nMinHeight > maRows[nRow].mnMinSize )
871  maRows[nRow].mnMinSize = nMinHeight;
872 
873  if( nMinHeight > maRows[nRow].mnSize )
874  {
875  maRows[nRow].mnSize = nMinHeight;
876  bChanges = true;
877  }
878  }
879  if( bChanges )
880  nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize - nOldSize);
881  }
882 
883  // now scale if wanted and needed
884  if( bFit && nCurrentHeight != rArea.getHeight() )
885  distribute(maRows, o3tl::saturating_sub<sal_Int32>(rArea.getHeight(), nCurrentHeight));
886 
887  // last step, update left edges
888  sal_Int32 nNewHeight = 0;
889  for( nRow = 0; nRow < nRowCount; ++nRow )
890  {
891  maRows[nRow].mnPos = nNewHeight;
892  nNewHeight = o3tl::saturating_add(nNewHeight, maRows[nRow].mnSize);
893 
894  if( bFit )
895  {
896  Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
897  xRowSet->setPropertyValue( gsSize, Any( maRows[nRow].mnSize ) );
898  }
899  }
900 
901  rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
902  updateCells( rArea );
903 }
904 
905 
908 void TableLayouter::LayoutTable( tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
909 {
910  if( !mxTable.is() )
911  return;
912 
913  const sal_Int32 nRowCount = mxTable->getRowCount();
914  const sal_Int32 nColCount = mxTable->getColumnCount();
915 
916  if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
917  {
918  if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
919  maRows.resize( nRowCount );
920 
921  for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
922  maRows[nRow].clear();
923 
924  if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
925  maColumns.resize( nColCount );
926 
927  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
928  maColumns[nCol].clear();
929  }
930 
931  LayoutTableWidth( rRectangle, bFitWidth );
932  LayoutTableHeight( rRectangle, bFitHeight );
934 }
935 
936 
938 {
939  const sal_Int32 nColCount = getColumnCount();
940  const sal_Int32 nRowCount = getRowCount();
941 
942  CellPos aPos;
943  for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
944  {
945  for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
946  {
947  CellRef xCell( getCell( aPos ) );
948  if( xCell.is() )
949  {
950  basegfx::B2IRectangle aCellArea;
951  if( getCellArea( xCell, aPos, aCellArea ) )
952  {
953  tools::Rectangle aCellRect;
954  aCellRect.SetLeft( aCellArea.getMinX() );
955  aCellRect.SetRight( aCellArea.getMaxX() );
956  aCellRect.SetTop( aCellArea.getMinY() );
957  aCellRect.SetBottom( aCellArea.getMaxY() );
958  aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
959  xCell->setCellRect( aCellRect );
960  }
961  }
962  }
963  }
964 }
965 
966 
968 {
969  CellRef xCell;
970  if( mxTable.is() ) try
971  {
972  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
973  }
974  catch( Exception& )
975  {
976  TOOLS_WARN_EXCEPTION("svx", "");
977  }
978  return xCell;
979 }
980 
981 
982 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
983 {
984  if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr)))
985  return false;
986  if (!pOther || (pOther == &gEmptyBorder))
987  return true;
988 
989  sal_uInt16 nThisSize = pThis->GetScaledWidth();
990  sal_uInt16 nOtherSize = pOther->GetScaledWidth();
991 
992  if (nThisSize > nOtherSize)
993  return true;
994 
995  else if (nThisSize < nOtherSize)
996  {
997  return false;
998  }
999  else
1000  {
1001  if ( pOther->GetInWidth() && !pThis->GetInWidth() )
1002  {
1003  return true;
1004  }
1005  else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
1006  {
1007  return false;
1008  }
1009  else
1010  {
1011  return true;
1012  }
1013  }
1014 }
1015 
1016 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
1017 {
1018  if (!pLine)
1019  pLine = &gEmptyBorder;
1020 
1021  BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
1022 
1023  if( (nCol >= 0) && (nCol < sal::static_int_cast<sal_Int32>(rMap.size())) &&
1024  (nRow >= 0) && (nRow < sal::static_int_cast<sal_Int32>(rMap[nCol].size())) )
1025  {
1026  SvxBorderLine *pOld = rMap[nCol][nRow];
1027 
1028  if (HasPriority(pLine, pOld))
1029  {
1030  if (pOld && pOld != &gEmptyBorder)
1031  delete pOld;
1032 
1033  SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
1034 
1035  rMap[nCol][nRow] = pNew;
1036  }
1037  }
1038  else
1039  {
1040  OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" );
1041  }
1042 }
1043 
1045 {
1048 }
1049 
1051 {
1052  const sal_Int32 nColCount = rMap.size();
1053 
1054  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1055  {
1056  const sal_Int32 nRowCount = rMap[nCol].size();
1057  for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1058  {
1059  SvxBorderLine* pLine = rMap[nCol][nRow];
1060  if( pLine )
1061  {
1062  if( pLine != &gEmptyBorder )
1063  delete pLine;
1064 
1065  rMap[nCol][nRow] = nullptr;
1066  }
1067  }
1068  }
1069 }
1070 
1072 {
1076 }
1077 
1078 
1080 {
1081  const sal_Int32 nColCount = getColumnCount() + 1;
1082  const sal_Int32 nRowCount = getRowCount() + 1;
1083 
1084  if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
1085  rMap.resize( nColCount );
1086 
1087  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1088  {
1089  if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
1090  rMap[nCol].resize( nRowCount );
1091  }
1092 }
1093 
1094 
1096 {
1097  // make sure old border layout is cleared and border maps have correct size
1099 
1100  const sal_Int32 nColCount = getColumnCount();
1101  const sal_Int32 nRowCount = getRowCount();
1102 
1103  CellPos aPos;
1104  for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1105  {
1106  for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1107  {
1108  CellRef xCell( getCell( aPos ) );
1109  if( !xCell.is() )
1110  continue;
1111 
1112  const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER );
1113  OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1114 
1115  if( !pThisAttr )
1116  continue;
1117 
1118  const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1119  const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1120 
1121  for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1122  {
1123  SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1124  SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1125  }
1126 
1127  for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1128  {
1129  SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1130  SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1131  }
1132  }
1133  }
1134 }
1135 
1136 
1138  sal_Int32 nFirstCol,
1139  sal_Int32 nLastCol,
1140  const bool bOptimize,
1141  const bool bMinimize )
1142 {
1143  if( !mxTable.is() )
1144  return;
1145 
1146  try
1147  {
1148  const sal_Int32 nColCount = getColumnCount();
1149  Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
1150  const Size aSize(0xffffff, 0xffffff);
1151 
1152  //special case - optimize a single column
1153  if ( (bOptimize || bMinimize) && nFirstCol == nLastCol )
1154  {
1155  const sal_Int32 nWish = calcPreferredColumnWidth(nFirstCol, aSize);
1156  if ( nWish < getColumnWidth(nFirstCol) )
1157  {
1158  Reference< XPropertySet > xColSet( xCols->getByIndex(nFirstCol), UNO_QUERY_THROW );
1159  xColSet->setPropertyValue( gsSize, Any( nWish ) );
1160 
1161  //FitWidth automatically distributes the new excess space
1162  LayoutTable( rArea, /*bFitWidth=*/!bMinimize, /*bFitHeight=*/false );
1163  }
1164  }
1165 
1166  if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
1167  return;
1168 
1169  sal_Int32 nAllWidth = 0;
1170  float fAllWish = 0;
1171  sal_Int32 nUnused = 0;
1172  std::vector<sal_Int32> aWish(nColCount);
1173 
1174  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1175  nAllWidth += getColumnWidth(nCol);
1176 
1177  const sal_Int32 nEqualWidth = nAllWidth / (nLastCol-nFirstCol+1);
1178 
1179  //pass 1 - collect unneeded space (from an equal width perspective)
1180  if ( bMinimize || bOptimize )
1181  {
1182  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1183  {
1184  const sal_Int32 nIndex = nCol - nFirstCol;
1185  aWish[nIndex] = calcPreferredColumnWidth(nCol, aSize);
1186  fAllWish += aWish[nIndex];
1187  if ( aWish[nIndex] < nEqualWidth )
1188  nUnused += nEqualWidth - aWish[nIndex];
1189  }
1190  }
1191  const sal_Int32 nDistributeExcess = nAllWidth - fAllWish;
1192 
1193  sal_Int32 nWidth = nEqualWidth;
1194  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1195  {
1196  if ( !bMinimize && nCol == nLastCol )
1197  nWidth = nAllWidth; // last column gets rounding/logic errors
1198  else if ( (bMinimize || bOptimize) && fAllWish )
1199  {
1200  //pass 2 - first come, first served when requesting from the
1201  // unneeded pool, or proportionally allocate excess.
1202  const sal_Int32 nIndex = nCol - nFirstCol;
1203  if ( aWish[nIndex] > nEqualWidth + nUnused )
1204  {
1205  nWidth = nEqualWidth + nUnused;
1206  nUnused = 0;
1207  }
1208  else
1209  {
1210  nWidth = aWish[nIndex];
1211  if ( aWish[nIndex] > nEqualWidth )
1212  nUnused -= aWish[nIndex] - nEqualWidth;
1213 
1214  if ( !bMinimize && nDistributeExcess > 0 )
1215  nWidth += nWidth / fAllWish * nDistributeExcess;
1216  }
1217  }
1218 
1219  Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
1220  xColSet->setPropertyValue( gsSize, Any( nWidth ) );
1221 
1222  nAllWidth -= nWidth;
1223  }
1224 
1225  LayoutTable( rArea, !bMinimize, false );
1226  }
1227  catch( Exception& )
1228  {
1229  TOOLS_WARN_EXCEPTION("svx", "");
1230  }
1231 }
1232 
1233 
1235  sal_Int32 nFirstRow,
1236  sal_Int32 nLastRow,
1237  const bool bOptimize,
1238  const bool bMinimize )
1239 {
1240  if( !mxTable.is() )
1241  return;
1242 
1243  try
1244  {
1245  const sal_Int32 nRowCount = mxTable->getRowCount();
1246  Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
1247  sal_Int32 nMinHeight = 0;
1248 
1249  //special case - minimize a single row
1250  if ( bMinimize && nFirstRow == nLastRow )
1251  {
1252  const sal_Int32 nWish = std::max( maRows[nFirstRow].mnMinSize, nMinHeight );
1253  if ( nWish < getRowHeight(nFirstRow) )
1254  {
1255  Reference< XPropertySet > xRowSet( xRows->getByIndex( nFirstRow ), UNO_QUERY_THROW );
1256  xRowSet->setPropertyValue( gsSize, Any( nWish ) );
1257 
1258  LayoutTable( rArea, /*bFitWidth=*/false, /*bFitHeight=*/!bMinimize );
1259  }
1260  }
1261 
1262  if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
1263  return;
1264 
1265  sal_Int32 nAllHeight = 0;
1266  sal_Int32 nMaxHeight = 0;
1267 
1268  for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1269  {
1270  nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
1271  nMaxHeight = std::max( maRows[nRow].mnSize, nMaxHeight );
1272  nAllHeight += maRows[nRow].mnSize;
1273  }
1274 
1275  const sal_Int32 nRows = nLastRow-nFirstRow+1;
1276  sal_Int32 nHeight = nAllHeight / nRows;
1277 
1278  if ( !bMinimize && nHeight < nMaxHeight )
1279  {
1280  if ( !bOptimize )
1281  {
1282  sal_Int32 nNeededHeight = nRows * nMaxHeight;
1283  rArea.AdjustBottom(nNeededHeight - nAllHeight );
1284  nHeight = nMaxHeight;
1285  nAllHeight = nRows * nMaxHeight;
1286  }
1287  else if ( nHeight < nMinHeight )
1288  {
1289  sal_Int32 nNeededHeight = nRows * nMinHeight;
1290  rArea.AdjustBottom(nNeededHeight - nAllHeight );
1291  nHeight = nMinHeight;
1292  nAllHeight = nRows * nMinHeight;
1293  }
1294  }
1295 
1296  for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1297  {
1298  if ( bMinimize )
1299  nHeight = maRows[nRow].mnMinSize;
1300  else if ( nRow == nLastRow )
1301  nHeight = nAllHeight; // last row get round errors
1302 
1303  Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1304  xRowSet->setPropertyValue( gsSize, Any( nHeight ) );
1305 
1306  nAllHeight -= nHeight;
1307  }
1308 
1309  LayoutTable( rArea, false, !bMinimize );
1310  }
1311  catch( Exception& )
1312  {
1313  TOOLS_WARN_EXCEPTION("svx", "");
1314  }
1315 }
1316 
1318 {
1319  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter"));
1320 
1321  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("columns"));
1322  for (const auto& rColumn : maColumns)
1323  rColumn.dumpAsXml(pWriter);
1324  (void)xmlTextWriterEndElement(pWriter);
1325 
1326  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("rows"));
1327  for (const auto& rRow : maRows)
1328  rRow.dumpAsXml(pWriter);
1329  (void)xmlTextWriterEndElement(pWriter);
1330 
1331  (void)xmlTextWriterEndElement(pWriter);
1332 }
1333 
1335 {
1336  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter_Layout"));
1337 
1338  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos).getStr()));
1339  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(mnSize).getStr()));
1340  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize).getStr()));
1341 
1342  (void)xmlTextWriterEndElement(pWriter);
1343 }
1344 
1345 }
1346 
1347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool findMergeOrigin(const TableModelRef &xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 &rOriginX, sal_Int32 &rOriginY)
returns true if the cell(nMergedX,nMergedY) is merged with other cells.
std::enable_if< std::is_signed< T >::value, bool >::type checked_sub(T a, T b, T &result)
sal_Int32 getY() const
::sal_Int32 getColumnCount() const
void DistributeColumns(::tools::Rectangle &rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol, const bool bOptimize, const bool bMinimize)
sal_Int32 nIndex
void dumpAsXml(xmlTextWriterPtr pWriter) const
struct _xmlTextWriter * xmlTextWriterPtr
basegfx::B2ITuple getCellSize(const CellRef &xCell, const CellPos &rPos) const
std::vector< Layout > LayoutVector
tools::Long getWidth() const
static bool checkMergeOrigin(const TableModelRef &xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool &bRunning)
constexpr tools::Long Left() const
constexpr OUStringLiteral gsSize(u"Size")
::sal_Int32 getRowCount() const
std::vector< EdgeInfo > getHorizontalEdges()
sal_Int64 n
static SvxBorderLine gEmptyBorder
static sal_Int32 distribute(LayoutVector &rLayouts, sal_Int32 nDistribute)
const editeng::SvxBorderLine * GetRight() const
sal_Int32 getMinimumColumnWidth(sal_Int32 nColumn)
float x
bool isValidColumn(sal_Int32 nColumn) const
sal_Int32 getHorizontalEdge(int nEdgeY, sal_Int32 *pnMin, sal_Int32 *pnMax)
void SetBorder(sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const editeng::SvxBorderLine *pLine)
tools::Long getHeight() const
editeng::SvxBorderLine * getBorderLine(sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal) const
returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge...
bool next(T &rValue)
Definition: celltypes.hxx:74
int nCount
constexpr tools::Long GetWidth() const
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
float y
constexpr void SetLeft(tools::Long v)
const editeng::SvxBorderLine * GetTop() const
#define TOOLS_WARN_EXCEPTION(area, stream)
const editeng::SvxBorderLine * GetLeft() const
#define DBG_ASSERT(sCon, aError)
int i
void dumpAsXml(xmlTextWriterPtr pWriter) const
void SetSize(const Size &rSize)
void updateCells(::tools::Rectangle const &rRectangle)
sal_Int32 getColumnWidth(sal_Int32 nColumn) const
BorderLineMap maHorizontalBorders
bool getCellArea(const CellRef &xCell, const CellPos &rPos, basegfx::B2IRectangle &rArea) const
std::enable_if< std::is_signed< T >::value, bool >::type checked_add(T a, T b, T &result)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
float u
constexpr tools::Long Top() const
CellRef getCell(const CellPos &rPos) const
size
constexpr void SetRight(tools::Long v)
constexpr void SetBottom(tools::Long v)
bool isValid(const CellPos &rPos) const
std::vector< CellRef > MergeableCellVector
constexpr void SetTop(tools::Long v)
static bool HasPriority(const editeng::SvxBorderLine *pThis, const editeng::SvxBorderLine *pOther)
std::vector< MergeableCellVector > MergeVector
bool isEdgeVisible(sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal) const
checks if the given edge is visible.
constexpr TypedWhichId< SvxBoxItem > SDRATTR_TABLE_BORDER(SDRATTR_TABLE_FIRST+0)
T saturating_add(T a, T b)
void LayoutTableHeight(::tools::Rectangle &rArea, bool bFit)
bool isValidRow(sal_Int32 nRow) const
TableLayouter(const TableModelRef &xTableModel)
sal_Int32 getRowHeight(sal_Int32 nRow) const
sal_Int32 calcPreferredColumnWidth(sal_Int32 nColumn, Size aSize) const
B2IRange B2IRectangle
std::vector< BorderLineVector > BorderLineMap
void LayoutTableWidth(::tools::Rectangle &rArea, bool bFit)
void LayoutTable(::tools::Rectangle &rRectangle, bool bFitWidth, bool bFitHeight)
try to fit the table into the given rectangle.
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
sal_Int32 getX() const
void DistributeRows(::tools::Rectangle &rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow, const bool bOptimize, const bool bMinimize)
const editeng::SvxBorderLine * GetBottom() const
std::vector< EdgeInfo > getVerticalEdges()
sal_Int32 getVerticalEdge(int nEdgeX, sal_Int32 *pnMin, sal_Int32 *pnMax)
constexpr tools::Long GetHeight() const
sal_uInt32 mnSize
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo