Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libstaroffice/src/lib/STOFFCell.cxx
Line
Count
Source
1
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2
3
/* libstaroffice
4
* Version: MPL 2.0 / LGPLv2+
5
*
6
* The contents of this file are subject to the Mozilla Public License Version
7
* 2.0 (the "License"); you may not use this file except in compliance with
8
* the License or as specified alternatively below. You may obtain a copy of
9
* the License at http://www.mozilla.org/MPL/
10
*
11
* Software distributed under the License is distributed on an "AS IS" basis,
12
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13
* for the specific language governing rights and limitations under the
14
* License.
15
*
16
* Major Contributor(s):
17
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20
* Copyright (C) 2006, 2007 Andrew Ziem
21
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22
*
23
*
24
* All Rights Reserved.
25
*
26
* For minor contributions see the git repository.
27
*
28
* Alternatively, the contents of this file may be used under the terms of
29
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30
* in which case the provisions of the LGPLv2+ are applicable
31
* instead of those above.
32
*/
33
34
/** \file STOFFCell.cxx
35
 * Implements STOFFCell (cell content and format)
36
 */
37
#include <time.h>
38
39
#include <cmath>
40
#include <iomanip>
41
#include <iostream>
42
#include <sstream>
43
44
#include <librevenge/librevenge.h>
45
46
#include "STOFFFont.hxx"
47
#include "STOFFListener.hxx"
48
49
#include "STOFFCell.hxx"
50
51
////////////////////////////////////////////////////////////
52
// STOFFCell::Format
53
////////////////////////////////////////////////////////////
54
STOFFCell::Format::~Format()
55
1.93M
{
56
1.93M
}
57
58
std::string STOFFCell::Format::getValueType() const
59
388k
{
60
388k
  switch (m_format) {
61
387k
  case F_NUMBER:
62
387k
    if (m_numberFormat==F_NUMBER_CURRENCY) return "currency";
63
385k
    if (m_numberFormat==F_NUMBER_PERCENT) return "percent";
64
384k
    if (m_numberFormat==F_NUMBER_SCIENTIFIC) return "scientific";
65
384k
    return "float";
66
0
  case F_BOOLEAN:
67
0
    return "boolean";
68
0
  case F_DATE:
69
0
  case F_DATETIME:
70
0
    return "date";
71
0
  case F_TIME:
72
0
    return "time";
73
263
  case F_TEXT:
74
263
  case F_UNKNOWN:
75
263
  default:
76
263
    break;
77
388k
  }
78
263
  return "float";
79
388k
}
80
81
bool STOFFCell::Format::convertDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propVect)
82
19.4k
{
83
19.4k
  propVect.clear();
84
19.4k
  size_t len=dtFormat.size();
85
19.4k
  std::string text("");
86
19.4k
  librevenge::RVNGPropertyList list;
87
123k
  for (size_t c=0; c < len; ++c) {
88
103k
    if (dtFormat[c]!='%' || c+1==len) {
89
42.1k
      text+=dtFormat[c];
90
42.1k
      continue;
91
42.1k
    }
92
61.6k
    char ch=dtFormat[++c];
93
61.6k
    if (ch=='%') {
94
0
      text += '%';
95
0
      continue;
96
0
    }
97
61.6k
    if (!text.empty()) {
98
42.1k
      list.clear();
99
42.1k
      list.insert("librevenge:value-type", "text");
100
42.1k
      list.insert("librevenge:text", text.c_str());
101
42.1k
      propVect.append(list);
102
42.1k
      text.clear();
103
42.1k
    }
104
61.6k
    list.clear();
105
61.6k
    switch (ch) {
106
7.60k
    case 'Y':
107
7.60k
      list.insert("number:style", "long");
108
7.60k
      STOFF_FALLTHROUGH;
109
11.7k
    case 'y':
110
11.7k
      list.insert("librevenge:value-type", "year");
111
11.7k
      propVect.append(list);
112
11.7k
      break;
113
461
    case 'B':
114
461
      list.insert("number:style", "long");
115
461
      STOFF_FALLTHROUGH;
116
488
    case 'b':
117
488
    case 'h':
118
488
      list.insert("librevenge:value-type", "month");
119
488
      list.insert("number:textual", true);
120
488
      propVect.append(list);
121
488
      break;
122
11.7k
    case 'm':
123
11.7k
      list.insert("librevenge:value-type", "month");
124
11.7k
      propVect.append(list);
125
11.7k
      break;
126
0
    case 'e':
127
0
      list.insert("number:style", "long");
128
0
      STOFF_FALLTHROUGH;
129
11.7k
    case 'd':
130
11.7k
      list.insert("librevenge:value-type", "day");
131
11.7k
      propVect.append(list);
132
11.7k
      break;
133
0
    case 'A':
134
0
      list.insert("number:style", "long");
135
0
      STOFF_FALLTHROUGH;
136
0
    case 'a':
137
0
      list.insert("librevenge:value-type", "day-of-week");
138
0
      propVect.append(list);
139
0
      break;
140
141
3.63k
    case 'H':
142
3.63k
      list.insert("number:style", "long");
143
3.63k
      STOFF_FALLTHROUGH;
144
7.44k
    case 'I':
145
7.44k
      list.insert("librevenge:value-type", "hours");
146
7.44k
      propVect.append(list);
147
7.44k
      break;
148
7.76k
    case 'M':
149
7.76k
      list.insert("librevenge:value-type", "minutes");
150
7.76k
      list.insert("number:style", "long");
151
7.76k
      propVect.append(list);
152
7.76k
      break;
153
6.55k
    case 'S':
154
6.55k
      list.insert("librevenge:value-type", "seconds");
155
6.55k
      list.insert("number:style", "long");
156
6.55k
      propVect.append(list);
157
6.55k
      break;
158
322
    case 's': // extension for second + milli second...
159
322
      list.insert("librevenge:value-type", "seconds");
160
322
      list.insert("number:decimal-places", 2);
161
322
      list.insert("number:style", "long");
162
322
      propVect.append(list);
163
322
      break;
164
3.80k
    case 'p':
165
3.80k
      list.insert("librevenge:value-type", "text");
166
3.80k
      list.insert("librevenge:text", " ");
167
3.80k
      propVect.append(list);
168
3.80k
      list.clear();
169
3.80k
      list.insert("librevenge:value-type", "am-pm");
170
3.80k
      propVect.append(list);
171
3.80k
      break;
172
0
    case 'Q': // extension for quarter
173
0
      list.insert("librevenge:value-type", "quarter");
174
0
      propVect.append(list);
175
0
      break;
176
0
    default:
177
0
      STOFF_DEBUG_MSG(("STOFFCell::Format::convertDTFormat: find unimplement command %c(ignored)\n", ch));
178
61.6k
    }
179
61.6k
  }
180
19.4k
  if (!text.empty()) {
181
0
    list.clear();
182
0
    list.insert("librevenge:value-type", "text");
183
0
    list.insert("librevenge:text", text.c_str());
184
0
    propVect.append(list);
185
0
  }
186
19.4k
  return propVect.count()!=0;
187
19.4k
}
188
189
std::ostream &operator<<(std::ostream &o, STOFFCell::Format const &format)
190
0
{
191
0
  switch (format.m_format) {
192
0
  case STOFFCell::F_BOOLEAN:
193
0
    o << "boolean";
194
0
    break;
195
0
  case STOFFCell::F_TEXT:
196
0
    o << "text";
197
0
    break;
198
0
  case STOFFCell::F_NUMBER:
199
0
    o << "number";
200
0
    switch (format.m_numberFormat) {
201
0
    case STOFFCell::F_NUMBER_GENERIC:
202
0
      break;
203
0
    case STOFFCell::F_NUMBER_DECIMAL:
204
0
      o << "[decimal]";
205
0
      break;
206
0
    case STOFFCell::F_NUMBER_SCIENTIFIC:
207
0
      o << "[exp]";
208
0
      break;
209
0
    case STOFFCell::F_NUMBER_PERCENT:
210
0
      o << "[percent]";
211
0
      break;
212
0
    case STOFFCell::F_NUMBER_CURRENCY:
213
0
      o << "[money]";
214
0
      break;
215
0
    case STOFFCell::F_NUMBER_FRACTION:
216
0
      o << "[fraction]";
217
0
      break;
218
0
    case STOFFCell::F_NUMBER_UNKNOWN:
219
0
    default:
220
0
      STOFF_DEBUG_MSG(("STOFFCell::operator<<(Format): find unexpected type\n"));
221
0
      o << "###format,";
222
0
      break;
223
0
    }
224
0
    break;
225
0
  case STOFFCell::F_DATE:
226
0
    o << "date";
227
0
    break;
228
0
  case STOFFCell::F_DATETIME:
229
0
    o << "date+time";
230
0
    break;
231
0
  case STOFFCell::F_TIME:
232
0
    o << "time";
233
0
    break;
234
0
  case STOFFCell::F_UNKNOWN:
235
0
  default:
236
0
    break; // default
237
0
  }
238
0
  o << ",";
239
240
0
  return o;
241
0
}
242
243
////////////////////////////////////////////////////////////
244
// STOFFCell
245
////////////////////////////////////////////////////////////
246
void STOFFCell::addTo(librevenge::RVNGPropertyList &propList) const
247
817k
{
248
817k
  propList.insert("librevenge:column", position()[0]);
249
817k
  propList.insert("librevenge:row", position()[1]);
250
251
817k
  m_font.addTo(propList);
252
817k
  m_cellStyle.addTo(propList);
253
817k
}
254
255
std::string STOFFCell::getColumnName(int col)
256
0
{
257
0
  std::stringstream f;
258
0
  f << "[.";
259
0
  if (col > 26) f << char('A'+col/26);
260
0
  f << char('A'+(col%26));
261
0
  f << "]";
262
0
  return f.str();
263
0
}
264
265
std::string STOFFCell::getCellName(STOFFVec2i const &pos, STOFFVec2b const &absolute)
266
0
{
267
0
  std::stringstream f;
268
0
  f << "[." << libstoff::getCellName(pos, absolute) << ']';
269
0
  return f.str();
270
0
}
271
272
std::ostream &operator<<(std::ostream &o, STOFFCell const &cell)
273
0
{
274
0
  o << STOFFCell::getCellName(cell.m_position, STOFFVec2b(false,false)) << ":";
275
0
  if (cell.m_bdBox.size()[0]>0 || cell.m_bdBox.size()[1]>0)
276
0
    o << "bdBox=" << cell.m_bdBox << ",";
277
0
  if (cell.m_bdSize[0]>0 || cell.m_bdSize[1]>0)
278
0
    o << "bdSize=" << cell.m_bdSize << ",";
279
0
  o << cell.m_format;
280
0
  return o;
281
0
}
282
283
// send data to listener
284
bool STOFFCell::send(STOFFListenerPtr listener, STOFFTable &table)
285
0
{
286
0
  if (!listener) return true;
287
0
  listener->openTableCell(*this);
288
0
  bool ok=sendContent(listener, table);
289
0
  listener->closeTableCell();
290
0
  return ok;
291
0
}
292
293
bool STOFFCell::sendContent(STOFFListenerPtr, STOFFTable &)
294
0
{
295
0
  STOFF_DEBUG_MSG(("STOFFCell::sendContent: must not be called!!!\n"));
296
0
  return false;
297
0
}
298
299
////////////////////////////////////////////////////////////
300
// STOFFCellContent
301
////////////////////////////////////////////////////////////
302
bool STOFFCellContent::double2Date(double val, int &Y, int &M, int &D)
303
3.90k
{
304
  /* first convert the date in long*/
305
3.90k
  auto numDaysSinceOrigin=long(val+(val>0 ? -2 : -3)+0.4);
306
  // checkme: do we need to check before for isNan(val) ?
307
3.90k
  if (numDaysSinceOrigin<-10000*365 || numDaysSinceOrigin>10000*365) {
308
    /* normally, we can expect documents to contain date between 1900
309
       and 2100. So even if such a date can make sense, storing it as
310
       a number of days is clearly abnormal */
311
11
    STOFF_DEBUG_MSG(("STOFFCellContent::double2Date: using a double to represent the date %ld seems odd\n", numDaysSinceOrigin));
312
11
    Y=1900;
313
11
    M=D=1;
314
11
    return false;
315
11
  }
316
  // find the century
317
3.89k
  int century=19;
318
5.30k
  while (numDaysSinceOrigin>=36500+24) {
319
1.40k
    long numDaysInCentury=36500+24+((century%4)?0:1);
320
1.40k
    if (numDaysSinceOrigin<numDaysInCentury) break;
321
1.40k
    numDaysSinceOrigin-=numDaysInCentury;
322
1.40k
    ++century;
323
1.40k
  }
324
4.50k
  while (numDaysSinceOrigin<0) {
325
607
    --century;
326
607
    numDaysSinceOrigin+=36500+24+((century%4)?0:1);
327
607
  }
328
  // now compute the year
329
3.89k
  Y=int(numDaysSinceOrigin/365);
330
3.89k
  long numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
331
3.89k
  if (numDaysToEndY1>numDaysSinceOrigin) {
332
890
    --Y;
333
890
    numDaysToEndY1=Y*365+(Y>0 ? (Y-1)/4+((century%4)?0:1) : 0);
334
890
  }
335
  // finish to compute the date
336
3.89k
  auto numDaysFrom1Jan=int(numDaysSinceOrigin-numDaysToEndY1);
337
3.89k
  Y+=century*100;
338
3.89k
  bool isLeap=(Y%4)==0 && ((Y%400)==0 || (Y%100)!=0);
339
340
23.9k
  for (M=0; M<12; ++M) {
341
23.9k
    static const int days[2][12] = {
342
23.9k
      { 0,31,59,90,120,151,181,212,243,273,304,334},
343
23.9k
      { 0,31,60,91,121,152,182,213,244,274,305,335}
344
23.9k
    };
345
23.9k
    if (M<11 && days[isLeap ? 1 : 0][M+1]<=numDaysFrom1Jan) continue;
346
3.89k
    D=(numDaysFrom1Jan-days[isLeap ? 1 : 0][M++])+1;
347
3.89k
    break;
348
23.9k
  }
349
3.89k
  return true;
350
3.90k
}
351
352
bool STOFFCellContent::date2Double(int Y, int M, int D, double &val)
353
0
{
354
0
  --M;
355
0
  --D;
356
0
  if (M>11) {
357
0
    Y += M/12;
358
0
    M %= 12;
359
0
  }
360
0
  else if (M<0) {
361
0
    int yDiff = (-M + 11)/12;
362
0
    Y -= yDiff;
363
0
    M+=12*yDiff;
364
0
  }
365
  // sanity check
366
0
  if (M<0||M>11) {
367
0
    STOFF_DEBUG_MSG(("STOFFCellContent::date2Double: something is bad\n"));
368
0
    return false;
369
0
  }
370
0
  bool isLeap=(Y%4)==0 && ((Y%400)==0 || (Y%100)!=0);
371
0
  int32_t const daysFrom0=365*Y+(Y/400)-(Y/100)+(Y/4);
372
0
  int32_t const daysFrom1900=365*1900+(1900/400)-(1900/100)+(1900/4);
373
0
  static const int32_t days[2][12] = {
374
0
    { 0,31,59,90,120,151,181,212,243,273,304,334},
375
0
    { 0,31,60,91,121,152,182,213,244,274,305,335}
376
0
  };
377
0
  int32_t daysFrom1Jan=days[isLeap ? 1 : 0][M] + D;
378
0
  val=double(daysFrom0-daysFrom1900+daysFrom1Jan);
379
0
  return true;
380
0
}
381
382
bool STOFFCellContent::double2Time(double val, int &H, int &M, int &S)
383
6.35k
{
384
6.35k
  if (val < 0.0 || val > 1.0) return false;
385
6.02k
  double time = 24.*3600.*val+0.5;
386
6.02k
  H=int(time/3600.);
387
6.02k
  time -= H*3600.;
388
6.02k
  M=int(time/60.);
389
6.02k
  time -= M*60.;
390
6.02k
  S=int(time);
391
6.02k
  return true;
392
6.35k
}
393
394
std::ostream &operator<<(std::ostream &o, STOFFCellContent const &content)
395
0
{
396
0
  switch (content.m_contentType) {
397
0
  case STOFFCellContent::C_NONE:
398
0
    break;
399
0
  case STOFFCellContent::C_TEXT:
400
0
    o << ",text";
401
0
    break;
402
0
  case STOFFCellContent::C_TEXT_BASIC:
403
0
    o << ",text=\"" << libstoff::getString(content.m_text).cstr() << "\"";
404
0
    break;
405
0
  case STOFFCellContent::C_NUMBER:
406
0
    o << ",val="<< content.m_value;
407
0
    break;
408
0
  case STOFFCellContent::C_FORMULA:
409
0
    o << ",formula=";
410
0
    for (const auto &l : content.m_formula)
411
0
      o << l;
412
0
    if (content.isValueSet()) o << "[" << content.m_value << "]";
413
0
    break;
414
0
  case STOFFCellContent::C_UNKNOWN:
415
0
    break;
416
0
  default:
417
0
    o << "###unknown type,";
418
0
    break;
419
0
  }
420
0
  return o;
421
0
}
422
423
// ---------- MWAWContentListener::FormulaInstruction ------------------
424
librevenge::RVNGPropertyList STOFFCellContent::FormulaInstruction::getPropertyList() const
425
233k
{
426
233k
  librevenge::RVNGPropertyList pList;
427
233k
  switch (m_type) {
428
0
  case F_None:
429
0
    break;
430
108k
  case F_Operator:
431
108k
    pList.insert("librevenge:type","librevenge-operator");
432
108k
    pList.insert("librevenge:operator",m_content);
433
108k
    break;
434
39.1k
  case F_Function:
435
39.1k
    pList.insert("librevenge:type","librevenge-function");
436
39.1k
    pList.insert("librevenge:function",m_content);
437
39.1k
    break;
438
244
  case F_Text:
439
244
    pList.insert("librevenge:type","librevenge-text");
440
244
    pList.insert("librevenge:text",m_content);
441
244
    break;
442
1.91k
  case F_Double:
443
1.91k
    pList.insert("librevenge:type","librevenge-number");
444
1.91k
    pList.insert("librevenge:number",m_doubleValue, librevenge::RVNG_GENERIC);
445
1.91k
    break;
446
368
  case F_Long:
447
368
    pList.insert("librevenge:type","librevenge-number");
448
368
    pList.insert("librevenge:number",double(m_longValue), librevenge::RVNG_GENERIC);
449
368
    break;
450
67.8k
  case F_Cell:
451
67.8k
    pList.insert("librevenge:type","librevenge-cell");
452
67.8k
    pList.insert("librevenge:column",m_position[0][0], librevenge::RVNG_GENERIC);
453
67.8k
    pList.insert("librevenge:row",m_position[0][1], librevenge::RVNG_GENERIC);
454
67.8k
    pList.insert("librevenge:column-absolute",!m_positionRelative[0][0]);
455
67.8k
    pList.insert("librevenge:row-absolute",!m_positionRelative[0][1]);
456
67.8k
    if (!m_sheet.empty())
457
5.15k
      pList.insert("librevenge:sheet",m_sheet);
458
67.8k
    break;
459
15.3k
  case F_CellList:
460
15.3k
    pList.insert("librevenge:type","librevenge-cells");
461
15.3k
    pList.insert("librevenge:start-column",m_position[0][0], librevenge::RVNG_GENERIC);
462
15.3k
    pList.insert("librevenge:start-row",m_position[0][1], librevenge::RVNG_GENERIC);
463
15.3k
    pList.insert("librevenge:start-column-absolute",!m_positionRelative[0][0]);
464
15.3k
    pList.insert("librevenge:start-row-absolute",!m_positionRelative[0][1]);
465
15.3k
    pList.insert("librevenge:end-column",m_position[1][0], librevenge::RVNG_GENERIC);
466
15.3k
    pList.insert("librevenge:end-row",m_position[1][1], librevenge::RVNG_GENERIC);
467
15.3k
    pList.insert("librevenge:end-column-absolute",!m_positionRelative[1][0]);
468
15.3k
    pList.insert("librevenge:end-row-absolute",!m_positionRelative[1][1]);
469
15.3k
    if (!m_sheet.empty())
470
356
      pList.insert("librevenge:sheet-name",m_sheet.cstr());
471
15.3k
    break;
472
0
  case F_Index: {
473
0
    static bool first=true;
474
0
    if (first) {
475
0
      STOFF_DEBUG_MSG(("STOFFCellContent::FormulaInstruction::getPropertyList: impossible to send index data\n"));
476
0
      first=false;
477
0
    }
478
0
    break;
479
0
  }
480
0
  default:
481
0
    STOFF_DEBUG_MSG(("STOFFCellContent::FormulaInstruction::getPropertyList: unexpected type\n"));
482
233k
  }
483
233k
  return pList;
484
233k
}
485
486
std::ostream &operator<<(std::ostream &o, STOFFCellContent::FormulaInstruction const &inst)
487
0
{
488
0
  if (inst.m_type==STOFFCellContent::FormulaInstruction::F_Double)
489
0
    o << inst.m_doubleValue;
490
0
  else if (inst.m_type==STOFFCellContent::FormulaInstruction::F_Long)
491
0
    o << inst.m_longValue;
492
0
  else if (inst.m_type==STOFFCellContent::FormulaInstruction::F_Index)
493
0
    o << "[Idx" << inst.m_longValue << "]";
494
0
  else if (inst.m_type==STOFFCellContent::FormulaInstruction::F_Cell) {
495
0
    if (!inst.m_sheet.empty()) o << inst.m_sheet.cstr();
496
0
    else if (inst.m_sheetId>=0) {
497
0
      if (!inst.m_sheetIdRelative) o << "$";
498
0
      o << "S" << inst.m_sheetId;
499
0
      o << ":";
500
0
    }
501
0
    o << libstoff::getCellName(inst.m_position[0],inst.m_positionRelative[0]);
502
0
  }
503
0
  else if (inst.m_type==STOFFCellContent::FormulaInstruction::F_CellList) {
504
0
    if (!inst.m_sheet.empty()) o << inst.m_sheet.cstr() << ":";
505
0
    else if (inst.m_sheetId>=0) {
506
0
      if (inst.m_sheetIdRelative) o << "$";
507
0
      o << "S" << inst.m_sheetId;
508
0
      o << ":";
509
0
    }
510
0
    for (int l=0; l<2; ++l) {
511
0
      o << libstoff::getCellName(inst.m_position[l],inst.m_positionRelative[l]);
512
0
      if (l==0) o << ":";
513
0
    }
514
0
  }
515
0
  else if (inst.m_type==STOFFCellContent::FormulaInstruction::F_Text)
516
0
    o << "\"" << inst.m_content.cstr() << "\"";
517
0
  else if (inst.m_type!=STOFFCellContent::FormulaInstruction::F_None)
518
0
    o << inst.m_content.cstr();
519
0
  if (!inst.m_extra.empty())
520
0
    o << "[" << inst.m_extra << "]";
521
0
  return o;
522
0
}
523
524
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: