Coverage Report

Created: 2026-10-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/WPSTable.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
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
 * Major Contributor(s):
10
 * Copyright (C) 2009, 2011 Alonso Laurent (alonso@loria.fr)
11
 * Copyright (C) 2006, 2007 Andrew Ziem
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
14
 * Copyright (C) 2003-2005 William Lachance (william.lachance@sympatico.ca)
15
 *
16
 * For minor contributions see the git repository.
17
 *
18
 * Alternatively, the contents of this file may be used under the terms
19
 * of the GNU Lesser General Public License Version 2.1 or later
20
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
21
 * applicable instead of those above.
22
 *
23
 * For further information visit http://libwps.sourceforge.net
24
 */
25
26
/*
27
 * Structure to store and construct a table from an unstructured list
28
 * of cell
29
 *
30
 */
31
32
#include <iostream>
33
#include <set>
34
35
#include "WPSContentListener.h"
36
#include "WPSCell.h"
37
38
#include "WPSTable.h"
39
40
////////////////////////////////////////////////////////////
41
// WPSColumnFormat
42
std::ostream &operator<<(std::ostream &o, WPSColumnFormat const &column)
43
0
{
44
0
  if (column.m_width>=0)
45
0
  {
46
0
    if (column.m_isPercentWidth)
47
0
      o<<"w=" << column.m_width << "%,";
48
0
    else
49
0
      o<<"w=" << column.m_width << ",";
50
0
  }
51
0
  if (column.m_useOptimalWidth) o << "optimal[h],";
52
0
  if (column.m_isHeader) o << "table[header],";
53
0
  if (column.m_numRepeat>1) o << "repeat=" << column.m_numRepeat << ",";
54
0
  if (!column.m_visible) o << "hidden,";
55
0
  return o;
56
0
}
57
58
void WPSColumnFormat::addTo(librevenge::RVNGPropertyList &propList) const
59
2.22M
{
60
2.22M
  if (m_width>=0)
61
2.22M
    propList.insert("style:column-width", double(m_width), m_isPercentWidth ? librevenge::RVNG_PERCENT : librevenge::RVNG_POINT);
62
2.22M
  if (m_useOptimalWidth)
63
1.12M
    propList.insert("style:use-optimal-column-width", true);
64
2.22M
  if (m_isHeader)
65
0
    propList.insert("librevenge:is-header-column", true); // checkme
66
2.22M
  if (m_numRepeat>1)
67
318k
    propList.insert("table:number-columns-repeated", m_numRepeat);
68
2.22M
  if (!m_visible) // checkme: libodfgen/librevenge do not recognize table:visibility
69
5.59k
    propList.insert("table:visibility", "collapse");
70
2.22M
}
71
72
////////////////////////////////////////////////////////////
73
// WPSRowFormat
74
std::ostream &operator<<(std::ostream &o, WPSRowFormat const &row)
75
0
{
76
0
  if (row.m_height>=0)
77
0
  {
78
0
    if (row.m_isMinimalHeight)
79
0
      o<<"h[min]=" << row.m_height << ",";
80
0
    else
81
0
      o<<"h=" << row.m_height << ",";
82
0
  }
83
0
  if (row.m_useOptimalHeight) o << "optimal[h],";
84
0
  if (row.m_isHeader) o << "table[header],";
85
0
  return o;
86
0
}
87
88
void WPSRowFormat::addTo(librevenge::RVNGPropertyList &propList) const
89
13.2M
{
90
13.2M
  if (m_height>=0)
91
13.2M
  {
92
13.2M
    if (m_isMinimalHeight)
93
1.90M
      propList.insert("style:min-row-height", double(m_height), librevenge::RVNG_POINT);
94
11.3M
    else
95
11.3M
      propList.insert("style:row-height", double(m_height), librevenge::RVNG_POINT);
96
13.2M
  }
97
13.2M
  if (m_useOptimalHeight)
98
3.68k
    propList.insert("style:use-optimal-row-height", true);
99
13.2M
  propList.insert("librevenge:is-header-row", m_isHeader);
100
13.2M
}
101
102
////////////////////////////////////////////////////////////
103
// destructor, ...
104
WPSTable::~WPSTable()
105
6.84k
{
106
6.84k
}
107
108
void WPSTable::add(WPSCellPtr &cell)
109
11.5k
{
110
11.5k
  m_cellsList.push_back(cell);
111
11.5k
}
112
113
WPSCellPtr WPSTable::getCell(int id)
114
11.3k
{
115
11.3k
  if (id < 0 || id >= int(m_cellsList.size()))
116
0
  {
117
0
    WPS_DEBUG_MSG(("WPSTable::get: cell %d does not exists\n",id));
118
0
    return std::shared_ptr<WPSCell>();
119
0
  }
120
11.3k
  return m_cellsList[size_t(id)];
121
11.3k
}
122
123
////////////////////////////////////////////////////////////
124
// build the table structure
125
bool WPSTable::buildStructures()
126
357
{
127
357
  if (m_colsSize.size())
128
81
    return true;
129
130
276
  size_t nCells = m_cellsList.size();
131
276
  std::vector<float> listPositions[2];
132
828
  for (int dim = 0; dim < 2; dim++)
133
552
  {
134
552
    WPSCell::Compare compareFunction(dim);
135
552
    std::set<WPSCell::Compare::Point,
136
552
        WPSCell::Compare> set(compareFunction);
137
2.19k
    for (size_t c = 0; c < nCells; c++)
138
1.64k
    {
139
1.64k
      set.insert(WPSCell::Compare::Point(0, m_cellsList[c].get()));
140
1.64k
      set.insert(WPSCell::Compare::Point(1, m_cellsList[c].get()));
141
1.64k
    }
142
143
552
    std::vector<float> positions;
144
552
    float maxPosiblePos=0;
145
552
    int actCell = -1;
146
552
    for (auto const &it : set)
147
3.28k
    {
148
3.28k
      float pos = it.getPos(dim);
149
3.28k
      if (actCell < 0 || pos > maxPosiblePos)
150
1.83k
      {
151
1.83k
        actCell++;
152
1.83k
        positions.push_back(pos);
153
1.83k
        maxPosiblePos = pos+2.0f; // 2 pixel ok
154
1.83k
      }
155
3.28k
      if (it.m_which == 0 && it.getPos(dim)-2.0f < maxPosiblePos)
156
1.59k
        maxPosiblePos = pos;
157
3.28k
    }
158
552
    listPositions[dim] = positions;
159
552
  }
160
276
  std::vector<int> numYSet(listPositions[1].size(), 0);
161
276
  std::vector<int> numYUnset(listPositions[1].size(), 0);
162
276
  for (auto const &cell : m_cellsList)
163
723
  {
164
723
    int cellPos[2], spanCell[2];
165
1.99k
    for (int dim = 0; dim < 2; dim++)
166
1.38k
    {
167
1.38k
      float const pt[2] = { cell->box().min()[dim],
168
1.38k
                            cell->box().max()[dim]
169
1.38k
                          };
170
1.38k
      std::vector<float> const &pos = listPositions[dim];
171
1.38k
      size_t numPos = pos.size();
172
1.38k
      size_t i = 0;
173
2.05k
      while (i+1 < numPos && pos[i+1] < pt[0])
174
668
        i++;
175
1.77k
      while (i+1 < numPos && (pos[i]+pos[i+1])/2 < pt[0])
176
393
        i++;
177
1.38k
      if (i+1 > numPos)
178
0
      {
179
0
        WPS_DEBUG_MSG(("WPSTable::buildStructures: impossible to find cell position !!!\n"));
180
0
        return false;
181
0
      }
182
1.38k
      cellPos[dim] = int(i);
183
1.78k
      while (i+1 < numPos && pos[i+1] < pt[1])
184
405
        i++;
185
1.38k
      if (i+1 < numPos && (pos[i]+pos[i+1])/2 < pt[1])
186
1.02k
        i++;
187
1.38k
      spanCell[dim] = int(i)-cellPos[dim];
188
1.38k
      if (spanCell[dim]==0 &&
189
348
              (cell->box().size()[dim]<0||cell->box().size()[dim]>0))
190
110
      {
191
110
        WPS_DEBUG_MSG(("WPSTable::buildStructures: impossible to find span number !!!\n"));
192
110
        return false;
193
110
      }
194
1.27k
      if (spanCell[dim] > 1 &&
195
231
              pos[size_t(cellPos[dim])]+2.0f > pos[size_t(cellPos[dim]+1)])
196
120
      {
197
120
        spanCell[dim]--;
198
120
        cellPos[dim]++;
199
120
      }
200
1.27k
      if (spanCell[dim] > 1 &&
201
167
              pos[size_t(cellPos[dim])]+2.0f > pos[size_t(cellPos[dim]+1)])
202
50
      {
203
50
        spanCell[dim]--;
204
50
        cellPos[dim]++;
205
50
      }
206
1.27k
    }
207
613
    cell->m_position = Vec2i(cellPos[0], cellPos[1]);
208
613
    cell->m_numberCellSpanned = Vec2i(spanCell[0], spanCell[1]);
209
613
    if (spanCell[1] > 0)
210
512
    {
211
1.11k
      for (int x = cellPos[0]; x < cellPos[0]+spanCell[0]; x++)
212
600
      {
213
600
        if (cell->isVerticalSet())
214
204
          numYSet[size_t(cellPos[1]+spanCell[1]-1)]++;
215
396
        else
216
396
          numYUnset[size_t(cellPos[1]+spanCell[1]-1)]++;
217
600
      }
218
512
    }
219
613
  }
220
  // finally update the row/col size
221
498
  for (int dim = 0; dim < 2; dim++)
222
332
  {
223
332
    std::vector<float> const &pos = listPositions[dim];
224
332
    size_t numPos = pos.size();
225
332
    if (!numPos) continue;
226
312
    std::vector<float> &res = (dim==0) ? m_colsSize : m_rowsSize;
227
312
    res.resize(numPos-1);
228
987
    for (size_t i = 0; i < numPos-1; i++)
229
675
    {
230
675
      if (dim==0 || numYUnset[i]==0)
231
485
        res[i] = pos[i+1]-pos[i];
232
190
      else if (numYSet[i])
233
29
        res[i] = -(pos[i+1]-pos[i]);
234
161
      else
235
161
        res[i] = 0;
236
675
    }
237
312
  }
238
239
166
  return true;
240
276
}
241
242
////////////////////////////////////////////////////////////
243
// try to send the table
244
bool WPSTable::sendTable(WPSContentListenerPtr listener)
245
357
{
246
357
  if (!buildStructures())
247
110
    return false;
248
247
  if (!listener)
249
0
    return true;
250
251
247
  size_t numCols = m_colsSize.size();
252
247
  size_t numRows = m_rowsSize.size();
253
247
  if (!numCols || !numRows)
254
31
    return false;
255
216
  std::vector<int> cellsId(numCols*numRows, -1);
256
216
  size_t nCells = m_cellsList.size();
257
789
  for (size_t c = 0; c < nCells; c++)
258
599
  {
259
599
    if (!m_cellsList[c]) continue;
260
599
    Vec2i const &pos=m_cellsList[c]->m_position;
261
599
    Vec2i const &span=m_cellsList[c]->m_numberCellSpanned;
262
263
1.19k
    for (int x = pos[0]; x < pos[0]+span[0]; x++)
264
623
    {
265
623
      if (x >= int(numCols))
266
0
      {
267
0
        WPS_DEBUG_MSG(("WPSTable::sendTable: x is too big !!!\n"));
268
0
        return false;
269
0
      }
270
1.33k
      for (int y = pos[1]; y < pos[1]+span[1]; y++)
271
741
      {
272
741
        if (y >= int(numRows))
273
0
        {
274
0
          WPS_DEBUG_MSG(("WPSTable::sendTable: y is too big !!!\n"));
275
0
          return false;
276
0
        }
277
741
        auto tablePos = size_t(y*int(numCols)+x);
278
741
        if (cellsId[tablePos] != -1)
279
26
        {
280
26
          WPS_DEBUG_MSG(("WPSTable::sendTable: cells is used!!!\n"));
281
26
          return false;
282
26
        }
283
715
        if (x == pos[0] && y == pos[1])
284
510
          cellsId[tablePos] = int(c);
285
205
        else
286
205
          cellsId[tablePos] = -2;
287
715
      }
288
623
    }
289
599
  }
290
291
190
  listener->openTable(m_colsSize, librevenge::RVNG_POINT);
292
190
  WPSListenerPtr listen=listener;
293
570
  for (size_t r = 0; r < numRows; r++)
294
380
  {
295
380
    listener->openTableRow(m_rowsSize[r], librevenge::RVNG_POINT);
296
1.30k
    for (size_t c = 0; c < numCols; c++)
297
921
    {
298
921
      size_t tablePos = r*numCols+c;
299
921
      int id = cellsId[tablePos];
300
921
      if (id == -1)
301
464
        listener->addEmptyTableCell(Vec2i(int(c), int(r)));
302
921
      if (id < 0)
303
487
        continue;
304
305
434
      m_cellsList[size_t(id)]->send(listen);
306
434
    }
307
380
    listener->closeTableRow();
308
380
  }
309
190
  listener->closeTable();
310
311
190
  return true;
312
216
}
313
314
315
////////////////////////////////////////////////////////////
316
// try to send the table
317
bool WPSTable::sendAsText(WPSContentListenerPtr listener)
318
167
{
319
167
  if (!listener) return true;
320
321
167
  WPSListenerPtr listen=listener;
322
167
  for (auto &cell : m_cellsList)
323
502
  {
324
502
    if (!cell) continue;
325
502
    cell->sendContent(listen);
326
502
    listener->insertEOL();
327
502
  }
328
167
  return true;
329
167
}
330
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */