Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libstaroffice/src/lib/libstaroffice_internal.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
#include <cmath>
35
#include <cstdarg>
36
#include <cstdio>
37
#include <iomanip>
38
#include <string>
39
#include <sstream>
40
#include <time.h>
41
42
#include <ctype.h>
43
#include <locale.h>
44
45
#include <librevenge-stream/librevenge-stream.h>
46
47
#include "libstaroffice_internal.hxx"
48
49
/** namespace used to regroup all libwpd functions, enumerations which we have redefined for internal usage */
50
namespace libstoff
51
{
52
uint8_t readU8(librevenge::RVNGInputStream *input)
53
0
{
54
0
  unsigned long numBytesRead;
55
0
  uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead);
56
57
0
  if (!p || numBytesRead != sizeof(uint8_t))
58
0
    throw libstoff::FileException();
59
60
0
  return *p;
61
0
}
62
63
librevenge::RVNGString getString(std::vector<uint32_t> const &unicode)
64
3.47M
{
65
3.47M
  librevenge::RVNGString res("");
66
648M
  for (unsigned int i : unicode) {
67
648M
    if (i<0x20 && i!=0x9 && i!=0xa && i!=0xd) {
68
392M
      static int numErrors=0;
69
392M
      if (++numErrors<10) {
70
36
        STOFF_DEBUG_MSG(("libstoff::getString: find odd char %x\n", static_cast<unsigned int>(i)));
71
36
      }
72
392M
    }
73
256M
    else if (i<0x80)
74
157M
      res.append(char(i));
75
99.5M
    else
76
99.5M
      appendUnicode(i, res);
77
648M
  }
78
3.47M
  return res;
79
3.47M
}
80
81
void appendUnicode(uint32_t val, librevenge::RVNGString &buffer)
82
102M
{
83
102M
  uint8_t first;
84
102M
  int len;
85
102M
  if (val < 0x80) {
86
2.59M
    first = 0;
87
2.59M
    len = 1;
88
2.59M
  }
89
100M
  else if (val < 0x800) {
90
53.5M
    first = 0xc0;
91
53.5M
    len = 2;
92
53.5M
  }
93
46.5M
  else if (val < 0x10000) {
94
46.4M
    first = 0xe0;
95
46.4M
    len = 3;
96
46.4M
  }
97
95.5k
  else if (val < 0x200000) {
98
5.60k
    first = 0xf0;
99
5.60k
    len = 4;
100
5.60k
  }
101
89.9k
  else if (val < 0x4000000) {
102
16.1k
    first = 0xf8;
103
16.1k
    len = 5;
104
16.1k
  }
105
73.7k
  else {
106
73.7k
    first = 0xfc;
107
73.7k
    len = 6;
108
73.7k
  }
109
110
102M
  char outbuf[7];
111
102M
  int i;
112
249M
  for (i = len - 1; i > 0; --i) {
113
147M
    outbuf[i] = char((val & 0x3f) | 0x80);
114
147M
    val >>= 6;
115
147M
  }
116
102M
  outbuf[0] = char(val | first);
117
102M
  outbuf[len] = 0;
118
102M
  buffer.append(outbuf);
119
102M
}
120
}
121
122
namespace libstoff
123
{
124
std::string numberingTypeToString(NumberingType type)
125
0
{
126
0
  switch (type) {
127
0
  case ARABIC:
128
0
    return "1";
129
0
  case LOWERCASE:
130
0
    return "a";
131
0
  case UPPERCASE:
132
0
    return "A";
133
0
  case LOWERCASE_ROMAN:
134
0
    return "i";
135
0
  case UPPERCASE_ROMAN:
136
0
    return "I";
137
0
  case NONE:
138
0
  case BULLET:
139
0
  default:
140
0
    break;
141
0
  }
142
0
  STOFF_DEBUG_MSG(("libstoff::numberingTypeToString: must not be called with type %d\n", int(type)));
143
0
  return "1";
144
0
}
145
146
std::string numberingValueToString(NumberingType type, int value)
147
0
{
148
0
  std::stringstream ss;
149
0
  std::string s("");
150
0
  switch (type) {
151
0
  case ARABIC:
152
0
    ss << value;
153
0
    return ss.str();
154
0
  case LOWERCASE:
155
0
  case UPPERCASE:
156
0
    if (value <= 0) {
157
0
      STOFF_DEBUG_MSG(("libstoff::numberingValueToString: value can not be negative or null for type %d\n", int(type)));
158
0
      return (type == LOWERCASE) ? "a" : "A";
159
0
    }
160
0
    while (value > 0) {
161
0
      s = char((type == LOWERCASE ? 'a' : 'A')+((value-1)%26))+s;
162
0
      value = (value-1)/26;
163
0
    }
164
0
    return s;
165
0
  case LOWERCASE_ROMAN:
166
0
  case UPPERCASE_ROMAN: {
167
0
    static char const* const romanS[] = {"M", "CM", "D", "CD", "C", "XC", "L",
168
0
                                   "XL", "X", "IX", "V", "IV", "I"
169
0
                                  };
170
0
    static char const* const romans[] = {"m", "cm", "d", "cd", "c", "xc", "l",
171
0
                                   "xl", "x", "ix", "v", "iv", "i"
172
0
                                  };
173
0
    static int const romanV[] = {1000, 900, 500, 400,  100, 90, 50,
174
0
                                 40, 10, 9, 5, 4, 1
175
0
                                };
176
0
    if (value <= 0 || value >= 4000) {
177
0
      STOFF_DEBUG_MSG(("libstoff::numberingValueToString: out of range value for type %d\n", int(type)));
178
0
      return (type == LOWERCASE_ROMAN) ? "i" : "I";
179
0
    }
180
0
    for (int p = 0; p < 13; p++) {
181
0
      while (value >= romanV[p]) {
182
0
        ss << ((type == LOWERCASE_ROMAN) ? romans[p] : romanS[p]);
183
0
        value -= romanV[p];
184
0
      }
185
0
    }
186
0
    return ss.str();
187
0
  }
188
0
  case NONE:
189
0
    return "";
190
0
  case BULLET:
191
0
  default:
192
0
    STOFF_DEBUG_MSG(("libstoff::numberingValueToString: must not be called with type %d\n", int(type)));
193
0
    break;
194
0
  }
195
0
  return "";
196
0
}
197
}
198
199
// color function
200
STOFFColor STOFFColor::barycenter(float alpha, STOFFColor const &colA,
201
                                  float beta, STOFFColor const &colB)
202
722
{
203
722
  uint32_t res = 0;
204
3.61k
  for (int i=0, depl=0; i<4; i++, depl+=8) {
205
2.88k
    float val=alpha*float((colA.m_value>>depl)&0xFF)+beta*float((colB.m_value>>depl)&0xFF);
206
2.88k
    if (val < 0) val=0;
207
2.88k
    if (val > 256) val=256;
208
2.88k
    auto comp= static_cast<unsigned char>(val);
209
2.88k
    res+=uint32_t(comp<<depl);
210
2.88k
  }
211
722
  return STOFFColor(res);
212
722
}
213
214
std::ostream &operator<< (std::ostream &o, STOFFColor const &c)
215
563k
{
216
563k
  const std::streamsize width = o.width();
217
563k
  const char fill = o.fill();
218
563k
  o << "#" << std::hex << std::setfill('0') << std::setw(6)
219
563k
    << (c.m_value&0xFFFFFF)
220
    // std::ios::width() takes/returns std::streamsize (long), but
221
    // std::setw() takes int. Go figure...
222
563k
    << std::dec << std::setfill(fill) << std::setw(static_cast<int>(width));
223
563k
  return o;
224
563k
}
225
226
std::string STOFFColor::str() const
227
292k
{
228
292k
  std::stringstream stream;
229
292k
  stream << *this;
230
292k
  return stream.str();
231
292k
}
232
233
// field function
234
void STOFFField::addTo(librevenge::RVNGPropertyList &pList) const
235
1.92k
{
236
1.92k
  librevenge::RVNGPropertyList::Iter i(m_propertyList);
237
4.74k
  for (i.rewind(); i.next();) {
238
2.82k
    if (i.child()) {
239
0
      pList.insert(i.key(), *i.child());
240
0
      continue;
241
0
    }
242
2.82k
    pList.insert(i.key(), i()->clone());
243
2.82k
  }
244
1.92k
}
245
246
// link function
247
bool STOFFLink::addTo(librevenge::RVNGPropertyList &propList) const
248
92
{
249
92
  propList.insert("xlink:type","simple");
250
92
  if (!m_HRef.empty())
251
92
    propList.insert("xlink:href",m_HRef.c_str());
252
92
  return true;
253
92
}
254
255
// border function
256
bool STOFFBorderLine::addTo(librevenge::RVNGPropertyList &propList, std::string const which) const
257
994k
{
258
994k
  std::stringstream stream, field;
259
994k
  field << "fo:border";
260
994k
  if (which.length())
261
994k
    field << "-" << which;
262
994k
  if (isEmpty()) {
263
723k
    propList.insert(field.str().c_str(), "none");
264
723k
    return true;
265
723k
  }
266
271k
  stream << float(m_inWidth+m_distance+m_outWidth)/20.f << "pt ";
267
271k
  stream << ((m_inWidth && m_outWidth) ? "double" : "solid") << " " << m_color;
268
271k
  propList.insert(field.str().c_str(), stream.str().c_str());
269
270
271k
  if (m_inWidth && m_outWidth) {
271
34.3k
    field.str("");
272
34.3k
    field << "style:border-line-width";
273
34.3k
    if (which.length())
274
34.3k
      field << "-" << which;
275
34.3k
    stream.str("");
276
34.3k
    stream << float(m_inWidth)/20.f << "pt " << float(m_distance)/20.f << "pt " << float(m_outWidth)/20.f << "pt";
277
34.3k
    propList.insert(field.str().c_str(), stream.str().c_str());
278
34.3k
  }
279
271k
  return true;
280
994k
}
281
282
std::ostream &operator<< (std::ostream &o, STOFFBorderLine const &border)
283
0
{
284
0
  if (border.m_outWidth) o << "width[out]=" << border.m_outWidth << ":";
285
0
  if (border.m_inWidth) o << "width[in]=" << border.m_inWidth << ":";
286
0
  if (border.m_distance) o << "distance=" << border.m_distance << ":";
287
0
  if (!border.m_color.isBlack()) o << "col=" << border.m_color << ":";
288
0
  o << ",";
289
0
  return o;
290
0
}
291
292
// picture function
293
STOFFEmbeddedObject::~STOFFEmbeddedObject()
294
494k
{
295
494k
}
296
297
bool STOFFEmbeddedObject::addAsFillImageTo(librevenge::RVNGPropertyList &propList) const
298
852
{
299
852
  for (size_t i=0; i<m_dataList.size(); ++i) {
300
852
    if (m_dataList[i].empty()) continue;
301
852
    std::string type=m_typeList.size() ? m_typeList[i] : "image/pict";
302
852
    propList.insert("librevenge:mime-type", type.c_str());
303
852
    propList.insert("draw:fill-image", m_dataList[i].getBase64Data());
304
852
    return true;
305
852
  }
306
0
  return false;
307
852
}
308
309
bool STOFFEmbeddedObject::addTo(librevenge::RVNGPropertyList &propList) const
310
37.8k
{
311
37.8k
  bool firstSet=false;
312
37.8k
  librevenge::RVNGPropertyListVector auxiliarVector;
313
75.7k
  for (size_t i=0; i<m_dataList.size(); ++i) {
314
37.8k
    if (m_dataList[i].empty()) continue;
315
37.8k
    std::string type=m_typeList.size() ? m_typeList[i] : "image/pict";
316
37.8k
    if (!firstSet) {
317
37.8k
      propList.insert("librevenge:mime-type", type.c_str());
318
37.8k
      propList.insert("office:binary-data", m_dataList[i]);
319
37.8k
      firstSet=true;
320
37.8k
      continue;
321
37.8k
    }
322
0
    librevenge::RVNGPropertyList auxiList;
323
0
    auxiList.insert("librevenge:mime-type", type.c_str());
324
0
    auxiList.insert("office:binary-data", m_dataList[i]);
325
0
    auxiliarVector.append(auxiList);
326
0
  }
327
37.8k
  if (!m_filenameLink.empty()) {
328
6
    if (!firstSet) {
329
6
      propList.insert("librevenge:xlink", m_filenameLink);
330
6
      firstSet=true;
331
6
    }
332
0
    else {
333
0
      librevenge::RVNGPropertyList auxiList;
334
0
      auxiList.insert("librevenge:xlink", m_filenameLink);
335
0
      auxiliarVector.append(auxiList);
336
0
    }
337
6
  }
338
37.8k
  if (!auxiliarVector.empty())
339
0
    propList.insert("librevenge:replacement-objects", auxiliarVector);
340
37.8k
  if (!firstSet) {
341
0
    STOFF_DEBUG_MSG(("STOFFEmbeddedObject::addTo: called without picture\n"));
342
0
    return false;
343
0
  }
344
37.8k
  return true;
345
37.8k
}
346
347
int STOFFEmbeddedObject::cmp(STOFFEmbeddedObject const &pict) const
348
0
{
349
0
  if (m_typeList.size()!=pict.m_typeList.size())
350
0
    return m_typeList.size()<pict.m_typeList.size() ? -1 : 1;
351
0
  for (size_t i=0; i<m_typeList.size(); ++i) {
352
0
    if (m_typeList[i]<pict.m_typeList[i]) return -1;
353
0
    if (m_typeList[i]>pict.m_typeList[i]) return 1;
354
0
  }
355
0
  if (m_dataList.size()!=pict.m_dataList.size())
356
0
    return m_dataList.size()<pict.m_dataList.size() ? -1 : 1;
357
0
  for (size_t i=0; i<m_dataList.size(); ++i) {
358
0
    if (m_dataList[i].size() < pict.m_dataList[i].size()) return 1;
359
0
    if (m_dataList[i].size() > pict.m_dataList[i].size()) return -1;
360
361
0
    auto const *ptr=m_dataList[i].getDataBuffer();
362
0
    auto const *aPtr=pict.m_dataList[i].getDataBuffer();
363
0
    if (!ptr || !aPtr) continue; // must only appear if the two buffers are empty
364
0
    for (unsigned long h=0; h < m_dataList[i].size(); ++h, ++ptr, ++aPtr) {
365
0
      if (*ptr < *aPtr) return 1;
366
0
      if (*ptr > *aPtr) return -1;
367
0
    }
368
0
  }
369
0
  return 0;
370
0
}
371
372
std::ostream &operator<<(std::ostream &o, STOFFEmbeddedObject const &pict)
373
0
{
374
0
  if (pict.isEmpty()) return o;
375
0
  o << "[";
376
0
  for (auto const &t : pict.m_typeList) {
377
0
    if (t.empty())
378
0
      o << "_,";
379
0
    else
380
0
      o << t << ",";
381
0
  }
382
0
  o << "],";
383
0
  return o;
384
0
}
385
386
namespace libstoff
387
{
388
// some date conversion
389
bool convertToDateTime(uint32_t date, uint32_t time, std::string &dateTime)
390
51.8k
{
391
51.8k
  std::stringstream s;
392
51.8k
  s << std::setfill('0') << std::setw(8) << date;
393
51.8k
  dateTime=s.str();
394
51.8k
  if (dateTime.length()!=8) {
395
3.11k
    STOFF_DEBUG_MSG(("libstoff:convertToDateTime: can not convert the date"));
396
3.11k
    return false;
397
3.11k
  }
398
48.7k
  s.str("");
399
48.7k
  s << std::setfill('0') << std::setw(8) << time;
400
48.7k
  if (s.str().length()!=8) {
401
5.71k
    STOFF_DEBUG_MSG(("libstoff:convertToDateTime: can not convert the time"));
402
5.71k
    return false;
403
5.71k
  }
404
43.0k
  dateTime+=s.str();
405
43.0k
  dateTime.insert(14,1,'.');
406
43.0k
  dateTime.insert(12,1,':');
407
43.0k
  dateTime.insert(10,1,':');
408
43.0k
  dateTime.insert(8,1,'T');
409
43.0k
  dateTime.insert(6,1,'-');
410
43.0k
  dateTime.insert(4,1,'-');
411
43.0k
  return true;
412
48.7k
}
413
414
void splitString(librevenge::RVNGString const &string, librevenge::RVNGString const &delim,
415
                 librevenge::RVNGString &string1, librevenge::RVNGString &string2)
416
710
{
417
710
  std::string fString(string.cstr());
418
710
  std::string fDelim(delim.cstr());
419
710
  size_t pos=fString.find(fDelim);
420
710
  if (pos!=std::string::npos) {
421
17
    if (pos+fDelim.length()<fString.length())
422
17
      string2=librevenge::RVNGString(fString.substr(pos+fDelim.length()).c_str());
423
17
    if (pos>0)
424
17
      string1=librevenge::RVNGString(fString.substr(0,pos).c_str());
425
17
  }
426
693
  else
427
693
    string1=string;
428
710
}
429
430
librevenge::RVNGString simplifyString(librevenge::RVNGString const &s)
431
109k
{
432
109k
  librevenge::RVNGString res("");
433
109k
  char const *ptr=s.cstr();
434
109k
  if (!ptr) return res;
435
109k
  int numBad=0;
436
1.34M
  while (*ptr) {
437
1.23M
    char c=*(ptr++);
438
1.23M
    if (unsigned(c)<0x80) {
439
1.01M
      if (numBad) {
440
32.1k
        numBad=0;
441
32.1k
        res.append('@');
442
32.1k
      }
443
1.01M
      res.append(c);
444
1.01M
      continue;
445
1.01M
    }
446
219k
    if (numBad++>=4) {
447
27.2k
      res.append('@');
448
27.2k
      numBad=0;
449
27.2k
    }
450
219k
  }
451
109k
  if (numBad) res.append('@');
452
109k
  return res;
453
109k
}
454
455
std::string getCellName(STOFFVec2i const &cellPos, STOFFVec2b const &relative)
456
0
{
457
0
  if (cellPos[0]<0 || cellPos[0]>=26*26*26 || cellPos[1]<0) {
458
0
    STOFF_DEBUG_MSG(("libwps::getCellName: invalid cell position\n"));
459
0
    return "";
460
0
  }
461
0
  std::stringstream o;
462
0
  if (!relative[0]) o << "$";
463
0
  if (cellPos[0]>=26*26) o << (char)(cellPos[0]/26/26-1 + 'A');
464
0
  if (cellPos[0]>=26) o << (char)((cellPos[0]%(26*26))/26-1 + 'A');
465
0
  o << (char)(cellPos[0]%26+'A');
466
0
  if (!relative[1]) o << "$";
467
0
  o << cellPos[1]+1;
468
0
  return o.str();
469
0
}
470
// a little geometry
471
STOFFVec2f rotatePointAroundCenter(STOFFVec2f const &point, STOFFVec2f const &center, float angle)
472
0
{
473
0
  float angl=float(M_PI/180.)*angle;
474
0
  STOFFVec2f pt = point-center;
475
0
  return center + STOFFVec2f(std::cos(angl)*pt[0]-std::sin(angl)*pt[1],
476
0
                             std::sin(angl)*pt[0]+std::cos(angl)*pt[1]);
477
0
}
478
479
STOFFBox2f rotateBoxFromCenter(STOFFBox2f const &box, float angle)
480
0
{
481
0
  STOFFVec2f center=box.center();
482
0
  STOFFVec2f minPt, maxPt;
483
0
  for (int p=0; p<4; ++p) {
484
0
    STOFFVec2f pt=rotatePointAroundCenter(STOFFVec2f(box[p<2?0:1][0],box[(p%2)?0:1][1]), center, angle);
485
0
    if (p==0) {
486
0
      minPt=maxPt=pt;
487
0
      continue;
488
0
    }
489
0
    for (int c=0; c<2; ++c) {
490
0
      if (pt[c]<minPt[c])
491
0
        minPt[c]=pt[c];
492
0
      else if (pt[c]>maxPt[c])
493
0
        maxPt[c]=pt[c];
494
0
    }
495
0
  }
496
0
  return STOFFBox2f(minPt,maxPt);
497
0
}
498
499
float getScaleFactor(librevenge::RVNGUnit orig, librevenge::RVNGUnit dest)
500
0
{
501
0
  float actSc = 1.0, newSc = 1.0;
502
0
  switch (orig) {
503
0
  case librevenge::RVNG_TWIP:
504
0
    break;
505
0
  case librevenge::RVNG_POINT:
506
0
    actSc=20;
507
0
    break;
508
0
  case librevenge::RVNG_INCH:
509
0
    actSc = 1440;
510
0
    break;
511
0
  case librevenge::RVNG_PERCENT:
512
0
  case librevenge::RVNG_GENERIC:
513
0
  case librevenge::RVNG_UNIT_ERROR:
514
0
  default:
515
0
    STOFF_DEBUG_MSG(("libstoff::getScaleFactor %d unit must not appear\n", int(orig)));
516
0
  }
517
0
  switch (dest) {
518
0
  case librevenge::RVNG_TWIP:
519
0
    break;
520
0
  case librevenge::RVNG_POINT:
521
0
    newSc=20;
522
0
    break;
523
0
  case librevenge::RVNG_INCH:
524
0
    newSc = 1440;
525
0
    break;
526
0
  case librevenge::RVNG_PERCENT:
527
0
  case librevenge::RVNG_GENERIC:
528
0
  case librevenge::RVNG_UNIT_ERROR:
529
0
  default:
530
0
    STOFF_DEBUG_MSG(("libstoff::getScaleFactor %d unit must not appear\n", int(dest)));
531
0
  }
532
0
  return actSc/newSc;
533
0
}
534
535
// debug message
536
#ifdef DEBUG
537
void printDebugMsg(const char *format, ...)
538
{
539
  va_list args;
540
  va_start(args, format);
541
  std::vfprintf(stderr, format, args);
542
  va_end(args);
543
}
544
#endif
545
}
546
547
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: