Coverage Report

Created: 2026-04-29 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/libwps_internal.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) 2002, 2005 William Lachance (william.lachance@sympatico.ca)
11
 * Copyright (C) 2002, 2004 Marc Maurer (uwog@uwog.net)
12
 *
13
 * For minor contributions see the git repository.
14
 *
15
 * Alternatively, the contents of this file may be used under the terms
16
 * of the GNU Lesser General Public License Version 2.1 or later
17
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
18
 * applicable instead of those above.
19
 *
20
 * For further information visit http://libwps.sourceforge.net
21
 */
22
23
#include <stdlib.h>
24
#include <time.h>
25
26
#include <cmath>
27
#include <cstdarg>
28
#include <cstdio>
29
#include <iomanip>
30
#include <limits>
31
#include <sstream>
32
#include <string>
33
34
#include <librevenge/librevenge.h>
35
36
#include "libwps_internal.h"
37
38
namespace libwps
39
{
40
uint8_t readU8(librevenge::RVNGInputStream *input)
41
3.83G
{
42
3.83G
  unsigned long numBytesRead;
43
3.83G
  unsigned char const *p = input->read(sizeof(uint8_t), numBytesRead);
44
45
3.83G
  if (!p || numBytesRead != sizeof(uint8_t))
46
228k
  {
47
228k
    static bool first = true;
48
228k
    if (first)
49
8
    {
50
8
      first = false;
51
8
      WPS_DEBUG_MSG(("libwps::readU8: can not read data\n"));
52
8
    }
53
228k
    return 0;
54
228k
  }
55
56
3.83G
  return *reinterpret_cast<uint8_t const *>(p);
57
3.83G
}
58
59
int8_t read8(librevenge::RVNGInputStream *input)
60
13.4M
{
61
13.4M
  return int8_t(readU8(input));
62
13.4M
}
63
64
uint16_t readU16(librevenge::RVNGInputStream *input)
65
319M
{
66
319M
  uint8_t p0 = readU8(input);
67
319M
  uint8_t p1 = readU8(input);
68
319M
  return uint16_t(p0|(p1<<8));
69
319M
}
70
71
int16_t read16(librevenge::RVNGInputStream *input)
72
99.0M
{
73
99.0M
  return int16_t(readU16(input));
74
99.0M
}
75
76
uint32_t readU32(librevenge::RVNGInputStream *input)
77
102M
{
78
102M
  uint8_t p0 = readU8(input);
79
102M
  uint8_t p1 = readU8(input);
80
102M
  uint8_t p2 = readU8(input);
81
102M
  uint8_t p3 = readU8(input);
82
102M
  return uint32_t((p0<<0)|(p1<<8)|(p2<<16)|(p3<<24));
83
102M
}
84
85
int32_t read32(librevenge::RVNGInputStream *input)
86
7.90M
{
87
7.90M
  return int32_t(readU32(input));
88
7.90M
}
89
90
bool readDouble4(RVNGInputStreamPtr &input, double &res, bool &isNaN)
91
17.9k
{
92
17.9k
  isNaN=false;
93
17.9k
  res = 0;
94
17.9k
  long pos = input->tell();
95
17.9k
  if (input->seek(4, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+4)
96
0
  {
97
0
    WPS_DEBUG_MSG(("libwps::readDouble4: the zone seems too short\n"));
98
0
    input->seek(pos, librevenge::RVNG_SEEK_SET);
99
0
    return false;
100
0
  }
101
17.9k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
102
103
17.9k
  double mantisse = 0;
104
  /** (first&3)==1: is used to decide if we store 100*N or N.,
105
    (first&3)==2: indicates a basic int number (appears mainly when converting a dos file in a windows file)
106
    (first&3)==3: Can this exist ? What does this mean: 100*a basic int ?
107
    The other bytes seem to have classic meaning...
108
  */
109
17.9k
  auto first = int(readU8(input));
110
17.9k
  if ((first&3)==2)
111
5.00k
  {
112
    // so read it as a normal number
113
5.00k
    input->seek(-1, librevenge::RVNG_SEEK_CUR);
114
5.00k
    auto val=long(readU16(input)>>2);
115
5.00k
    val+=long(readU16(input))<<14;
116
5.00k
    if (val&0x20000000)
117
666
      res = double(val-0x40000000);
118
4.33k
    else
119
4.33k
      res = double(val);
120
5.00k
    return true;
121
5.00k
  }
122
12.9k
  mantisse = double(first & 0xFC)/256 + double(readU8(input));
123
12.9k
  auto mantExp = int(readU8(input));
124
12.9k
  mantisse = (mantisse/256 + double(0x10+(mantExp & 0x0F)))/16;
125
12.9k
  int exp = ((mantExp&0xF0)>>4)+int(readU8(input)<<4);
126
12.9k
  int sign = 1;
127
12.9k
  if (exp & 0x800)
128
2.25k
  {
129
2.25k
    exp &= 0x7ff;
130
2.25k
    sign = -1;
131
2.25k
  }
132
133
12.9k
  if (exp == 0)
134
4.92k
  {
135
4.92k
    if (double(mantisse) > 1.-1e-4)  return true; // ok zero
136
    // fixme find Nan representation
137
0
    return false;
138
4.92k
  }
139
8.02k
  if (exp == 0x7FF)
140
1.05k
  {
141
1.05k
    if (double(mantisse) > 1.-1e-4)
142
1.05k
    {
143
1.05k
      res=std::numeric_limits<double>::quiet_NaN();
144
1.05k
      isNaN=true;
145
      /* 0x7FFFF.. are nan(infinite, ...):ok
146
147
         0xFFFFF.. are nan(in the sense, not a number but
148
         text...). In this case wps2csv and wps2text will
149
         display a nan. Not good, but difficult to retrieve the
150
         cell's content without excuting the formula associated
151
         to this cell :-~
152
       */
153
1.05k
      return true;
154
1.05k
    }
155
0
    return false;
156
1.05k
  }
157
158
6.96k
  exp -= 0x3ff;
159
6.96k
  res = std::ldexp(mantisse, exp);
160
6.96k
  if (sign == -1)
161
1.19k
  {
162
1.19k
    res *= -1.;
163
1.19k
  }
164
6.96k
  if (first & 1) res/=100;
165
6.96k
  if (first & 2)
166
1.28k
  {
167
    // CHECKME...
168
1.28k
    WPS_DEBUG_MSG(("libwps::readDouble4: ARRGGGGGGGGGG find a float with first & 3 ARRGGGGGGGGGG in pos%lx,\n some float can be broken\n", static_cast<unsigned long>(pos)));
169
1.28k
  }
170
6.96k
  return true;
171
8.02k
}
172
173
bool readDouble8(RVNGInputStreamPtr &input, double &res, bool &isNaN)
174
838k
{
175
838k
  isNaN=false;
176
838k
  res = 0;
177
838k
  long pos = input->tell();
178
838k
  if (input->seek(8, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+8)
179
41
  {
180
41
    WPS_DEBUG_MSG(("libwps::readDouble8: the zone seems too short\n"));
181
41
    input->seek(pos, librevenge::RVNG_SEEK_SET);
182
41
    return false;
183
41
  }
184
838k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
185
838k
  double mantisse = 0;
186
5.86M
  for (int i = 0; i < 6; i++)
187
5.03M
    mantisse = mantisse/256 + double(readU8(input));
188
838k
  auto mantExp = int(readU8(input));
189
838k
  mantisse = (mantisse/256 + double(0x10+(mantExp & 0x0F)))/16;
190
838k
  int exp = ((mantExp&0xF0)>>4)+int(readU8(input)<<4);
191
838k
  int sign = 1;
192
838k
  if (exp & 0x800)
193
85.3k
  {
194
85.3k
    exp &= 0x7ff;
195
85.3k
    sign = -1;
196
85.3k
  }
197
198
838k
  double const epsilon=1.e-5;
199
838k
  if (exp == 0)
200
412k
  {
201
412k
    if (mantisse > 1-epsilon && mantisse < 1+epsilon)  return true; // ok zero
202
    // fixme find Nan representation
203
90.5k
    return false;
204
412k
  }
205
425k
  if (exp == 0x7FF)
206
22.5k
  {
207
22.5k
    if (mantisse >= 1-epsilon)
208
22.5k
    {
209
22.5k
      res=std::numeric_limits<double>::quiet_NaN();
210
22.5k
      return true; // ok 0x7FF and 0xFFF are nan
211
22.5k
    }
212
0
    return false;
213
22.5k
  }
214
215
402k
  exp -= 0x3ff;
216
402k
  res = std::ldexp(mantisse, exp);
217
402k
  if (sign == -1)
218
64.7k
  {
219
64.7k
    res *= -1.;
220
64.7k
  }
221
402k
  return true;
222
425k
}
223
224
bool readDouble10(RVNGInputStreamPtr &input, double &res, bool &isNaN)
225
108k
{
226
108k
  isNaN=false;
227
108k
  res = 0;
228
108k
  long pos = input->tell();
229
108k
  if (input->seek(10, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+10)
230
37
  {
231
37
    WPS_DEBUG_MSG(("libwps::readDouble8: the zone seems too short\n"));
232
37
    input->seek(pos, librevenge::RVNG_SEEK_SET);
233
37
    return false;
234
37
  }
235
108k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
236
108k
  double mantisse = 0;
237
976k
  for (int i = 0; i < 8; i++)
238
868k
    mantisse = mantisse/256 + double(readU8(input))/128;
239
108k
  auto exp = int(readU16(input));
240
108k
  int sign = 1;
241
108k
  if (exp & 0x8000)
242
9.01k
  {
243
9.01k
    exp &= 0x7fff;
244
9.01k
    sign = -1;
245
9.01k
  }
246
247
108k
  double const epsilon=1.e-5;
248
108k
  if (exp == 0)
249
63.0k
  {
250
63.0k
    if (mantisse < epsilon)  return true; // checkme is this zero or a nan
251
    // fixme find Nan representation
252
4.03k
    return false;
253
63.0k
  }
254
45.4k
  if (exp == 0x7FFf)
255
4.10k
  {
256
4.10k
    if (mantisse >= 1-epsilon)
257
3.78k
    {
258
3.78k
      res=std::numeric_limits<double>::quiet_NaN();
259
3.78k
      return true; // ok 0x7FF and 0xFFF are nan
260
3.78k
    }
261
318
    return false;
262
4.10k
  }
263
264
41.3k
  exp -= 0x3fff;
265
41.3k
  res = std::ldexp(mantisse, exp);
266
41.3k
  if (sign == -1)
267
4.90k
  {
268
4.90k
    res *= -1.;
269
4.90k
  }
270
41.3k
  return true;
271
45.4k
}
272
273
bool readDouble2Inv(RVNGInputStreamPtr &input, double &res, bool &isNaN)
274
16.2k
{
275
16.2k
  isNaN=false;
276
16.2k
  res = 0;
277
16.2k
  long pos = input->tell();
278
16.2k
  if (input->seek(2, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+2)
279
4
  {
280
4
    WPS_DEBUG_MSG(("libwps::readDouble2Inv: the zone seems too short\n"));
281
4
    input->seek(pos, librevenge::RVNG_SEEK_SET);
282
4
    return false;
283
4
  }
284
16.2k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
285
16.2k
  auto val=int(libwps::readU16(input));
286
16.2k
  int exp=val&0xf;
287
16.2k
  if ((exp&1)==1)
288
3.20k
  {
289
3.20k
    int mantisse=(val>>4);
290
3.20k
    if ((mantisse&0x800))
291
738
      mantisse -= 0x1000;
292
3.20k
    exp/=2;
293
3.20k
    const double factors[8]= { 5000, 500, 0.05, 0.005, 0.0005, 0.00005, 1/16., 1/64. };
294
3.20k
    res=double(mantisse)*factors[exp];
295
3.20k
    return true;
296
3.20k
  }
297
13.0k
  if ((val&0x8000))
298
325
    val-=0x10000;
299
13.0k
  res=double(val>>1);
300
13.0k
  return true;
301
16.2k
}
302
303
bool readDouble4Inv(RVNGInputStreamPtr &input, double &res, bool &isNaN)
304
69.7k
{
305
69.7k
  isNaN=false;
306
69.7k
  res = 0;
307
69.7k
  long pos = input->tell();
308
69.7k
  if (input->seek(4, librevenge::RVNG_SEEK_CUR) || input->tell()!=pos+4)
309
16
  {
310
16
    WPS_DEBUG_MSG(("libwps::readDouble4Inv: the zone seems too short\n"));
311
16
    input->seek(pos, librevenge::RVNG_SEEK_SET);
312
16
    return false;
313
16
  }
314
69.7k
  input->seek(pos, librevenge::RVNG_SEEK_SET);
315
69.7k
  auto val=long(libwps::readU32(input));
316
69.7k
  auto exp=int(val&0xf);
317
69.7k
  auto mantisse=int(val>>6);
318
69.7k
  if (val&0x20)
319
2.83k
    mantisse *= -1;
320
69.7k
  if (exp)
321
38.1k
  {
322
38.1k
    if (val&0x10)
323
27.2k
      res=mantisse/std::pow(10., exp);
324
10.9k
    else
325
10.9k
      res=mantisse*std::pow(10., exp);
326
38.1k
    return true;
327
38.1k
  }
328
31.5k
  res=double(mantisse);
329
31.5k
  return true;
330
69.7k
}
331
332
bool readData(RVNGInputStreamPtr &input, unsigned long size, librevenge::RVNGBinaryData &data)
333
12.9k
{
334
12.9k
  data.clear();
335
12.9k
  if (size == 0) return true;
336
337
12.9k
  const unsigned char *readData;
338
12.9k
  unsigned long sizeRead;
339
12.9k
  if ((readData=input->read(size, sizeRead)) == nullptr || sizeRead!=size)
340
0
    return false;
341
12.9k
  data.append(readData, sizeRead);
342
343
12.9k
  return true;
344
12.9k
}
345
346
bool readDataToEnd(RVNGInputStreamPtr &input, librevenge::RVNGBinaryData &data)
347
10.1k
{
348
10.1k
  data.clear();
349
10.1k
  long pos=input->tell();
350
10.1k
  input->seek(0,librevenge::RVNG_SEEK_END);
351
10.1k
  long sz=input->tell()-pos;
352
10.1k
  if (sz < 0) return false;
353
10.1k
  input->seek(pos,librevenge::RVNG_SEEK_SET);
354
10.1k
  return readData(input, static_cast<unsigned long>(sz), data) && input->isEnd();
355
10.1k
}
356
357
std::string numberingTypeToString(NumberingType type)
358
209k
{
359
209k
  switch (type)
360
209k
  {
361
173k
  case ARABIC:
362
173k
    return "1";
363
13.8k
  case LOWERCASE:
364
13.8k
    return "a";
365
2.78k
  case UPPERCASE:
366
2.78k
    return "A";
367
8.54k
  case LOWERCASE_ROMAN:
368
8.54k
    return "i";
369
10.7k
  case UPPERCASE_ROMAN:
370
10.7k
    return "I";
371
0
  case NONE:
372
0
  case BULLET:
373
0
  default:
374
0
    break;
375
209k
  }
376
0
  WPS_DEBUG_MSG(("libwps::numberingTypeToString: must not be called with type %d\n", int(type)));
377
0
  return "1";
378
209k
}
379
}
380
381
////////////////////////////////////////////////////////////
382
// color
383
////////////////////////////////////////////////////////////
384
385
// color function
386
WPSColor WPSColor::barycenter(float alpha, WPSColor const &colA,
387
                              float beta, WPSColor const &colB)
388
437k
{
389
437k
  uint32_t res = 0;
390
2.18M
  for (int i=0, depl=0; i<4; i++, depl+=8)
391
1.74M
  {
392
1.74M
    float val=alpha*float((colA.m_value>>depl)&0xFF)+beta*float((colB.m_value>>depl)&0xFF);
393
1.74M
    if (val < 0) val=0;
394
1.74M
    if (val > 256) val=256;
395
1.74M
    auto comp= static_cast<unsigned char>(val);
396
1.74M
    res+=uint32_t(comp<<depl);
397
1.74M
  }
398
437k
  return WPSColor(res);
399
437k
}
400
401
std::ostream &operator<< (std::ostream &o, WPSColor const &c)
402
11.2M
{
403
11.2M
  auto const  width = o.width();
404
11.2M
  auto const fill = o.fill();
405
11.2M
  o << "#" << std::hex << std::setfill('0') << std::setw(6)
406
11.2M
    << (c.m_value&0xFFFFFF)
407
    // std::ios::width() takes/returns std::streamsize (long), but
408
    // std::setw() takes int. Go figure...
409
11.2M
    << std::dec << std::setfill(fill) << std::setw(static_cast<int>(width));
410
11.2M
  return o;
411
11.2M
}
412
413
std::string WPSColor::str() const
414
11.2M
{
415
11.2M
  std::stringstream stream;
416
11.2M
  stream << *this;
417
11.2M
  return stream.str();
418
11.2M
}
419
420
////////////////////////////////////////////////////////////
421
// field
422
////////////////////////////////////////////////////////////
423
// format function
424
namespace libwps
425
{
426
static bool convertDTFormat(std::string const &dtFormat, librevenge::RVNGPropertyListVector &propVect)
427
237k
{
428
237k
  propVect.clear();
429
237k
  std::string text("");
430
237k
  librevenge::RVNGPropertyList list;
431
237k
  size_t len=dtFormat.size();
432
1.76M
  for (size_t c=0; c < len; ++c)
433
1.52M
  {
434
1.52M
    if (dtFormat[c]!='%' || c+1==len)
435
645k
    {
436
645k
      text+=dtFormat[c];
437
645k
      continue;
438
645k
    }
439
882k
    char ch=dtFormat[++c];
440
882k
    if (ch=='%')
441
0
    {
442
0
      text += '%';
443
0
      continue;
444
0
    }
445
882k
    if (!text.empty())
446
645k
    {
447
645k
      list.clear();
448
645k
      list.insert("librevenge:value-type", "text");
449
645k
      list.insert("librevenge:text", text.c_str());
450
645k
      propVect.append(list);
451
645k
      text.clear();
452
645k
    }
453
882k
    list.clear();
454
882k
    switch (ch)
455
882k
    {
456
2.10k
    case 'Y':
457
2.10k
      list.insert("number:style", "long");
458
2.10k
      WPS_FALLTHROUGH;
459
63.5k
    case 'y':
460
63.5k
      list.insert("librevenge:value-type", "year");
461
63.5k
      propVect.append(list);
462
63.5k
      break;
463
843
    case 'B':
464
843
      list.insert("number:style", "long");
465
843
      WPS_FALLTHROUGH;
466
843
    case 'b':
467
843
    case 'h':
468
843
      list.insert("librevenge:value-type", "month");
469
843
      list.insert("number:textual", true);
470
843
      propVect.append(list);
471
843
      break;
472
62.6k
    case 'm':
473
62.6k
      list.insert("librevenge:value-type", "month");
474
62.6k
      propVect.append(list);
475
62.6k
      break;
476
0
    case 'e':
477
0
      list.insert("number:style", "long");
478
0
      WPS_FALLTHROUGH;
479
63.2k
    case 'd':
480
63.2k
      list.insert("librevenge:value-type", "day");
481
63.2k
      propVect.append(list);
482
63.2k
      break;
483
498
    case 'A':
484
498
      list.insert("number:style", "long");
485
498
      WPS_FALLTHROUGH;
486
498
    case 'a':
487
498
      list.insert("librevenge:value-type", "day-of-week");
488
498
      propVect.append(list);
489
498
      break;
490
491
1.22k
    case 'H':
492
1.22k
      list.insert("number:style", "long");
493
1.22k
      WPS_FALLTHROUGH;
494
173k
    case 'I':
495
173k
      list.insert("librevenge:value-type", "hours");
496
173k
      propVect.append(list);
497
173k
      break;
498
173k
    case 'M':
499
173k
      list.insert("librevenge:value-type", "minutes");
500
173k
      list.insert("number:style", "long");
501
173k
      propVect.append(list);
502
173k
      break;
503
172k
    case 'S':
504
172k
      list.insert("librevenge:value-type", "seconds");
505
172k
      list.insert("number:style", "long");
506
172k
      propVect.append(list);
507
172k
      break;
508
171k
    case 'p':
509
171k
      list.clear();
510
171k
      list.insert("librevenge:value-type", "am-pm");
511
171k
      propVect.append(list);
512
171k
      break;
513
#if !defined(__clang__)
514
    default:
515
      WPS_DEBUG_MSG(("convertDTFormat: find unimplement command %c(ignored)\n", ch));
516
#endif
517
882k
    }
518
882k
  }
519
237k
  if (!text.empty())
520
0
  {
521
0
    list.clear();
522
0
    list.insert("librevenge:value-type", "text");
523
0
    list.insert("librevenge:text", text.c_str());
524
0
    propVect.append(list);
525
0
  }
526
237k
  return propVect.count()!=0;
527
237k
}
528
}
529
530
bool WPSField::addTo(librevenge::RVNGPropertyList &propList) const
531
474k
{
532
474k
  switch (m_type)
533
474k
  {
534
65.4k
  case Date:
535
65.4k
  {
536
65.4k
    propList.insert("librevenge:field-type", "text:date");
537
538
65.4k
    librevenge::RVNGPropertyListVector pVect;
539
65.4k
    if (!libwps::convertDTFormat(m_DTFormat.empty() ? std::string("%m/%d/%y") : m_DTFormat, pVect))
540
0
      break;
541
542
65.4k
    propList.insert("librevenge:value-type", "date");
543
65.4k
    propList.insert("number:automatic-order", "true");
544
65.4k
    propList.insert("librevenge:format", pVect);
545
65.4k
    break;
546
65.4k
  }
547
0
  case PageCount:
548
0
    propList.insert("librevenge:field-type", "text:page-count");
549
0
    propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str());
550
0
    break;
551
168k
  case PageNumber:
552
169k
  case PageNumberNext:
553
169k
    propList.insert("librevenge:field-type", "text:page-number");
554
169k
    propList.insert("style:num-format", numberingTypeToString(m_numberingType).c_str());
555
169k
    if (m_type==PageNumberNext)
556
1.23k
      propList.insert("text:select-page", "next");
557
169k
    break;
558
68.0k
  case Title:
559
68.0k
    propList.insert("librevenge:field-type", "text:title");
560
68.0k
    break;
561
171k
  case Time:
562
171k
  {
563
171k
    propList.insert("librevenge:field-type", "text:time");
564
565
171k
    librevenge::RVNGPropertyListVector pVect;
566
171k
    if (!libwps::convertDTFormat(m_DTFormat.empty() ? std::string("%I:%M:%S %p") : m_DTFormat, pVect))
567
0
      break;
568
569
171k
    propList.insert("librevenge:value-type", "time");
570
171k
    propList.insert("number:automatic-order", "true");
571
171k
    propList.insert("librevenge:format", pVect);
572
171k
    break;
573
171k
  }
574
575
0
  case Database:
576
0
  case Link:
577
0
  case None:
578
0
  default:
579
0
    return false;
580
474k
  }
581
474k
  return true;
582
474k
}
583
584
librevenge::RVNGString WPSField::getString() const
585
0
{
586
0
  librevenge::RVNGString res;
587
0
  switch (m_type)
588
0
  {
589
0
  case Database:
590
0
    if (m_data.length())
591
0
      res=librevenge::RVNGString(m_data.c_str());
592
0
    else
593
0
      res=librevenge::RVNGString("#DATAFIELD#");
594
0
    break;
595
0
  case Link:
596
0
    if (m_data.length())
597
0
      res=librevenge::RVNGString(m_data.c_str());
598
0
    else
599
0
      res=librevenge::RVNGString("#LINK#");
600
0
    break;
601
0
  case Title:
602
0
    if (m_data.length())
603
0
      res=librevenge::RVNGString(m_data.c_str());
604
0
    else
605
0
      res=librevenge::RVNGString("#TITLE#");
606
0
    break;
607
0
  case Date:
608
0
  case PageCount:
609
0
  case PageNumber:
610
0
  case PageNumberNext:
611
0
  case Time:
612
0
  case None:
613
0
  default:
614
0
    break;
615
0
  }
616
617
0
  return res;
618
0
}
619
////////////////////////////////////////////////////////////
620
// border
621
////////////////////////////////////////////////////////////
622
623
int WPSBorder::compare(WPSBorder const &orig) const
624
24.3k
{
625
24.3k
  int diff = int(m_style)-int(orig.m_style);
626
24.3k
  if (diff) return diff;
627
22.6k
  diff = m_width-orig.m_width;
628
22.6k
  if (diff) return diff;
629
22.4k
  if (m_color < orig.m_color) return -1;
630
21.7k
  if (m_color > orig.m_color) return 1;
631
21.1k
  if (m_widthsList.size() != orig.m_widthsList.size())
632
0
    return m_widthsList.size() < orig.m_widthsList.size() ? -1 : 1;
633
21.1k
  for (size_t i=0; i<m_widthsList.size(); ++i)
634
0
  {
635
0
    if (m_widthsList[i]<orig.m_widthsList[i]) return -1;
636
0
    if (m_widthsList[i]>orig.m_widthsList[i]) return 1;
637
0
  }
638
21.1k
  return 0;
639
21.1k
}
640
641
bool WPSBorder::addTo(librevenge::RVNGPropertyList &propList, std::string const which) const
642
1.06M
{
643
1.06M
  std::stringstream stream, field;
644
1.06M
  stream << m_width << "pt ";
645
1.06M
  if (m_type==WPSBorder::Double || m_type==WPSBorder::Triple)
646
142k
  {
647
142k
    static bool first = true;
648
142k
    if (first && m_style!=Simple)
649
0
    {
650
0
      WPS_DEBUG_MSG(("WPSBorder::addTo: find double or tripe border with complex style\n"));
651
0
      first = false;
652
0
    }
653
142k
    stream << "double";
654
142k
  }
655
921k
  else
656
921k
  {
657
921k
    switch (m_style)
658
921k
    {
659
48.8k
    case Dot:
660
52.9k
    case LargeDot:
661
52.9k
      stream << "dotted";
662
52.9k
      break;
663
98.4k
    case Dash:
664
98.4k
      stream << "dashed";
665
98.4k
      break;
666
770k
    case Simple:
667
770k
      stream << "solid";
668
770k
      break;
669
0
    case None:
670
0
    default:
671
0
      stream << "none";
672
0
      break;
673
921k
    }
674
921k
  }
675
1.06M
  stream << " " << m_color.str();
676
1.06M
  field << "fo:border";
677
1.06M
  if (which.length())
678
1.06M
    field << "-" << which;
679
1.06M
  propList.insert(field.str().c_str(), stream.str().c_str());
680
1.06M
  size_t numRelWidth=m_widthsList.size();
681
1.06M
  if (!numRelWidth)
682
1.06M
    return true;
683
0
  if (m_type!=WPSBorder::Double || numRelWidth!=3)
684
0
  {
685
0
    static bool first = true;
686
0
    if (first)
687
0
    {
688
0
      WPS_DEBUG_MSG(("WPSBorder::addTo: relative width is only implemented with double style\n"));
689
0
      first = false;
690
0
    }
691
0
    return true;
692
0
  }
693
0
  double totalWidth=0;
694
0
  for (auto const &w : m_widthsList)
695
0
    totalWidth+=w;
696
0
  if (totalWidth <= 0)
697
0
  {
698
0
    WPS_DEBUG_MSG(("WPSBorder::addTo: can not compute total width\n"));
699
0
    return true;
700
0
  }
701
0
  double factor=m_width/totalWidth;
702
0
  stream.str("");
703
0
  for (size_t w=0; w < numRelWidth; w++)
704
0
  {
705
0
    stream << factor *m_widthsList[w]<< "pt";
706
0
    if (w+1!=numRelWidth)
707
0
      stream << " ";
708
0
  }
709
0
  field.str("");
710
0
  field << "style:border-line-width";
711
0
  if (!which.empty())
712
0
    field << "-" << which;
713
0
  propList.insert(field.str().c_str(), stream.str().c_str());
714
0
  return true;
715
0
}
716
717
std::ostream &operator<< (std::ostream &o, WPSBorder::Style const &style)
718
0
{
719
0
  switch (style)
720
0
  {
721
0
  case WPSBorder::None:
722
0
    o << "none";
723
0
    break;
724
0
  case WPSBorder::Simple:
725
0
    break;
726
0
  case WPSBorder::Dot:
727
0
    o << "dot";
728
0
    break;
729
0
  case WPSBorder::LargeDot:
730
0
    o << "large dot";
731
0
    break;
732
0
  case WPSBorder::Dash:
733
0
    o << "dash";
734
0
    break;
735
0
  default:
736
0
    WPS_DEBUG_MSG(("WPSBorder::operator<<: find unknown style\n"));
737
0
    o << "#style=" << int(style);
738
0
    break;
739
0
  }
740
0
  return o;
741
0
}
742
743
std::ostream &operator<< (std::ostream &o, WPSBorder const &border)
744
0
{
745
0
  o << border.m_style << ":";
746
0
  switch (border.m_type)
747
0
  {
748
0
  case WPSBorder::Single:
749
0
    break;
750
0
  case WPSBorder::Double:
751
0
    o << "double:";
752
0
    break;
753
0
  case WPSBorder::Triple:
754
0
    o << "triple:";
755
0
    break;
756
0
  default:
757
0
    WPS_DEBUG_MSG(("WPSBorder::operator<<: find unknown type\n"));
758
0
    o << "#type=" << int(border.m_type) << ":";
759
0
    break;
760
0
  }
761
0
  if (border.m_width > 1 || border.m_width < 1) o << "w=" << border.m_width << ":";
762
0
  if (!border.m_color.isBlack())
763
0
    o << "col=" << std::hex << border.m_color << std::dec << ":";
764
0
  o << ",";
765
0
  size_t numRelWidth=border.m_widthsList.size();
766
0
  if (numRelWidth)
767
0
  {
768
0
    o << "bordW[rel]=[";
769
0
    for (auto const &w : border.m_widthsList)
770
0
      o << w << ",";
771
0
    o << "]:";
772
0
  }
773
0
  o << border.m_extra;
774
0
  return o;
775
0
}
776
777
////////////////////////////////////////////////////////////
778
// object
779
////////////////////////////////////////////////////////////
780
WPSEmbeddedObject::~WPSEmbeddedObject()
781
385k
{
782
385k
}
783
784
bool WPSEmbeddedObject::addTo(librevenge::RVNGPropertyList &propList) const
785
2.32k
{
786
2.32k
  bool firstSet=false;
787
2.32k
  librevenge::RVNGPropertyListVector auxiliarVector;
788
4.63k
  for (size_t i=0; i<m_dataList.size(); ++i)
789
2.30k
  {
790
2.30k
    if (m_dataList[i].empty()) continue;
791
2.30k
    std::string type=m_typeList.size() ? m_typeList[i] : "image/pict";
792
2.30k
    if (!firstSet)
793
2.09k
    {
794
2.09k
      propList.insert("librevenge:mime-type", type.c_str());
795
2.09k
      propList.insert("office:binary-data", m_dataList[i]);
796
2.09k
      firstSet=true;
797
2.09k
      continue;
798
2.09k
    }
799
211
    librevenge::RVNGPropertyList auxiList;
800
211
    auxiList.insert("librevenge:mime-type", type.c_str());
801
211
    auxiList.insert("office:binary-data", m_dataList[i]);
802
211
    auxiliarVector.append(auxiList);
803
211
  }
804
2.32k
  if (!auxiliarVector.empty())
805
202
    propList.insert("librevenge:replacement-objects", auxiliarVector);
806
2.32k
  if (!firstSet)
807
225
  {
808
225
    WPS_DEBUG_MSG(("WPSEmbeddedObject::addTo: called without picture\n"));
809
225
    return false;
810
225
  }
811
2.09k
  return true;
812
2.32k
}
813
814
std::ostream &operator<<(std::ostream &o, WPSEmbeddedObject const &pict)
815
0
{
816
0
  if (pict.isEmpty()) return o;
817
0
  if (pict.m_size!=Vec2f())
818
0
    o << "size=" << pict.m_size << ",";
819
0
  o << "[";
820
0
  for (auto const &type : pict.m_typeList)
821
0
  {
822
0
    if (type.empty())
823
0
      o << "_,";
824
0
    else
825
0
      o << type << ",";
826
0
  }
827
0
  o << "],";
828
0
  return o;
829
0
}
830
831
////////////////////////////////////////////////////////////
832
// unicode
833
////////////////////////////////////////////////////////////
834
namespace libwps
835
{
836
void appendUnicode(uint32_t val, librevenge::RVNGString &buffer)
837
142M
{
838
142M
  if (val < 0x20)
839
822k
  {
840
822k
    WPS_DEBUG_MSG(("libwps::appendUnicode: find an old char %x, skip it\n", val));
841
822k
    return;
842
822k
  }
843
142M
  uint8_t first;
844
142M
  int len;
845
142M
  if (val < 0x80)
846
43.8M
  {
847
43.8M
    first = 0;
848
43.8M
    len = 1;
849
43.8M
  }
850
98.2M
  else if (val < 0x800)
851
31.3M
  {
852
31.3M
    first = 0xc0;
853
31.3M
    len = 2;
854
31.3M
  }
855
66.9M
  else if (val < 0x10000)
856
66.4M
  {
857
66.4M
    first = 0xe0;
858
66.4M
    len = 3;
859
66.4M
  }
860
493k
  else if (val < 0x200000)
861
493k
  {
862
493k
    first = 0xf0;
863
493k
    len = 4;
864
493k
  }
865
0
  else if (val < 0x4000000)
866
0
  {
867
0
    first = 0xf8;
868
0
    len = 5;
869
0
  }
870
0
  else
871
0
  {
872
0
    first = 0xfc;
873
0
    len = 6;
874
0
  }
875
876
142M
  char outbuf[7];
877
142M
  int i;
878
307M
  for (i = len - 1; i > 0; --i)
879
165M
  {
880
165M
    outbuf[i] = char((val & 0x3f) | 0x80);
881
165M
    val >>= 6;
882
165M
  }
883
142M
  outbuf[0] = char(val | first);
884
142M
  outbuf[len] = 0;
885
142M
  buffer.append(outbuf);
886
142M
}
887
}
888
889
// a little geometry
890
WPSTransformation WPSTransformation::rotation(float angle, Vec2f const &center)
891
23.7k
{
892
23.7k
  auto angl=float(double(angle)*M_PI/180);
893
23.7k
  auto cosA=float(std::cos(angl));
894
23.7k
  auto sinA=float(std::sin(angl));
895
23.7k
  return WPSTransformation(WPSVec3f(cosA, -sinA, center[0]-cosA*center[0]+sinA*center[1]),
896
23.7k
                           WPSVec3f(sinA, cosA, center[1]-sinA*center[0]-cosA*center[1]));
897
23.7k
}
898
899
bool WPSTransformation::decompose(float &rot, Vec2f &shearing, WPSTransformation &transform, Vec2f const &origCenter) const
900
23.5k
{
901
23.5k
  if (m_isIdentity) return false;
902
23.5k
  WPSVec3f const &xRow=(*this)[0];
903
23.5k
  WPSVec3f const &yRow=(*this)[1];
904
23.5k
  Vec2f const &center=*this * origCenter;
905
  // first check shearing
906
23.5k
  float shearY=0;
907
23.5k
  float val1=xRow[0]*xRow[1];
908
23.5k
  float val2=yRow[0]*yRow[1];
909
23.5k
  float diff=val2-val1;
910
23.5k
  if (diff<-0.01f || diff>0.01f)
911
21.8k
  {
912
21.8k
    float const &A=val1;
913
21.8k
    float const B=xRow[1]*yRow[0]+xRow[0]*yRow[1];
914
21.8k
    float const &C=diff;
915
21.8k
    if (A>=0 && A<=0)
916
2.94k
    {
917
2.94k
      if (B>=0 && A<=0)
918
1.45k
      {
919
1.45k
        WPS_DEBUG_MSG(("WPSTransformation::decompose: can not determine the shearing\n"));
920
1.45k
        return false;
921
1.45k
      }
922
1.48k
      shearY=C/B;
923
1.48k
    }
924
18.8k
    else
925
18.8k
    {
926
18.8k
      float const &delta=B*B-4*A*C;
927
18.8k
      if (delta<0)
928
259
      {
929
259
        WPS_DEBUG_MSG(("WPSTransformation::decompose: can not determine the shearing\n"));
930
259
        return false;
931
259
      }
932
18.6k
      shearY=(B-float(std::sqrt(delta)))/2.f/A;
933
18.6k
    }
934
20.1k
    transform=WPSTransformation::shear(Vec2f(0,-shearY), center) **this;
935
20.1k
  }
936
1.69k
  else
937
1.69k
    transform=*this;
938
21.8k
  shearing=Vec2f(0,shearY);
939
  // fixme: we must first check for symetry here...
940
  // now the rotation
941
21.8k
  rot=-std::atan2(-transform[1][0],transform[1][1]);
942
21.8k
  rot *= float(180/M_PI);
943
21.8k
  transform=WPSTransformation::rotation(-rot, center) * transform;
944
21.8k
  return true;
945
23.5k
}
946
947
////////////////////////////////////////////////////////////
948
// utility
949
////////////////////////////////////////////////////////////
950
951
namespace libwps
952
{
953
std::string getCellName(Vec2i const &cellPos, Vec2b const &relative)
954
95.7k
{
955
95.7k
  if (cellPos[0]<0 || cellPos[1]<0)
956
823
  {
957
823
    WPS_DEBUG_MSG(("libwps::getCellName: invalid cell position\n"));
958
823
    return "";
959
823
  }
960
94.9k
  std::stringstream o;
961
94.9k
  if (!relative[0]) o << "$";
962
94.9k
  int c=cellPos[0];
963
94.9k
  std::string colString(1, char(c%26+'A'));
964
94.9k
  c /= 26;
965
168k
  while (c>0)
966
73.6k
  {
967
73.6k
    --c;
968
73.6k
    colString.insert(0, std::string(1,char(c%26+'A')));
969
73.6k
    c /= 26;
970
73.6k
  }
971
94.9k
  o << colString;
972
94.9k
  if (!relative[1]) o << "$";
973
94.9k
  o << cellPos[1]+1;
974
94.9k
  return o.str();
975
95.7k
}
976
977
bool encodeLotusPassword(char const *password, uint16_t &key, std::vector<uint8_t> &keys, uint8_t const(&defValues)[16])
978
3.44k
{
979
3.44k
  if (!password)
980
0
  {
981
0
    WPS_DEBUG_MSG(("libwps::encodeLotusPassword: called without password\n"));
982
0
    return false;
983
0
  }
984
3.44k
  size_t const len=16;
985
3.44k
  key=0xFFFF;
986
3.44k
  uint16_t val=0;
987
18.8k
  for (size_t i=0; i<len; ++i)
988
18.8k
  {
989
18.8k
    if (password[i]==0)
990
3.44k
      break;
991
15.3k
    auto c=uint8_t(password[i]);
992
15.3k
    key=uint16_t(key^c);
993
15.3k
    val=uint16_t((val&0xFF)|(key<<8));
994
15.3k
    val=uint16_t(((val<<4)&0xFFF0)|(val>>12));
995
15.3k
    key^=val;
996
15.3k
    val=uint16_t((val<<8)|(val>>8));
997
15.3k
    val=uint16_t((val<<1)|(val>>15));
998
15.3k
    val=uint16_t((val<<8)|(val>>8));
999
15.3k
    key=uint16_t((key<<8)|(key>>8));
1000
15.3k
    key^=val;
1001
1002
15.3k
    val=uint16_t((((val>>4)&0xfff)|(val<<12))&0xe0ff);
1003
15.3k
    key^=val;
1004
15.3k
    val=uint16_t((val>>1)|(val<<15));
1005
15.3k
    key=uint16_t(key^(val>>8));
1006
15.3k
  }
1007
1008
3.44k
  size_t cPos;
1009
3.44k
  keys.resize(len);
1010
  // copy password in keys and fill the remaining space with
1011
  // defValues
1012
18.8k
  for (cPos=0 ; cPos<len; ++cPos)
1013
18.8k
  {
1014
18.8k
    if (password[cPos]==0)
1015
3.44k
      break;
1016
15.3k
    keys[cPos]=uint8_t(password[cPos]);
1017
15.3k
  }
1018
3.44k
  uint8_t const *defPtr=defValues;
1019
43.1k
  for (; cPos<len; ++cPos)
1020
39.7k
    keys[cPos] = *(defPtr++);
1021
  // now do an xor to code the result
1022
58.5k
  for (size_t i=0; i<len; ++i)
1023
55.1k
    keys[i]=uint8_t(keys[i]^(key>>((i%2)==0 ? 8 : 0)));
1024
3.44k
  return true;
1025
3.44k
}
1026
}
1027
////////////////////////////////////////////////////////////
1028
// debug
1029
////////////////////////////////////////////////////////////
1030
1031
namespace libwps
1032
{
1033
#ifdef DEBUG
1034
void printDebugMsg(const char *format, ...)
1035
{
1036
  va_list args;
1037
  va_start(args, format);
1038
  std::vfprintf(stderr, format, args);
1039
  va_end(args);
1040
}
1041
#endif
1042
}
1043
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */