Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libetonyek/src/lib/IWAParser.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/*
3
 * This file is part of the libetonyek 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
10
#include "IWAParser.h"
11
12
#include <algorithm>
13
#include <cassert>
14
#include <functional>
15
#include <iomanip>
16
#include <map>
17
#include <memory>
18
#include <sstream>
19
#include <utility>
20
21
#include <boost/optional.hpp>
22
23
#include "libetonyek_xml.h"
24
#include "IWAObjectType.h"
25
#include "IWAText.h"
26
#include "IWORKCollector.h"
27
#include "IWORKFormula.h"
28
#include "IWORKNumberConverter.h"
29
#include "IWORKPath.h"
30
#include "IWORKProperties.h"
31
#include "IWORKTable.h"
32
#include "IWORKText.h"
33
#include "IWORKTransformation.h"
34
#include "IWORKTypes.h"
35
36
#include "PAGCollector.h"
37
38
namespace libetonyek
39
{
40
41
using boost::none;
42
using boost::optional;
43
44
using namespace std::placeholders;
45
46
using std::bind;
47
using std::deque;
48
using std::make_pair;
49
using std::make_shared;
50
using std::map;
51
using std::shared_ptr;
52
using std::string;
53
54
namespace
55
{
56
bool samePoint(const optional<IWORKPosition> &point1, const optional<IWORKPosition> &point2)
57
92
{
58
92
  if (point1 && point2)
59
92
    return approxEqual(get(point1).m_x, get(point2).m_x) && approxEqual(get(point1).m_y, get(point2).m_y);
60
0
  return true;
61
92
}
62
63
template<typename T>
64
optional<T> convert(const unsigned value)
65
{
66
  return IWORKNumberConverter<T>::convert(value);
67
}
68
69
template<typename P>
70
void putEnum(IWORKPropertyMap &props, const unsigned value)
71
5.74k
{
72
5.74k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
5.74k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
5.74k
  if (converted)
75
2.94k
    props.put<P>(get(converted));
76
5.74k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Alignment>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.51k
{
72
1.51k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.51k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.51k
  if (converted)
75
1.51k
    props.put<P>(get(converted));
76
1.51k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::ParagraphBorderType>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.40k
{
72
1.40k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.40k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.40k
  if (converted)
75
22
    props.put<P>(get(converted));
76
1.40k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Baseline>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.42k
{
72
1.42k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.42k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.42k
  if (converted)
75
25
    props.put<P>(get(converted));
76
1.42k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Capitalization>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.40k
{
72
1.40k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.40k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.40k
  if (converted)
75
1.37k
    props.put<P>(get(converted));
76
1.40k
}
77
78
bool parseColumnOffsets(const RVNGInputStreamPtr_t &input, const unsigned length, map<unsigned,unsigned> &offsets, unsigned factor=1)
79
247
{
80
247
  try
81
247
  {
82
247
    unsigned col=0;
83
62.9k
    while (!input->isEnd())
84
62.7k
    {
85
62.7k
      const unsigned offset = readU16(input);
86
62.7k
      if (factor*offset<length && factor*offset+4 < length)
87
757
        offsets[col]=factor*offset;
88
61.9k
      else if (offset!=0xffff)
89
22
      {
90
22
        if (col==0 && offset==0x9ff0) // the content: f09fa4a0 seems to mean undef
91
0
          return false;
92
22
        ETONYEK_DEBUG_MSG(("parseColumnOffsets[IWAParser]: find %x>%x\n", offset, length));
93
22
      }
94
62.7k
      ++col;
95
62.7k
    }
96
247
  }
97
247
  catch (...)
98
247
  {
99
    // ignore failure to read the last word: it can only happen in a broken file
100
1
    return false;
101
1
  }
102
246
  return true;
103
247
}
104
105
deque<IWORKColumnRowSize> makeSizes(const mdds::flat_segment_tree<unsigned, float> &sizes)
106
344
{
107
344
  IWORKColumnRowSize defVal;
108
344
  if (sizes.default_value()>0) defVal=IWORKColumnRowSize(sizes.default_value(),false);
109
344
  deque<IWORKColumnRowSize> out(sizes.max_key(), IWORKColumnRowSize());
110
1.15k
  for (mdds::flat_segment_tree<unsigned, float>::const_iterator it = sizes.begin(); it != sizes.end();)
111
812
  {
112
812
    const deque<IWORKColumnRowSize>::iterator start(out.begin() + deque<double>::iterator::difference_type(it->first));
113
812
    const double size = it->second;
114
812
    ++it;
115
812
    const deque<IWORKColumnRowSize>::iterator end(it == sizes.end() ? out.end() : out.begin() +  deque<double>::iterator::difference_type(it->first));
116
812
    std::fill(start, end, size>0 ? IWORKColumnRowSize(size) : defVal);
117
812
  }
118
344
  return out;
119
344
}
120
121
}
122
123
IWAParser::Format::Format()
124
103
  : m_type()
125
103
  , m_format()
126
103
{
127
103
}
128
129
IWAParser::PageMaster::PageMaster()
130
0
  : m_style()
131
0
  , m_headerFootersSameAsPrevious(true)
132
0
{
133
0
}
134
135
IWAParser::TableHeader::TableHeader(const unsigned count, float defValue)
136
390
  : m_sizes(0, count, defValue)
137
390
  , m_hidden(0, count, false)
138
390
{
139
390
}
140
141
IWAParser::ConditionRule::ConditionRule()
142
0
  : m_formula()
143
0
  , m_cellStyleRef()
144
0
  , m_paragraphStyleRef()
145
0
{
146
0
}
147
148
IWAParser::TableInfo::TableInfo(const shared_ptr<IWORKTable> &table, const unsigned columns, const unsigned rows)
149
195
  : m_table(table)
150
195
  , m_columns(columns)
151
195
  , m_rows(rows)
152
195
  , m_style()
153
195
  , m_columnHeader(columns)
154
195
  , m_rowHeader(rows,20)
155
195
  , m_simpleTextList()
156
195
  , m_cellStyleList()
157
195
  , m_conditionStyleList()
158
195
  , m_formattedTextList()
159
195
  , m_formulaList()
160
195
  , m_formatList()
161
195
  , m_newFormatList()
162
195
  , m_commentList()
163
195
{
164
195
}
165
166
IWAParser::IWAParser(const RVNGInputStreamPtr_t &fragments, const RVNGInputStreamPtr_t &package, IWORKCollector &collector)
167
2.49k
  : m_formatNameMap()
168
2.49k
  , m_langManager()
169
2.49k
  , m_tableNameMap(std::make_shared<IWORKTableNameMap_t>())
170
2.49k
  , m_currentText()
171
2.49k
  , m_collector(collector)
172
2.49k
  , m_index(fragments, package)
173
2.49k
  , m_visited()
174
2.49k
  , m_charStyles()
175
2.49k
  , m_dropCapStyles()
176
2.49k
  , m_paraStyles()
177
2.49k
  , m_sectionStyles()
178
2.49k
  , m_graphicStyles()
179
2.49k
  , m_mediaStyles()
180
2.49k
  , m_cellStyles()
181
2.49k
  , m_tableStyles()
182
2.49k
  , m_listStyles()
183
2.49k
  , m_currentTable()
184
2.49k
  , m_uidFormatMap()
185
2.49k
{
186
2.49k
}
187
188
bool IWAParser::parse()
189
2.49k
{
190
2.49k
  parseObjectIndex();
191
2.49k
  return parseDocument();
192
2.49k
}
193
194
IWAParser::ObjectMessage::ObjectMessage(IWAParser &parser, const unsigned id, const unsigned type)
195
90.5k
  : m_parser(parser)
196
90.5k
  , m_message()
197
90.5k
  , m_id(id)
198
90.5k
  , m_type(0)
199
90.5k
{
200
90.5k
  std::deque<unsigned>::const_iterator it = find(m_parser.m_visited.begin(), m_parser.m_visited.end(), m_id);
201
90.5k
  if (it == m_parser.m_visited.end())
202
89.5k
  {
203
89.5k
    optional<IWAMessage> msg;
204
89.5k
    m_parser.queryObject(m_id, m_type, msg);
205
89.5k
    if (msg)
206
73.4k
    {
207
73.4k
      if ((m_type == type) || (type == 0))
208
73.2k
      {
209
73.2k
        m_message = msg;
210
73.2k
        m_parser.m_visited.push_back(m_id);
211
73.2k
      }
212
247
      else
213
247
      {
214
247
        ETONYEK_DEBUG_MSG(("IWAParser::ObjectMessage::ObjectMessage: type mismatch for object %u: expected %u, got %u\n", id, type, m_type));
215
247
      }
216
73.4k
    }
217
89.5k
  }
218
1.00k
  else
219
1.00k
  {
220
1.00k
    ETONYEK_DEBUG_MSG(("IWAParser::ObjectMessage::ObjectMessage: object %u is actually visited\n", id));
221
1.00k
  }
222
90.5k
}
223
224
IWAParser::ObjectMessage::~ObjectMessage()
225
90.3k
{
226
90.3k
  if (m_message)
227
73.2k
  {
228
73.2k
    assert(!m_parser.m_visited.empty());
229
73.2k
    assert(m_parser.m_visited.back() == m_id);
230
73.2k
    m_parser.m_visited.pop_back();
231
73.2k
  }
232
90.3k
}
233
234
IWAParser::ObjectMessage::operator bool() const
235
90.3k
{
236
90.3k
  return bool(m_message);
237
90.3k
}
238
239
const IWAMessage &IWAParser::ObjectMessage::get() const
240
292k
{
241
292k
  return m_message.get();
242
292k
}
243
244
unsigned IWAParser::ObjectMessage::getType() const
245
53.8k
{
246
53.8k
  return m_type;
247
53.8k
}
248
249
void IWAParser::queryObject(const unsigned id, unsigned &type, boost::optional<IWAMessage> &msg) const
250
89.5k
{
251
89.5k
  m_index.queryObject(id, type, msg);
252
89.5k
}
253
254
boost::optional<unsigned> IWAParser::getObjectType(const unsigned id) const
255
0
{
256
0
  return m_index.getObjectType(id);
257
0
}
258
259
const RVNGInputStreamPtr_t IWAParser::queryFile(const unsigned id) const
260
1.27k
{
261
1.27k
  return m_index.queryFile(id);
262
1.27k
}
263
264
boost::optional<unsigned> IWAParser::readRef(const IWAMessage &msg, const unsigned field)
265
110k
{
266
110k
  if (msg.message(field))
267
87.0k
    return msg.message(field).uint32(1).optional();
268
23.6k
  return boost::none;
269
110k
}
270
271
std::deque<unsigned> IWAParser::readRefs(const IWAMessage &msg, const unsigned field)
272
19.4k
{
273
19.4k
  std::deque<unsigned> refs;
274
19.4k
  if (msg.message(field))
275
14.1k
  {
276
14.1k
    const std::deque<IWAMessage> &objs = msg.message(field).repeated();
277
14.1k
    for (const auto &obj : objs)
278
29.9k
    {
279
29.9k
      if (obj.uint32(1))
280
29.2k
        refs.push_back(obj.uint32(1).get());
281
29.9k
    }
282
14.1k
  }
283
19.4k
  return refs;
284
19.4k
}
285
286
boost::optional<IWORKPosition> IWAParser::readPosition(const IWAMessage &msg, const unsigned field)
287
30.0k
{
288
30.0k
  if (msg.message(field))
289
29.0k
  {
290
29.0k
    const optional<float> &x = msg.message(field).float_(1).optional();
291
29.0k
    const optional<float> &y = msg.message(field).float_(2).optional();
292
29.0k
    return IWORKPosition(get_optional_value_or(x, 0), get_optional_value_or(y, 0));
293
29.0k
  }
294
978
  return boost::none;
295
30.0k
}
296
297
boost::optional<IWORKSize> IWAParser::readSize(const IWAMessage &msg, const unsigned field)
298
20.6k
{
299
20.6k
  if (msg.message(field))
300
19.6k
  {
301
19.6k
    const optional<float> &w = msg.message(field).float_(1).optional();
302
19.6k
    const optional<float> &h = msg.message(field).float_(2).optional();
303
19.6k
    return IWORKSize(get_optional_value_or(w, 0), get_optional_value_or(h, 0));
304
19.6k
  }
305
1.01k
  return boost::none;
306
20.6k
}
307
308
boost::optional<IWORKColor> IWAParser::readColor(const IWAMessage &msg, const unsigned field)
309
16.2k
{
310
16.2k
  const IWAMessageField &color = msg.message(field);
311
16.2k
  if (color)
312
10.3k
  {
313
10.3k
    if (color.float_(3) && color.float_(4) && color.float_(5))
314
10.0k
      return IWORKColor(get(color.float_(3)), get(color.float_(4)), get(color.float_(5)), get_optional_value_or(color.float_(6), 0));
315
10.3k
  }
316
6.19k
  return boost::none;
317
16.2k
}
318
319
boost::optional<uint64_t> IWAParser::readUID(const IWAMessage &msg, unsigned field)
320
102
{
321
102
  const IWAMessageField &id = msg.message(field);
322
102
  if (!id) return boost::none;
323
0
  if (id && get(id).uint32(1) && get(id).uint32(2))
324
0
    return (uint64_t(get(get(id).uint32(1)))<<32) | get(get(id).uint32(2));
325
0
  ETONYEK_DEBUG_MSG(("IWAParser::readUID: can not find the id zone\n"));
326
0
  return boost::none;
327
0
}
328
329
std::deque<uint64_t> IWAParser::readUIDs(const IWAMessage &msg, unsigned field)
330
0
{
331
0
  const std::deque<IWAMessage> &objs = msg.message(field).repeated();
332
0
  std::deque<uint64_t> res;
333
0
  for (const auto &obj : objs)
334
0
  {
335
0
    if (obj.uint32(1) && obj.uint32(2))
336
0
      res.push_back((uint64_t(get(obj.uint32(1)))<<32) | get(obj.uint32(2)));
337
0
  }
338
0
  return res;
339
0
}
340
341
boost::optional<std::string> IWAParser::readUUID(const IWAMessage &msg, const unsigned field)
342
0
{
343
0
  const IWAMessageField &mId = msg.message(field);
344
0
  if (!mId) return boost::none;
345
0
  auto const &id = get(mId).message(1);
346
0
  if (id && get(id).uint32(2) && get(id).uint32(3) && get(id).uint32(4) && get(id).uint32(5))
347
0
  {
348
0
    std::string res;
349
0
    bool hasValues=false;
350
0
    for (unsigned w=2; w<=5; ++w)
351
0
    {
352
0
      std::stringstream s;
353
0
      auto val=get(get(id).uint32(w));
354
0
      if (val) hasValues=true;
355
0
      std::uppercase(s);
356
0
      s << std::hex << std::setfill('0') << std::setw(8) << val;
357
0
      if (s.str().size()!=8)
358
0
      {
359
0
        ETONYEK_DEBUG_MSG(("IWAParser::readUUID: bad size\n"));
360
0
        return boost::none;
361
0
      }
362
0
      for (size_t c=0; c<4; ++c)
363
0
      {
364
0
        if ((w==3 || w==4) && (c%2)==0) res+='-';
365
0
        res+=s.str()[6-2*c];
366
0
        res+=s.str()[7-2*c];
367
0
      }
368
0
    }
369
0
    if (!hasValues) return none;
370
0
    return res;
371
0
  }
372
0
  ETONYEK_DEBUG_MSG(("IWAParser::readUUID: can not find the id zone\n"));
373
0
  return boost::none;
374
0
}
375
376
void IWAParser::readStroke(const IWAMessage &msg, IWORKStroke &stroke)
377
1.23k
{
378
1.23k
  const optional<IWORKColor> &color = readColor(msg, 1);
379
1.23k
  if (color)
380
1.19k
    stroke.m_color = get(color);
381
1.23k
  stroke.m_width = get(msg.float_(2));
382
1.23k
  if (msg.uint32(3))
383
1.14k
  {
384
1.14k
    switch (get(msg.uint32(3)))
385
1.14k
    {
386
0
    default :
387
0
      ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown cap value: %u", get(msg.uint32(3))));
388
0
      ETONYEK_FALLTHROUGH;
389
881
    case 0 :
390
881
      stroke.m_cap = IWORK_LINE_CAP_BUTT;
391
881
      break;
392
262
    case 1 :
393
262
      stroke.m_cap = IWORK_LINE_CAP_ROUND;
394
262
      break;
395
1.14k
    }
396
1.14k
  }
397
1.23k
  if (msg.uint32(4))
398
1.16k
  {
399
1.16k
    switch (get(msg.uint32(4)))
400
1.16k
    {
401
1
    default :
402
1
      ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown join value: %u", get(msg.uint32(4))));
403
1
      ETONYEK_FALLTHROUGH;
404
909
    case 0 :
405
909
      stroke.m_join = IWORK_LINE_JOIN_MITER;
406
909
      break;
407
253
    case 1 :
408
253
      stroke.m_join = IWORK_LINE_JOIN_ROUND;
409
253
      break;
410
1.16k
    }
411
1.16k
  }
412
1.23k
  if (msg.message(6))
413
1.11k
  {
414
1.11k
    stroke.m_pattern.m_type = IWORK_STROKE_TYPE_SOLID;
415
1.11k
    if (msg.message(6).uint32(1))
416
1.11k
    {
417
1.11k
      switch (get(msg.message(6).uint32(1)))
418
1.11k
      {
419
58
      default :
420
58
        ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown stroke value: %u", get(msg.message(6).uint32(1))));
421
58
        ETONYEK_FALLTHROUGH;
422
301
      case 1:
423
301
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_SOLID;
424
301
        break;
425
406
      case 0:
426
406
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_DASHED;
427
406
        break;
428
408
      case 2:
429
408
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_NONE;
430
408
        break;
431
1.11k
      }
432
1.11k
    }
433
1.11k
    unsigned remaining = 0;
434
1.11k
    if (msg.message(6).uint32(3))
435
966
      remaining = get(msg.message(6).uint32(3));
436
1.11k
    const deque<float> &elements = msg.message(6).float_(4).repeated();
437
3.04k
    for (auto it = elements.begin(); it != elements.end() && remaining != 0; ++it)
438
1.92k
      stroke.m_pattern.m_values.push_back(*it);
439
1.11k
  }
440
  // todo: check also if there is a picture frame msg.message(8), if yes, use it as border
441
1.23k
}
442
443
bool IWAParser::readFill(const IWAMessage &msg, IWORKFill &fill)
444
2.14k
{
445
2.14k
  const optional<IWORKColor> &color = readColor(msg, 1);
446
2.14k
  if (color)
447
913
  {
448
913
    fill = get(color);
449
913
    return true;
450
913
  }
451
1.22k
  else if (msg.message(2))
452
170
  {
453
170
    IWORKGradient gradient;
454
170
    readGradient(get(msg.message(2)), gradient);
455
170
    fill = gradient;
456
170
    return true;
457
170
  }
458
1.05k
  else if (msg.message(3))
459
355
  {
460
355
    IWORKMediaContent bitmap;
461
355
    if (msg.message(3).uint32(2))
462
353
    {
463
353
      switch (get(msg.message(3).uint32(2)))
464
353
      {
465
0
      default :
466
0
        ETONYEK_DEBUG_MSG(("IWAParser::readFill: unknown bitmap fill type: %u", get(msg.message(3).uint32(2))));
467
0
        ETONYEK_FALLTHROUGH;
468
61
      case 0 :
469
61
        bitmap.m_type = IWORK_IMAGE_TYPE_ORIGINAL_SIZE;
470
61
        break;
471
29
      case 1 :
472
29
        bitmap.m_type = IWORK_IMAGE_TYPE_STRETCH;
473
29
        break;
474
218
      case 2 :
475
218
        bitmap.m_type = IWORK_IMAGE_TYPE_TILE;
476
218
        break;
477
23
      case 3 :
478
23
        bitmap.m_type = IWORK_IMAGE_TYPE_SCALE_TO_FILL;
479
23
        break;
480
22
      case 4 :
481
22
        bitmap.m_type = IWORK_IMAGE_TYPE_SCALE_TO_FIT;
482
22
        break;
483
353
      }
484
353
    }
485
355
    bitmap.m_fillColor = readColor(get(msg.message(3)), 3);
486
355
    if (!bitmap.m_fillColor) // a least in new KeyNote files, the field 9 can also store a color
487
249
      bitmap.m_fillColor = readColor(get(msg.message(3)), 9);
488
355
    bitmap.m_size = readSize(get(msg.message(3)), 4);
489
355
    if (!bitmap.m_size) bitmap.m_size=IWORKSize(); // to do not change result from previous code
490
355
    const optional<unsigned> &fileRef = readRef(get(msg.message(3)), 6);
491
355
    if (fileRef)
492
332
    {
493
      // find also 16 with no file...
494
332
      bitmap.m_data = std::make_shared<IWORKData>();
495
332
      bitmap.m_data->m_stream = queryFile(get(fileRef));
496
332
      if (!bitmap.m_data->m_stream && !bitmap.m_fillColor) bitmap.m_fillColor = m_index.queryFileColor(get(fileRef));
497
332
    }
498
355
    fill = bitmap;
499
355
    return true;
500
355
  }
501
702
  return false;
502
2.14k
}
503
504
void IWAParser::readGradient(const IWAMessage &msg, IWORKGradient &gradient)
505
170
{
506
170
  if (msg.uint32(1))
507
161
  {
508
161
    switch (get(msg.uint32(1)))
509
161
    {
510
0
    default :
511
0
      ETONYEK_DEBUG_MSG(("IWAParser::readGradient: unknown gradient type: %u", get(msg.uint32(1))));
512
0
      ETONYEK_FALLTHROUGH;
513
107
    case 0 :
514
107
      gradient.m_type = IWORK_GRADIENT_TYPE_LINEAR;
515
107
      break;
516
54
    case 1 :
517
54
      gradient.m_type = IWORK_GRADIENT_TYPE_RADIAL;
518
54
      break;
519
161
    }
520
161
  }
521
170
  for (const auto &it : msg.message(2))
522
326
  {
523
326
    IWORKGradientStop stop;
524
326
    const optional<IWORKColor> &color = readColor(it, 1);
525
326
    if (color)
526
287
      stop.m_color = get(color);
527
326
    if (it.float_(2))
528
266
      stop.m_fraction = get(it.float_(2));
529
326
    if (it.float_(3))
530
281
      stop.m_inflection = get(it.float_(3));
531
326
    gradient.m_stops.push_back(stop);
532
326
  }
533
170
  if (msg.message(5) && msg.message(5).float_(2))
534
92
    gradient.m_angle = get(msg.message(5).float_(2));
535
170
}
536
537
void IWAParser::readShadow(const IWAMessage &msg, IWORKShadow &shadow)
538
1.12k
{
539
1.12k
  const optional<IWORKColor> &color = readColor(msg, 1);
540
1.12k
  if (color)
541
1.07k
    shadow.m_color = get(color);
542
1.12k
  if (msg.float_(2))
543
1.09k
    shadow.m_angle = get(msg.float_(2));
544
1.12k
  if (msg.float_(3))
545
1.09k
    shadow.m_offset = get(msg.float_(3));
546
  // 4. blur
547
1.12k
  if (msg.float_(5))
548
1.07k
    shadow.m_opacity = get(msg.float_(5));
549
  // 6: bool true
550
1.12k
  if (msg.bool_(6))
551
1.06k
    shadow.m_visible = get(msg.bool_(6));
552
  // 7: type enum 0: drop,
553
1.12k
}
554
555
void IWAParser::readPadding(const IWAMessage &msg, IWORKPadding &padding)
556
885
{
557
885
  padding.m_left = msg.float_(1).optional();
558
885
  padding.m_top = msg.float_(2).optional();
559
885
  padding.m_right = msg.float_(3).optional();
560
885
  padding.m_bottom = msg.float_(4).optional();
561
885
}
562
563
void IWAParser::readDropCap(const IWAMessage &msg, IWORKDropCap &cap)
564
0
{
565
0
  using namespace property;
566
0
  if (msg.message(1))
567
0
  {
568
0
    auto const &capMsg=get(msg.message(1));
569
570
0
    if (capMsg.uint32(2))
571
0
      cap.m_numLines=get(capMsg.uint32(2));
572
0
    if (capMsg.uint32(3))
573
0
      cap.m_numLinesSpan=get(capMsg.uint32(3));
574
0
    if (capMsg.uint32(10))
575
0
      cap.m_numCharacters=get(capMsg.uint32(10));
576
0
    if (capMsg.double_(11))
577
0
      cap.m_decalParagraphLeft=get(capMsg.double_(11));
578
0
    if (capMsg.double_(12))
579
0
      cap.m_supplementalSpace=get(capMsg.double_(12));
580
0
  }
581
0
}
582
583
bool IWAParser::dispatchShape(const unsigned id)
584
15.7k
{
585
15.7k
  const ObjectMessage msg(*this, id);
586
15.7k
  if (!msg)
587
1.48k
    return false;
588
14.2k
  return dispatchShapeWithMessage(get(msg), msg.getType());
589
15.7k
}
590
591
bool IWAParser::dispatchShapeWithMessage(const IWAMessage &msg, unsigned type)
592
14.2k
{
593
14.2k
  switch (type)
594
14.2k
  {
595
12
  case IWAObjectType::ConnectionLine :
596
6.36k
  case IWAObjectType::DrawableShape :
597
6.36k
    return parseDrawableShape(msg, type==IWAObjectType::ConnectionLine);
598
151
  case IWAObjectType::Group :
599
151
    return parseGroup(msg);
600
533
  case IWAObjectType::Image :
601
533
    return parseImage(msg);
602
2.41k
  case IWAObjectType::StickyNote:
603
2.41k
    return parseStickyNote(msg);
604
203
  case IWAObjectType::TabularInfo :
605
203
    return parseTabularInfo(msg);
606
4.57k
  default:
607
4.57k
  {
608
4.57k
    static bool first=true;
609
4.57k
    if (first)
610
1
    {
611
1
      first=false;
612
1
      ETONYEK_DEBUG_MSG(("IWAParser::dispatchShape: find some unknown shapes, type=%d\n", int(type)));
613
1
    }
614
4.57k
  }
615
14.2k
  }
616
617
4.57k
  return false;
618
14.2k
}
619
620
void IWAParser::updateGeometryUsingTextRef(unsigned id, IWORKGeometry &geometry, unsigned flags)
621
860
{
622
  // no horizontal auto resize or width unknown
623
860
  if ((flags&1)==1 || geometry.m_size.m_width<=0) return;
624
735
  const ObjectMessage msg(*this, id);
625
735
  if (!msg)
626
44
    return;
627
691
  if (msg.getType()==IWAObjectType::TextRef)
628
0
  {
629
0
    auto textRef=readRef(get(msg),1);
630
0
    if (!textRef)
631
0
    {
632
0
      ETONYEK_DEBUG_MSG(("IWAParser::updateGeometryUsingTextRef: can not find the text reference\n"));
633
0
      return;
634
0
    }
635
0
    updateGeometryUsingTextRef(get(textRef),geometry, flags);
636
0
    return;
637
0
  }
638
691
  if (msg.getType()!=IWAObjectType::Text)
639
48
  {
640
48
    ETONYEK_DEBUG_MSG(("IWAParser::updateGeometryUsingTextRef: unexpected object type, type=%d\n", int(msg.getType())));
641
48
    return;
642
48
  }
643
643
  if (!get(msg).message(5))
644
102
    return;
645
  // ok, let find the paragraph style for pos=0
646
541
  for (const auto &it : get(msg).message(5).message(1))
647
611
  {
648
611
    if (it.uint32(1) && get(it.uint32(1))!=0) continue;
649
430
    const optional<unsigned> &styleRef = readRef(it, 2);
650
430
    if (!styleRef) return;
651
652
346
    const IWORKStylePtr_t &style = queryParagraphStyle(get(styleRef));
653
346
    if (!bool(style)) return;
654
54
    if (geometry.m_size.m_width>0 && style->has<property::Alignment>())
655
38
    {
656
38
      switch (style->get<property::Alignment>())
657
38
      {
658
5
      case IWORK_ALIGNMENT_RIGHT :
659
5
        geometry.m_position.m_x -= geometry.m_size.m_width;
660
5
        break;
661
26
      case IWORK_ALIGNMENT_CENTER :
662
26
        geometry.m_position.m_x -= geometry.m_size.m_width/2.;
663
26
        break;
664
7
      case IWORK_ALIGNMENT_LEFT :
665
7
      case IWORK_ALIGNMENT_JUSTIFY :
666
7
      case IWORK_ALIGNMENT_AUTOMATIC:
667
7
      default:
668
7
        break;
669
38
      }
670
38
    }
671
54
  }
672
541
}
673
674
bool IWAParser::parseText(const unsigned id, bool createNoteAsFootnote, const std::function<void(unsigned, IWORKStylePtr_t)> &openPageFunction)
675
19.6k
{
676
19.6k
  assert(bool(m_currentText));
677
19.6k
  const ObjectMessage msg(*this, id);
678
19.6k
  if (!msg)
679
634
    return false;
680
19.0k
  if (msg.getType()==IWAObjectType::TextRef)
681
0
  {
682
0
    auto textRef=readRef(get(msg),1);
683
0
    if (!textRef)
684
0
    {
685
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseText: can not find the text reference\n"));
686
0
      return false;
687
0
    }
688
0
    return parseText(get(textRef),createNoteAsFootnote,openPageFunction);
689
0
  }
690
19.0k
  if (msg.getType()!=IWAObjectType::Text)
691
89
  {
692
89
    ETONYEK_DEBUG_MSG(("IWAParser::parseText: unexpected object type, type=%d\n", int(msg.getType())));
693
89
    return false;
694
89
  }
695
18.9k
  std::multimap<unsigned, std::function<void(unsigned, bool &)> > attachments;
696
18.9k
  const IWAStringField &text = get(msg).string(3);
697
  // fixme: in Numbers, if text does not exists, the default text string is still displayed
698
18.9k
  if (text || (openPageFunction && get(msg).message(17)))
699
6.35k
  {
700
    // special case, when the document is empty and openPageFunction is
701
    //          defined, we still need to retrieve the headers/footers
702
6.35k
    IWAText textParser(get_optional_value_or(text," "), m_langManager);
703
6.35k
    const size_t length = text ? get(text).size() : 1;
704
705
6.35k
    if (get(msg).message(5))
706
5.67k
    {
707
5.67k
      map<unsigned, IWORKStylePtr_t> paras;
708
5.67k
      IWORKStylePtr_t style = make_shared<IWORKStyle>(IWORKPropertyMap(), none, none);
709
5.67k
      for (const auto &it : get(msg).message(5).message(1))
710
12.2k
      {
711
12.2k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
712
11.4k
        {
713
11.4k
          const optional<unsigned> &styleRef = readRef(it, 2);
714
11.4k
          if (styleRef)
715
5.21k
          {
716
5.21k
            const IWORKStylePtr_t &newStyle = queryParagraphStyle(get(styleRef));
717
5.21k
            if (bool(newStyle))
718
1.47k
              style = newStyle;
719
5.21k
          }
720
11.4k
          paras.insert(paras.end(), make_pair(get(it.uint32(1)), style));
721
11.4k
        }
722
12.2k
      }
723
5.67k
      textParser.setParagraphs(paras);
724
5.67k
    }
725
726
6.35k
    if (get(msg).message(6))
727
5.88k
    {
728
5.88k
      map<unsigned, unsigned> levels;
729
5.88k
      for (const auto &it : get(msg).message(6).message(1))
730
11.7k
      {
731
11.7k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
732
10.4k
          levels.insert(levels.end(), make_pair(get(it.uint32(1)), get_optional_value_or(it.uint32(2), 0)));
733
11.7k
      }
734
5.88k
      textParser.setListLevels(levels);
735
5.88k
    }
736
737
6.35k
    if (get(msg).message(7))
738
5.43k
    {
739
5.43k
      map<unsigned, IWORKStylePtr_t> lists;
740
5.43k
      for (const auto &it : get(msg).message(7).message(1))
741
6.19k
      {
742
6.19k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
743
5.09k
        {
744
5.09k
          IWORKStylePtr_t style;
745
5.09k
          const optional<unsigned> &styleRef = readRef(it, 2);
746
5.09k
          if (styleRef)
747
4.75k
            style = queryListStyle(get(styleRef));
748
5.09k
          lists.insert(lists.end(), make_pair(get(it.uint32(1)), style));
749
5.09k
        }
750
6.19k
      }
751
5.43k
      textParser.setLists(lists);
752
5.43k
    }
753
754
6.35k
    if (get(msg).message(8))
755
257
    {
756
257
      map<unsigned, IWORKStylePtr_t> spans;
757
257
      for (const auto &it : get(msg).message(8).message(1))
758
639
      {
759
639
        if (it.uint32(1) && (get(it.uint32(1)) < length))
760
466
        {
761
466
          IWORKStylePtr_t style;
762
466
          const optional<unsigned> &styleRef = readRef(it, 2);
763
466
          if (styleRef)
764
240
            style = queryCharacterStyle(get(styleRef));
765
466
          spans.insert(spans.end(), make_pair(get(it.uint32(1)), style));
766
466
        }
767
639
      }
768
257
      textParser.setSpans(spans);
769
257
    }
770
6.35k
    if (get(msg).message(9))
771
414
    {
772
414
      map<unsigned, IWORKFieldType> fields;
773
414
      for (const auto &it : get(msg).message(9).message(1))
774
635
      {
775
635
        if (!it.uint32(1))
776
48
        {
777
48
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: can not find the position\n"));
778
48
          continue;
779
48
        }
780
587
        const optional<unsigned> &ref = readRef(it, 2);
781
587
        if (!ref) continue;
782
338
        const ObjectMessage attachment(*this, get(ref));
783
338
        if (!attachment) continue;
784
228
        switch (attachment.getType())
785
228
        {
786
10
        case IWAObjectType::NoteStart: // the first character of a note seems special, so ...
787
10
          attachments.insert(make_pair(get(it.uint32(1)), [](unsigned, bool &ignore)
788
10
          {
789
6
            ignore=true;
790
6
          }));
791
10
          continue;
792
13
        case IWAObjectType::PageField:
793
13
        {
794
13
          if (!get(attachment).message(1)) break;
795
10
          const auto &field=get(get(attachment).message(1));
796
10
          if (field.uint32(2))
797
6
          {
798
6
            switch (get(field.uint32(2)))
799
6
            {
800
3
            case 0:
801
3
              attachments.insert(make_pair(get(it.uint32(1)),
802
3
                                           [this](unsigned, bool &ignore)
803
3
              {
804
3
                ignore=true;
805
3
                m_currentText->insertField(IWORKFieldType::IWORK_FIELD_PAGENUMBER);
806
3
              }));
807
3
              break;
808
0
            case 1:
809
0
              attachments.insert(make_pair(get(it.uint32(1)),
810
0
                                           [this](unsigned, bool &ignore)
811
0
              {
812
0
                ignore=true;
813
0
                m_currentText->insertField(IWORKFieldType::IWORK_FIELD_PAGECOUNT);
814
0
              }));
815
0
              break;
816
3
            default:
817
3
              ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: unknown field enum=%d\n", int(get(field.uint32(2)))));
818
6
            }
819
6
            continue;
820
6
          }
821
4
          break;
822
10
        }
823
4
        case IWAObjectType::ShapeField:
824
1
          if (!openPageFunction)
825
1
          {
826
1
            ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: find unexpected shape's attachment at pos=%d\n", int(get(it.uint32(1)))));
827
1
          }
828
0
          else
829
0
            attachments.insert(make_pair(get(it.uint32(1)),
830
0
                                         [this,ref](unsigned, bool &ignore)
831
0
          {
832
0
            ignore=true;
833
0
            parseAttachment(get(ref));
834
0
          }));
835
1
          continue;
836
201
        default:
837
201
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: find unknown object %d at position=%d\n", int(attachment.getType()), int(get(it.uint32(1)))));
838
201
          continue;
839
228
        }
840
6
        ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: can not read the object at position=%d\n", int(get(it.uint32(1)))));
841
6
      }
842
414
    }
843
6.35k
    if (get(msg).message(11))
844
273
    {
845
      // placeholder:2031 or link:2032 or time field:2034
846
273
      map<unsigned, string> links;
847
273
      for (const auto &it : get(msg).message(11).message(1))
848
711
      {
849
711
        if (it.uint32(1))
850
598
        {
851
598
          string url;
852
598
          const optional<unsigned> &linkRef = readRef(it, 2);
853
598
          if (linkRef)
854
53
            parseLink(get(linkRef), url);
855
598
          links.insert(links.end(), make_pair(get(it.uint32(1)), url));
856
598
        }
857
711
      }
858
273
      textParser.setLinks(links);
859
273
    }
860
6.35k
    if (openPageFunction && get(msg).message(12))
861
0
    {
862
0
      map<unsigned, IWORKStylePtr_t> sections;
863
0
      for (const auto &it : get(msg).message(12).message(1))
864
0
      {
865
0
        if (!it.uint32(1)) continue;
866
0
        const optional<unsigned> &sectionRef = readRef(it, 2);
867
0
        if (!sectionRef) continue;
868
0
        const IWORKStylePtr_t &sectionStyle = querySectionStyle(get(sectionRef));
869
0
        if (sectionStyle)
870
0
          sections.insert(sections.end(), make_pair(get(it.uint32(1)), sectionStyle));
871
0
      }
872
0
      textParser.setSections(sections);
873
0
    }
874
6.35k
    if (get(msg).message(16))
875
57
    {
876
57
      for (const auto &it : get(msg).message(16).message(1))
877
86
      {
878
86
        if (!it.uint32(1)) continue;
879
24
        const optional<unsigned> &noteRef = readRef(it, 2);
880
24
        if (!noteRef) continue;
881
2
        const ObjectMessage noteMsg(*this, get(noteRef), IWAObjectType::Note);
882
2
        if (!noteMsg) continue;
883
2
        auto textRef=readRef(get(noteMsg), 2);
884
2
        if (textRef)
885
0
        {
886
0
          attachments.insert(make_pair(get(it.uint32(1)),
887
0
                                       [this,createNoteAsFootnote,textRef](unsigned, bool &ignore)
888
0
          {
889
0
            ignore=true;
890
0
            auto currentText=m_currentText;
891
0
            m_currentText = m_collector.createText(m_langManager);
892
0
            parseText(get(textRef));
893
0
            IWORKOutputElements elements;
894
0
            if (createNoteAsFootnote)
895
0
              elements.addOpenFootnote(librevenge::RVNGPropertyList());
896
0
            else
897
0
              elements.addOpenEndnote(librevenge::RVNGPropertyList());
898
0
            m_currentText->draw(elements);
899
0
            if (createNoteAsFootnote)
900
0
              elements.addCloseFootnote();
901
0
            else
902
0
              elements.addCloseEndnote();
903
0
            m_currentText=currentText;
904
0
            m_currentText->insertInlineContent(elements);
905
0
          }));
906
0
        }
907
2
        else
908
2
        {
909
2
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[16]: can not find a note\n"));
910
2
        }
911
2
      }
912
57
    }
913
6.35k
    if (openPageFunction && get(msg).message(17))
914
0
    {
915
0
      map<unsigned, IWORKStylePtr_t> pageMasters;
916
0
      for (const auto &it : get(msg).message(17).message(1))
917
0
      {
918
0
        if (!it.uint32(1)) continue;
919
0
        const optional<unsigned> &pageMasterRef = readRef(it, 2);
920
0
        if (!pageMasterRef) continue;
921
0
        PageMaster pageMaster;
922
0
        parsePageMaster(get(pageMasterRef), pageMaster);
923
0
        pageMasters.insert(pageMasters.end(), make_pair(get(it.uint32(1)), pageMaster.m_style));
924
0
      }
925
0
      textParser.setPageMasters(pageMasters);
926
0
    }
927
6.35k
    if (get(msg).message(19))
928
1.78k
    {
929
1.78k
      map<unsigned, string> langs;
930
1.78k
      for (const auto &it : get(msg).message(19).message(1))
931
2.04k
      {
932
2.04k
        if (it.uint32(1))
933
1.97k
          langs.insert(langs.end(), make_pair(get(it.uint32(1)), get_optional_value_or(it.string(2), "")));
934
2.04k
      }
935
1.78k
      textParser.setLanguages(langs);
936
1.78k
    }
937
6.35k
    if (get(msg).message(24))
938
5.78k
    {
939
5.78k
      map<unsigned, bool> rtls;
940
5.78k
      for (const auto &it : get(msg).message(24).message(1))
941
5.84k
      {
942
5.84k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
943
4.93k
        {
944
4.93k
          if (it.bool_(2))
945
4.81k
            rtls[get(it.uint32(1))]=get(it.bool_(2));
946
4.93k
        }
947
5.84k
      }
948
5.78k
      textParser.setRTLs(rtls);
949
5.78k
    }
950
6.35k
    if (get(msg).message(28))
951
82
    {
952
82
      map<unsigned, IWORKStylePtr_t> dropCaps;
953
82
      for (const auto &it : get(msg).message(28).message(1))
954
109
      {
955
109
        if (!it.uint32(1)) continue;
956
67
        const optional<unsigned> &dropCapRef = readRef(it, 2);
957
67
        if (!dropCapRef) continue;
958
1
        const IWORKStylePtr_t &dropCapStyle = queryDropCapStyle(get(dropCapRef));
959
1
        if (dropCapStyle && dropCapStyle->has<property::DropCap>())
960
0
          dropCaps.insert(dropCaps.end(), make_pair(get(it.uint32(1)), dropCapStyle));
961
1
      }
962
82
      textParser.setDropCaps(dropCaps);
963
82
    }
964
6.35k
    if (get(msg).message(23))
965
52
    {
966
52
      for (const auto &it : get(msg).message(23).message(1))
967
57
      {
968
        // no position
969
57
        if (!it.uint32(1)) continue;
970
23
        if (!it.message(2)) continue; // no text ref means end of comment, ...
971
11
        auto const &commentRef = readRef(it, 2);
972
11
        if (!commentRef) continue;
973
1
        const ObjectMessage commentMsg(*this, get(commentRef), IWAObjectType::CommentField);
974
1
        if (!commentMsg) continue;
975
1
        auto textRef=readRef(get(commentMsg), 1);
976
        // field 2: some small integer
977
1
        if (textRef)
978
0
        {
979
0
          attachments.insert(make_pair(get(it.uint32(1)),
980
0
                                       [this,textRef](unsigned, bool &)
981
0
          {
982
0
            auto currentText=m_currentText;
983
0
            m_currentText = m_collector.createText(m_langManager);
984
0
            parseComment(get(textRef));
985
0
            IWORKOutputElements elements;
986
0
            elements.addOpenComment(librevenge::RVNGPropertyList());
987
0
            m_currentText->draw(elements);
988
0
            elements.addCloseComment();
989
0
            m_currentText=currentText;
990
0
            m_currentText->insertInlineContent(elements);
991
0
          }));
992
0
        }
993
1
        else
994
1
        {
995
1
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[23]: can not find a comment\n"));
996
1
        }
997
1
      }
998
52
    }
999
6.35k
    textParser.setAttachments(attachments);
1000
6.35k
    textParser.parse(*m_currentText, openPageFunction);
1001
6.35k
  }
1002
18.9k
  return true;
1003
18.9k
}
1004
1005
const IWORKStylePtr_t IWAParser::queryStyle(const unsigned id, StyleMap_t &styleMap, StyleParseFun_t parseStyle) const
1006
34.9k
{
1007
34.9k
  StyleMap_t::const_iterator it = styleMap.find(id);
1008
34.9k
  if (it == styleMap.end())
1009
17.0k
  {
1010
17.0k
    IWORKStylePtr_t style;
1011
17.0k
    parseStyle(id, style);
1012
17.0k
    it = styleMap.insert(make_pair(id, style)).first;
1013
17.0k
  }
1014
34.9k
  assert(it != styleMap.end());
1015
34.9k
  return it->second;
1016
34.9k
}
1017
1018
const IWORKStylePtr_t IWAParser::queryCharacterStyle(const unsigned id) const
1019
283
{
1020
283
  return queryStyle(id, m_charStyles, bind(&IWAParser::parseCharacterStyle, const_cast<IWAParser *>(this), _1, _2));
1021
283
}
1022
1023
const IWORKStylePtr_t IWAParser::queryDropCapStyle(const unsigned id) const
1024
0
{
1025
0
  return queryStyle(id, m_dropCapStyles, bind(&IWAParser::parseDropCapStyle, const_cast<IWAParser *>(this), _1, _2));
1026
0
}
1027
1028
const IWORKStylePtr_t IWAParser::queryParagraphStyle(const unsigned id) const
1029
7.12k
{
1030
7.12k
  return queryStyle(id, m_paraStyles, bind(&IWAParser::parseParagraphStyle, const_cast<IWAParser *>(this), _1, _2));
1031
7.12k
}
1032
1033
const IWORKStylePtr_t IWAParser::querySectionStyle(const unsigned id) const
1034
0
{
1035
0
  return queryStyle(id, m_sectionStyles, bind(&IWAParser::parseSectionStyle, const_cast<IWAParser *>(this), _1, _2));
1036
0
}
1037
1038
const IWORKStylePtr_t IWAParser::queryGraphicStyle(const unsigned id) const
1039
14.6k
{
1040
14.6k
  return queryStyle(id, m_graphicStyles, bind(&IWAParser::parseGraphicStyle, const_cast<IWAParser *>(this), _1, _2));
1041
14.6k
}
1042
1043
const IWORKStylePtr_t IWAParser::queryMediaStyle(const unsigned id) const
1044
459
{
1045
459
  return queryStyle(id, m_mediaStyles, bind(&IWAParser::parseMediaStyle, const_cast<IWAParser *>(this), _1, _2));
1046
459
}
1047
1048
const IWORKStylePtr_t IWAParser::queryCellStyle(const unsigned id) const
1049
841
{
1050
841
  return queryStyle(id, m_cellStyles, bind(&IWAParser::parseCellStyle, const_cast<IWAParser *>(this), _1, _2));
1051
841
}
1052
1053
const IWORKStylePtr_t IWAParser::queryTableStyle(const unsigned id) const
1054
244
{
1055
244
  return queryStyle(id, m_tableStyles, bind(&IWAParser::parseTableStyle, const_cast<IWAParser *>(this), _1, _2));
1056
244
}
1057
1058
const IWORKStylePtr_t IWAParser::queryListStyle(const unsigned id) const
1059
4.97k
{
1060
4.97k
  return queryStyle(id, m_tableStyles, bind(&IWAParser::parseListStyle, const_cast<IWAParser *>(this), _1, _2));
1061
4.97k
}
1062
1063
bool IWAParser::parseAttachment(const unsigned id)
1064
0
{
1065
0
  auto collector=dynamic_cast<PAGCollector *>(&m_collector);
1066
0
  if (!collector)
1067
0
  {
1068
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the page collector\n"));
1069
0
    return false;
1070
0
  }
1071
0
  const ObjectMessage msg(*this, id, IWAObjectType::ShapeField);
1072
0
  if (!msg)
1073
0
  {
1074
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the attachment\n"));
1075
0
    return false;
1076
0
  }
1077
0
  auto objectRef=readRef(get(msg),1);
1078
0
  if (!objectRef)
1079
0
  {
1080
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the attached object\n"));
1081
0
    return false;
1082
0
  }
1083
1084
0
  IWORKPosition position;
1085
  // 2: false
1086
0
  auto x=get(msg).float_(3);
1087
  // 4: false
1088
0
  auto y=get(msg).float_(5);
1089
0
  if (x && !std::isnan(get(x))) position.m_x=get(x);
1090
0
  else
1091
0
  {
1092
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the x's position\n"));
1093
0
  }
1094
0
  if (y && !std::isnan(get(y))) position.m_y=get(y);
1095
0
  else
1096
0
  {
1097
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the y's position\n"));
1098
0
  }
1099
1100
0
  const ObjectMessage object(*this, get(objectRef));
1101
0
  if (!object)
1102
0
  {
1103
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: can not find the attached object[II]\n"));
1104
0
    return false;
1105
0
  }
1106
0
  auto currentText=m_currentText;
1107
0
  m_currentText.reset();
1108
0
  collector->startLevel();
1109
0
  collector->startAttachments();
1110
0
  collector->startAttachment();
1111
0
  collector->collectAttachmentPosition(position);
1112
0
  collector->getOutputManager().push();
1113
1114
0
  bool ok=false, sendInBlock=false;
1115
0
  switch (object.getType())
1116
0
  {
1117
0
  case IWAObjectType::ConnectionLine :
1118
0
  case IWAObjectType::DrawableShape :
1119
0
    ok=parseDrawableShape(get(object), object.getType()==IWAObjectType::ConnectionLine);
1120
0
    break;
1121
0
  case IWAObjectType::Group :
1122
0
    ok=parseGroup(get(object));
1123
0
    break;
1124
0
  case IWAObjectType::Image :
1125
0
    ok=parseImage(get(object));
1126
0
    break;
1127
0
  case IWAObjectType::TabularInfo :
1128
0
    sendInBlock=true;
1129
0
    collector->collectAttachmentPosition(IWORKPosition());
1130
0
    ok=parseTabularInfo(get(object));
1131
0
    break;
1132
0
  default:
1133
0
  {
1134
0
    static bool first=true;
1135
0
    if (first)
1136
0
    {
1137
0
      first=false;
1138
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseAttachment: unknown object type\n"));
1139
0
    }
1140
0
  }
1141
0
  }
1142
0
  auto cId=collector->getOutputManager().save();
1143
0
  auto content = collector->getOutputManager().get(cId);
1144
0
  collector->getOutputManager().pop();
1145
0
  collector->endAttachment();
1146
0
  collector->endAttachments();
1147
0
  collector->endLevel();
1148
1149
0
  if (ok)
1150
0
  {
1151
0
    if (sendInBlock)
1152
0
      currentText->insertBlockContent(content);
1153
0
    else
1154
0
      currentText->insertInlineContent(content);
1155
0
  }
1156
0
  m_currentText=currentText;
1157
0
  return ok;
1158
0
}
1159
1160
bool IWAParser::parseArrowProperties(const IWAMessage &arrow, IWORKPropertyMap &props, bool headArrow)
1161
772
{
1162
772
  IWORKMarker marker;
1163
772
  bool hasPath=false;
1164
772
  if (arrow.message(1))
1165
186
  {
1166
186
    const auto &arrowProp=get(arrow.message(1));
1167
186
    IWORKPathPtr_t path;
1168
186
    if (parsePath(arrowProp, path) && path && !path->str().empty())
1169
102
    {
1170
102
      marker.m_path=path->str();
1171
102
      hasPath=true;
1172
102
    }
1173
186
  }
1174
772
  marker.m_endPoint=readPosition(arrow,3);
1175
  // 2: a bool, 4: a bool, 5: name
1176
772
  if (headArrow)
1177
94
  {
1178
94
    if (hasPath)
1179
58
      props.put<property::HeadLineEnd>(marker);
1180
36
    else
1181
36
      props.clear<property::HeadLineEnd>();
1182
94
  }
1183
678
  else
1184
678
  {
1185
678
    if (hasPath)
1186
44
      props.put<property::TailLineEnd>(marker);
1187
634
    else
1188
634
      props.clear<property::TailLineEnd>();
1189
678
  }
1190
772
  return true;
1191
772
}
1192
1193
bool IWAParser::parsePath(const IWAMessage &msg, IWORKPathPtr_t &path)
1194
3.79k
{
1195
3.79k
  const deque<IWAMessage> &elements = msg.message(1).repeated();
1196
3.79k
  bool closed = false;
1197
3.79k
  bool closingMove = false;
1198
3.79k
  path.reset(new IWORKPath());
1199
3.79k
  for (auto it : elements)
1200
20.7k
  {
1201
20.7k
    const auto &type = it.uint32(1).optional();
1202
20.7k
    if (!type)
1203
783
    {
1204
783
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: can not read the type\n"));
1205
783
      continue;
1206
783
    }
1207
19.9k
    if (closed && closingMove)
1208
13
    {
1209
13
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: unexpected element %c after the closing move\n", get(type)));
1210
13
      break;
1211
13
    }
1212
19.9k
    switch (get(type))
1213
19.9k
    {
1214
6.79k
    case 1 :
1215
6.79k
      if (closed)
1216
3.00k
      {
1217
3.00k
        closingMove = true;
1218
3.00k
        break;
1219
3.00k
      }
1220
3.79k
      ETONYEK_FALLTHROUGH;
1221
13.2k
    case 2 :
1222
13.2k
    {
1223
13.2k
      const optional<IWORKPosition> &coords = readPosition(it, 2);
1224
13.2k
      if (!coords)
1225
34
      {
1226
34
        ETONYEK_DEBUG_MSG(("IWAParser::parsePath: missing coordinates for %c element\n", get(type) == 1 ? 'M' : 'L'));
1227
34
        return false;
1228
34
      }
1229
13.2k
      if (get(type) == 1)
1230
3.76k
        path->appendMoveTo(get(coords).m_x, get(coords).m_y);
1231
9.45k
      else
1232
9.45k
      {
1233
9.45k
        if (path->empty())
1234
19
        {
1235
19
          ETONYEK_DEBUG_MSG(("IWAParser::parsePath: missing prior MoveTo subsequent LineTo\n"));
1236
19
          return false;
1237
19
        }
1238
9.43k
        path->appendLineTo(get(coords).m_x, get(coords).m_y);
1239
9.43k
      }
1240
13.2k
      break;
1241
13.2k
    }
1242
13.2k
    case 4 :
1243
627
    {
1244
627
      if (it.message(2))
1245
597
      {
1246
597
        const std::deque<IWAMessage> &positions = it.message(2).repeated();
1247
597
        if (positions.size() >= 3)
1248
587
        {
1249
587
          if (positions.size() > 3)
1250
26
          {
1251
26
            ETONYEK_DEBUG_MSG(("IWAParser::parsePath: a curve has got %u control coords\n", unsigned(positions.size())));
1252
26
          }
1253
587
          const optional<float> &x = positions[0].float_(1).optional();
1254
587
          const optional<float> &y = positions[0].float_(2).optional();
1255
587
          const optional<float> &x1 = positions[1].float_(1).optional();
1256
587
          const optional<float> &y1 = positions[1].float_(2).optional();
1257
587
          const optional<float> &x2 = positions[2].float_(1).optional();
1258
587
          const optional<float> &y2 = positions[2].float_(2).optional();
1259
587
          path->appendCCurveTo(get_optional_value_or(x, 0), get_optional_value_or(y, 0),
1260
587
                               get_optional_value_or(x1, 0), get_optional_value_or(y1, 0),
1261
587
                               get_optional_value_or(x2, 0), get_optional_value_or(y2, 0));
1262
587
        }
1263
10
        else
1264
10
        {
1265
10
          ETONYEK_DEBUG_MSG(("IWAParser::parsePath: %u is not enough coords for a curve\n", unsigned(positions.size())));
1266
10
          return false;
1267
10
        }
1268
597
      }
1269
617
      break;
1270
627
    }
1271
3.04k
    case 5 :
1272
3.04k
      path->appendClose();
1273
3.04k
      closed = true;
1274
3.04k
      break;
1275
34
    default :
1276
34
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: unknown bezier path element type %u\n", get(type)));
1277
34
      return false;
1278
19.9k
    }
1279
19.9k
  }
1280
3.68k
  return true;
1281
3.79k
}
1282
1283
bool IWAParser::parseStickyNote(const IWAMessage &/*msg*/)
1284
0
{
1285
0
  ETONYEK_DEBUG_MSG(("IWAParser::parseStickyNote: not implemented\n"));
1286
0
  return false;
1287
0
}
1288
1289
bool IWAParser::parseDrawableShape(const IWAMessage &msg, bool isConnectionLine)
1290
6.36k
{
1291
6.36k
  m_collector.startLevel();
1292
1293
6.36k
  const optional<IWAMessage> &shape = msg.message(1).optional();
1294
6.36k
  const optional<unsigned> &textRef = readRef(msg, 2);
1295
6.36k
  boost::optional<unsigned> resizeFlags;
1296
1297
6.36k
  if (shape)
1298
6.13k
  {
1299
6.13k
    const optional<IWAMessage> &placement = get(shape).message(1).optional();
1300
6.13k
    IWORKStylePtr_t style;
1301
6.13k
    const optional<unsigned> styleRef = readRef(get(shape), 2);
1302
6.13k
    if (styleRef)
1303
5.50k
      style=queryGraphicStyle(get(styleRef));
1304
6.13k
    const optional<IWAMessage> &path = get(shape).message(3).optional();
1305
6.13k
    if (placement)
1306
5.78k
    {
1307
5.78k
      IWORKGeometryPtr_t geometry;
1308
5.78k
      parseShapePlacement(get(placement), geometry, resizeFlags);
1309
5.78k
      if (geometry && (geometry->m_naturalSize.m_width<=0 || geometry->m_naturalSize.m_height<=0) && path)
1310
657
      {
1311
        // try to retrieve the shape's size in the path
1312
657
        std::map<unsigned,unsigned> const cIdToSizeId= { { 3, 3}, { 4, 3}, {5, 2}, { 6, 1}, { 8, 2} };
1313
657
        for (auto const &it : cIdToSizeId)
1314
2.95k
        {
1315
2.95k
          if (!get(path).message(it.first)) continue;
1316
594
          auto const &pathSize=readSize(get(get(path).message(it.first)), it.second);
1317
594
          if (!pathSize || pathSize->m_width<=0 || pathSize->m_height<=0) continue;
1318
156
          geometry->m_naturalSize=geometry->m_size=get(pathSize);
1319
156
          break;
1320
594
        }
1321
657
      }
1322
5.78k
      if (geometry && resizeFlags && (get(resizeFlags) &1)==0 && textRef) // correct horizontal position
1323
860
        updateGeometryUsingTextRef(get(textRef), *geometry, get(resizeFlags));
1324
5.78k
      if (geometry && resizeFlags && (get(resizeFlags)&2)==0 && geometry->m_size.m_height>0 && style && style->has<property::VerticalAlignment>())
1325
112
      {
1326
        // correct vertical position
1327
112
        switch (style->get<property::VerticalAlignment>())
1328
112
        {
1329
94
        case IWORK_VERTICAL_ALIGNMENT_MIDDLE:
1330
94
          geometry->m_position.m_y -= geometry->m_size.m_height/2.;
1331
94
          break;
1332
4
        case IWORK_VERTICAL_ALIGNMENT_BOTTOM:
1333
4
          geometry->m_position.m_y -= geometry->m_size.m_height;
1334
4
          break;
1335
14
        case IWORK_VERTICAL_ALIGNMENT_TOP:
1336
14
        default:
1337
14
          break;
1338
112
        }
1339
112
      }
1340
1341
5.78k
      m_collector.collectGeometry(geometry);
1342
5.78k
    }
1343
1344
    // look for arrow Keynote 6
1345
6.13k
    if (get(shape).message(4) || get(shape).message(5))
1346
618
    {
1347
618
      if (!style)
1348
608
        style=std::make_shared<IWORKStyle>(IWORKPropertyMap(),boost::none, boost::none);
1349
1.85k
      for (size_t st=0; st<2; ++st)
1350
1.23k
      {
1351
1.23k
        if (!get(shape).message(st+4)) continue;
1352
630
        parseArrowProperties(get(get(shape).message(st+4)),style->getPropertyMap(),st==0);
1353
630
      }
1354
618
    }
1355
6.13k
    if (style)
1356
3.00k
      m_collector.setGraphicStyle(style);
1357
1358
6.13k
    if (path)
1359
5.48k
    {
1360
5.48k
      if (get(path).message(3)) // point path
1361
703
      {
1362
703
        const IWAMessage &pointPath = get(path).message(3).get();
1363
703
        const optional<unsigned> &type = pointPath.uint32(1).optional();
1364
703
        const optional<IWORKPosition> &point = readPosition(pointPath, 2);
1365
703
        const optional<IWORKSize> &size = readSize(pointPath, 3);
1366
703
        if (type && point && size)
1367
587
        {
1368
587
          switch (get(type))
1369
587
          {
1370
289
          case 1 :
1371
487
          case 10 :
1372
487
            m_collector.collectArrowPath(get(size), get(point).m_x, get(point).m_y, get(type) == 10);
1373
487
            break;
1374
99
          case 100 :
1375
99
            m_collector.collectStarPath(get(size), unsigned(get(point).m_x+0.4), get(point).m_y);
1376
99
            break;
1377
1
          default :
1378
1
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: unknown point path type %u\n", get(type)));
1379
1
            break;
1380
587
          }
1381
587
        }
1382
703
      }
1383
4.78k
      else if (get(path).message(4)) // scalar path
1384
162
      {
1385
162
        const IWAMessage &scalarPath = get(path).message(4).get();
1386
162
        const optional<unsigned> &type = scalarPath.uint32(1).optional();
1387
162
        const optional<float> &value = scalarPath.float_(2).optional();
1388
162
        const optional<IWORKSize> &size = readSize(scalarPath, 3);
1389
162
        if (type && value && size)
1390
131
        {
1391
131
          switch (get(type))
1392
131
          {
1393
94
          case 0 :
1394
94
            m_collector.collectRoundedRectanglePath(get(size), get(value));
1395
94
            break;
1396
37
          case 1 :
1397
37
            m_collector.collectPolygonPath(get(size), unsigned(get(value)+0.4));
1398
37
            break;
1399
0
          default :
1400
0
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: unknown scalar path type %u\n", get(type)));
1401
0
            break;
1402
131
          }
1403
131
        }
1404
162
      }
1405
4.62k
      else if (get(path).message(5))
1406
3.64k
      {
1407
3.64k
        auto const &bezier = get(path).message(5).get().message(3).optional();
1408
3.64k
        if (bezier)
1409
3.60k
        {
1410
3.60k
          IWORKPathPtr_t bezierPath;
1411
3.60k
          if (parsePath(get(bezier),bezierPath))
1412
3.52k
          {
1413
3.52k
            const optional<IWORKSize> &size = readSize(get(get(path).message(5)), 2);
1414
3.52k
            if (size)
1415
3.50k
            {
1416
3.50k
              double x[2]= {0,0}, y[2]= {0,0};
1417
3.50k
              if (bezierPath)
1418
3.50k
                bezierPath->computeBoundingBox(x[0], y[0], x[1], y[1]);
1419
              // if we can not use the bounding box, assume tha the path is in unit area
1420
3.50k
              *bezierPath *= transformations::scale(get(size).m_width / (x[1]>x[0] ? x[1]-x[0] : 100), get(size).m_height / (y[1]>y[0] ? y[1]-y[0] : 100));
1421
3.50k
            }
1422
3.52k
            m_collector.collectBezier(bezierPath);
1423
3.52k
            m_collector.collectBezierPath();
1424
3.52k
          }
1425
3.60k
        }
1426
3.64k
      }
1427
979
      else if (get(path).message(6)) // callout2 path
1428
170
      {
1429
170
        const IWAMessage &callout2Path = get(path).message(6).get();
1430
170
        const optional<IWORKSize> &size = readSize(callout2Path, 1);
1431
170
        const optional<IWORKPosition> &tailPos = readPosition(callout2Path, 2);
1432
170
        const optional<float> &tailSize = callout2Path.float_(3).optional();
1433
170
        if (size && tailPos && tailSize)
1434
141
        {
1435
141
          const optional<float> &cornerRadius = callout2Path.float_(4).optional();
1436
141
          const optional<bool> &tailAtCenter = callout2Path.bool_(5).optional();
1437
141
          m_collector.collectCalloutPath(get(size), get_optional_value_or(cornerRadius, 0),
1438
141
                                         get(tailSize), get(tailPos).m_x, get(tailPos).m_y,
1439
141
                                         get_optional_value_or(tailAtCenter, false));
1440
141
        }
1441
170
      }
1442
809
      else if (get(path).message(7))
1443
52
      {
1444
52
        auto rootMsg=get(get(path).message(7)).message(1).optional();
1445
52
        if (rootMsg)
1446
37
        {
1447
37
          IWORKConnectionPath cPath;
1448
37
          cPath.m_size=readSize(get(rootMsg), 2);
1449
37
          cPath.m_isSpline=!get_optional_value_or(get(get(path).message(7)).bool_(2),false);
1450
37
          auto const &bezier = rootMsg.get().message(3).optional();
1451
37
          if (bezier)
1452
11
          {
1453
11
            const deque<IWAMessage> &elements = get(bezier).message(1).repeated();
1454
11
            int pos=0;
1455
11
            for (auto it : elements)
1456
0
            {
1457
              // normally first point (type 1) followed by 2 points (type 2)
1458
              // const auto &type = it.uint32(1).optional();
1459
0
              if (pos>=3)
1460
0
              {
1461
0
                ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: oops find unexpected number of points\n"));
1462
0
                break;
1463
0
              }
1464
0
              cPath.m_positions[pos++]=readPosition(it, 2);
1465
0
            }
1466
11
            if (pos==3)
1467
0
            {
1468
0
              m_collector.collectConnectionPath(cPath);
1469
0
            }
1470
11
            else
1471
11
            {
1472
11
              ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: oops find unexpected number of points\n"));
1473
11
            }
1474
11
          }
1475
26
          else
1476
26
          {
1477
26
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: can not find the points zone\n"));
1478
26
          }
1479
37
        }
1480
15
        else
1481
15
        {
1482
15
          ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: can not find the root bezier zone\n"));
1483
15
        }
1484
52
      }
1485
757
      else if (get(path).message(8)) // editable path
1486
80
      {
1487
80
        const IWAMessageField &pathPoints = get(get(path).message(8)).message(1);
1488
80
        if (pathPoints && !pathPoints.message(1).empty())
1489
51
        {
1490
51
          const IWORKPathPtr_t editablePath(new IWORKPath());
1491
51
          const IWAMessageField &points = pathPoints.message(1);
1492
51
          std::vector<IWORKPosition> positions;
1493
          // cubic bezier patch, [prev pt dir], pt, [next pt dir]
1494
51
          for (auto it : points)
1495
218
          {
1496
218
            const optional<IWORKPosition> &point1 = readPosition(it, 1);
1497
218
            const optional<IWORKPosition> &point2 = readPosition(it, 2);
1498
218
            const optional<IWORKPosition> &point3 = readPosition(it, 3);
1499
            // [4 type: {1: line, 3:curve}
1500
218
            if (!point2)
1501
138
            {
1502
138
              ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: no control points for point2\n"));
1503
138
              continue;
1504
138
            }
1505
80
            positions.push_back(get_optional_value_or(point1, get(point2)));
1506
80
            positions.push_back(get(point2));
1507
80
            positions.push_back(get_optional_value_or(point3, get(point2)));
1508
80
          }
1509
51
          size_t nbPt=positions.size()/3;
1510
51
          if (nbPt<=1)
1511
15
          {
1512
15
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: find only %d points\n", int(nbPt)));
1513
15
          }
1514
36
          else
1515
36
          {
1516
36
            editablePath->appendMoveTo(positions[1].m_x, positions[1].m_y);
1517
36
            bool isClosed= get_optional_value_or(pathPoints.bool_(2),false);
1518
82
            for (size_t i=0; i<nbPt; ++i)
1519
67
            {
1520
67
              if (i+1==nbPt && !isClosed) break;
1521
46
              auto const &prevPoint=positions[3*i+1];
1522
46
              auto const &pt1=positions[3*i+2];
1523
46
              auto const &pt2=positions[3*((i+1)%nbPt)];
1524
46
              auto const &pt3=positions[3*((i+1)%nbPt)+1];
1525
46
              if (samePoint(prevPoint,pt1) && samePoint(pt2,pt3))
1526
46
                editablePath->appendLineTo(pt3.m_x, pt3.m_y);
1527
0
              else
1528
0
                editablePath->appendCCurveTo(pt1.m_x,pt1.m_y, pt2.m_x,pt2.m_y, pt3.m_x,pt3.m_y);
1529
46
            }
1530
36
            if (isClosed)
1531
9
              editablePath->appendClose();
1532
36
            m_collector.collectBezier(editablePath);
1533
36
            m_collector.collectBezierPath();
1534
36
          }
1535
51
        }
1536
80
      }
1537
5.48k
    }
1538
6.13k
  }
1539
6.36k
  bool hasText=false;
1540
6.36k
  if (!isConnectionLine && textRef)
1541
5.74k
  {
1542
5.74k
    m_currentText = m_collector.createText(m_langManager, true);
1543
5.74k
    parseText(get(textRef));
1544
5.74k
    if (!m_currentText->empty())
1545
1.82k
    {
1546
1.82k
      hasText=true;
1547
1.82k
      m_collector.collectText(m_currentText);
1548
1.82k
    }
1549
5.74k
  }
1550
1551
6.36k
  if (shape || hasText)
1552
5.94k
    m_collector.collectShape(boost::none, resizeFlags);
1553
6.36k
  m_currentText.reset();
1554
1555
6.36k
  m_collector.endLevel();
1556
1557
6.36k
  return true;
1558
6.36k
}
1559
1560
bool IWAParser::parseGroup(const IWAMessage &msg)
1561
151
{
1562
151
  m_collector.startLevel();
1563
151
  if (msg.message(1))
1564
139
    parseShapePlacement(get(msg.message(1)));
1565
151
  if (!msg.message(2).empty())
1566
132
  {
1567
132
    m_collector.startGroup();
1568
132
    m_collector.openGroup();
1569
132
    const deque<unsigned> &shapeRefs = readRefs(msg, 2);
1570
132
    std::for_each(shapeRefs.begin(), shapeRefs.end(), bind(&IWAParser::dispatchShape, this, _1));
1571
132
    m_collector.closeGroup();
1572
132
    m_collector.endGroup();
1573
132
  }
1574
151
  m_collector.endLevel();
1575
1576
151
  return true;
1577
151
}
1578
1579
bool IWAParser::parseShapePlacement(const IWAMessage &msg, IWORKGeometryPtr_t &geometry, boost::optional<unsigned> &flags)
1580
14.6k
{
1581
14.6k
  geometry = make_shared<IWORKGeometry>();
1582
14.6k
  flags=3; // no auto resize
1583
1584
14.6k
  const optional<IWAMessage> &g = msg.message(1).optional();
1585
14.6k
  if (g)
1586
14.5k
  {
1587
14.5k
    const optional<IWORKPosition> &pos = readPosition(get(g), 1);
1588
14.5k
    if (pos)
1589
14.3k
      geometry->m_position = get(pos);
1590
14.5k
    const optional<IWORKSize> &size = readSize(get(g), 2);
1591
14.5k
    if (size)
1592
14.1k
      geometry->m_naturalSize = geometry->m_size = get(size);
1593
14.5k
    if (get(g).uint32(3))
1594
13.9k
    {
1595
13.9k
      flags=get(get(g).uint32(3));
1596
      // flags&1 : horizontal position is fixed
1597
      // flags&2 : vertical position is fixed
1598
13.9k
      if (get(flags)&4) // horizontal flip
1599
160
        geometry->m_horizontalFlip = true;
1600
13.9k
      if (get(flags)&0xFFF8)
1601
896
      {
1602
896
        ETONYEK_DEBUG_MSG(("IWAParser::parseShapePlacement: unknown transformation %u\n", get(flags)));
1603
896
      }
1604
13.9k
    }
1605
14.5k
    if (get(g).float_(4))
1606
13.2k
      geometry->m_angle = -deg2rad(get(get(g).float_(4)));
1607
14.5k
  }
1608
14.6k
  geometry->m_aspectRatioLocked = msg.bool_(7).optional();
1609
1610
14.6k
  return true;
1611
14.6k
}
1612
1613
bool IWAParser::parseShapePlacement(const IWAMessage &msg)
1614
340
{
1615
340
  IWORKGeometryPtr_t geometry;
1616
340
  boost::optional<unsigned> flags;
1617
340
  const bool retval = parseShapePlacement(msg, geometry, flags);
1618
340
  m_collector.collectGeometry(geometry);
1619
340
  return retval;
1620
340
}
1621
1622
void IWAParser::parseMask(unsigned id, IWORKGeometryPtr_t &geometry, IWORKPathPtr_t &/*path*/)
1623
219
{
1624
219
  const ObjectMessage msg(*this, id, IWAObjectType::Mask);
1625
219
  if (!msg)
1626
21
    return;
1627
198
  if (get(msg).message(1))
1628
139
  {
1629
139
    boost::optional<unsigned> flags;
1630
139
    parseShapePlacement(get(get(msg).message(1)), geometry, flags);
1631
139
  }
1632
  // if (get(msg).message(2)) same code as parseDrawableShape
1633
198
}
1634
1635
void IWAParser::parseObjectIndex()
1636
2.49k
{
1637
2.49k
  m_index.parse();
1638
2.49k
}
1639
1640
void IWAParser::parseCharacterStyle(const unsigned id, IWORKStylePtr_t &style)
1641
190
{
1642
190
  const ObjectMessage msg(*this, id, IWAObjectType::CharacterStyle);
1643
190
  if (!msg)
1644
102
    return;
1645
1646
88
  optional<string> name;
1647
88
  IWORKStylePtr_t parent;
1648
88
  const IWAMessageField &styleInfo = get(msg).message(1);
1649
88
  if (styleInfo)
1650
85
  {
1651
85
    name = styleInfo.string(2).optional();
1652
85
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1653
85
    if (parentRef)
1654
43
      parent = queryCharacterStyle(get(parentRef));
1655
85
  }
1656
1657
88
  IWORKPropertyMap props;
1658
88
  if (get(msg).message(11))
1659
64
    parseCharacterProperties(get(get(msg).message(11)), props);
1660
1661
88
  style = std::make_shared<IWORKStyle>(props, name, parent);
1662
88
}
1663
1664
void IWAParser::parseDropCapStyle(unsigned id, IWORKStylePtr_t &style)
1665
0
{
1666
0
  const ObjectMessage msg(*this, id, IWAObjectType::DropCapStyle);
1667
0
  if (!msg)
1668
0
    return;
1669
0
  optional<string> name;
1670
0
  IWORKStylePtr_t parent;
1671
0
  const IWAMessageField &styleInfo = get(msg).message(1);
1672
0
  if (styleInfo)
1673
0
  {
1674
0
    name = styleInfo.string(2).optional();
1675
0
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1676
0
    if (parentRef)
1677
0
      parent = queryDropCapStyle(get(parentRef));
1678
0
  }
1679
0
  IWORKDropCap cap;
1680
0
  if (parent && parent->has<property::DropCap>())
1681
0
    cap=parent->get<property::DropCap>();
1682
0
  if (get(msg).message(11))
1683
0
  {
1684
0
    IWORKPropertyMap props;
1685
0
    parseCharacterProperties(get(get(msg).message(11)), props);
1686
0
    cap.m_style=std::make_shared<IWORKStyle>(props, boost::none, cap.m_style);
1687
0
  }
1688
0
  if (get(msg).message(12))
1689
0
    readDropCap(get(get(msg).message(12)), cap);
1690
1691
0
  IWORKPropertyMap props;
1692
0
  props.put<property::DropCap>(cap);
1693
0
  style = std::make_shared<IWORKStyle>(props, name, parent);
1694
0
}
1695
1696
void IWAParser::parseParagraphStyle(const unsigned id, IWORKStylePtr_t &style)
1697
4.18k
{
1698
4.18k
  const ObjectMessage msg(*this, id, IWAObjectType::ParagraphStyle);
1699
4.18k
  if (!msg)
1700
2.24k
    return;
1701
1702
1.94k
  optional<string> name;
1703
1.94k
  IWORKStylePtr_t parent;
1704
1.94k
  const IWAMessageField &styleInfo = get(msg).message(1);
1705
1.94k
  if (styleInfo)
1706
1.90k
  {
1707
1.90k
    name = styleInfo.string(2).optional();
1708
1.90k
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1709
1.90k
    if (parentRef)
1710
285
      parent = queryParagraphStyle(get(parentRef));
1711
1.90k
  }
1712
1713
1.94k
  IWORKPropertyMap props;
1714
1.94k
  if (get(msg).message(11))
1715
1.82k
    parseCharacterProperties(get(get(msg).message(11)), props);
1716
1.94k
  if (get(msg).message(12))
1717
1.78k
  {
1718
1.78k
    const IWAMessage &paraProps = get(get(msg).message(12));
1719
1.78k
    using namespace property;
1720
1721
1.78k
    if (paraProps.uint32(1))
1722
1.51k
      putEnum<Alignment>(props, get(paraProps.uint32(1)));
1723
1.78k
    const optional<IWORKColor> &fillColor = readColor(paraProps, 6);
1724
1.78k
    if (fillColor)
1725
0
      props.put<ParagraphFill>(get(fillColor));
1726
1.78k
    if (paraProps.float_(7))
1727
1.44k
      props.put<FirstLineIndent>(get(paraProps.float_(7)));
1728
1.78k
    if (paraProps.bool_(8))
1729
1.45k
      props.put<Hyphenate>(get(paraProps.bool_(8)));
1730
1.78k
    if (paraProps.bool_(9))
1731
1.53k
      props.put<KeepLinesTogether>(get(paraProps.bool_(9)));
1732
1.78k
    if (paraProps.bool_(10))
1733
1.44k
      props.put<KeepWithNext>(get(paraProps.bool_(10)));
1734
1.78k
    if (paraProps.float_(11))
1735
1.44k
      props.put<LeftIndent>(get(paraProps.float_(11)));
1736
1.78k
    if (paraProps.message(13))
1737
1.44k
    {
1738
1.44k
      auto const &lineSpace=paraProps.message(13).float_(2);
1739
1.44k
      if (lineSpace)
1740
126
      {
1741
126
        auto const &type=paraProps.message(13).uint32(1).optional();
1742
126
        if (!type) // in line
1743
125
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), true));
1744
1
        else if (get(type)==1)   // at least in point
1745
0
        {
1746
0
          IWORKLineSpacing spacing(get(lineSpace), false);
1747
0
          spacing.m_atLeast=true;
1748
0
          props.put<LineSpacing>(spacing);
1749
0
        }
1750
1
        else if (get(type)==2) // in point
1751
0
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), false));
1752
1
        else if (get(type)==4) // between in point, transform in percent (and assume 12pt)
1753
0
          props.put<LineSpacing>(IWORKLineSpacing(1.+get(lineSpace)/12., true));
1754
1
        else   // unknown, use heuristic
1755
1
        {
1756
1
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), get(lineSpace)<3));
1757
1
          ETONYEK_DEBUG_MSG(("IWAParser::parseParagraphStyle: unknown type %u\n", get(type)));
1758
1
        }
1759
126
      }
1760
      // TODO what is paraProps.message(13).float_(3);
1761
1.44k
    }
1762
1.78k
    if (paraProps.bool_(14))
1763
1.44k
      props.put<PageBreakBefore>(get(paraProps.bool_(14)));
1764
1.78k
    if (paraProps.uint32(15))
1765
1.40k
      putEnum<ParagraphBorderType>(props, get(paraProps.uint32(15)));
1766
    // we need also to read the paragraphborder decal : field 17
1767
1.78k
    if (paraProps.float_(19))
1768
1.40k
      props.put<RightIndent>(get(paraProps.float_(19)));
1769
1.78k
    if (paraProps.float_(20))
1770
1.43k
      props.put<SpaceAfter>(get(paraProps.float_(20)));
1771
1.78k
    if (paraProps.float_(21))
1772
1.48k
      props.put<SpaceBefore>(get(paraProps.float_(21)));
1773
1.78k
    if (paraProps.message(25))
1774
1.43k
    {
1775
1.43k
      IWORKTabStops_t tabs;
1776
1.43k
      const IWAMessageField &tabStops = paraProps.message(25).message(1);
1777
1.43k
      for (const auto &tabStop : tabStops)
1778
1
      {
1779
1
        if (tabStop.float_(1))
1780
1
          tabs.push_back(IWORKTabStop(IWORK_TABULATION_LEFT, get(tabStop.float_(1))));
1781
1
      }
1782
1.43k
    }
1783
1.78k
    if (paraProps.bool_(26))
1784
1.42k
      props.put<WidowControl>(get(paraProps.bool_(26)));
1785
1.78k
    if (paraProps.message(32))
1786
2
    {
1787
2
      IWORKStroke stroke;
1788
2
      readStroke(get(paraProps.message(32)), stroke);
1789
2
      props.put<ParagraphStroke>(stroke);
1790
2
    }
1791
1.78k
  }
1792
1793
1.94k
  style = std::make_shared<IWORKStyle>(props, name, parent);
1794
1.94k
}
1795
1796
void IWAParser::parseSectionStyle(const unsigned id, IWORKStylePtr_t &style)
1797
0
{
1798
0
  const ObjectMessage msg(*this, id, IWAObjectType::SectionStyle);
1799
0
  if (!msg)
1800
0
    return;
1801
1802
0
  optional<string> name;
1803
0
  IWORKStylePtr_t parent;
1804
0
  const IWAMessageField &styleInfo = get(msg).message(1);
1805
0
  if (styleInfo)
1806
0
  {
1807
0
    name = styleInfo.string(2).optional();
1808
0
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1809
0
    if (parentRef)
1810
0
      parent = querySectionStyle(get(parentRef));
1811
0
  }
1812
  // 10: int 2 or 9
1813
  //
1814
0
  IWORKPropertyMap props;
1815
0
  if (get(msg).message(11))
1816
0
    parseColumnsProperties(get(get(msg).message(11)), props);
1817
0
  style = std::make_shared<IWORKStyle>(props, name, parent);
1818
0
}
1819
1820
void IWAParser::parseGraphicStyle(const unsigned id, IWORKStylePtr_t &style)
1821
6.30k
{
1822
6.30k
  const ObjectMessage msg(*this, id, IWAObjectType::GraphicStyle);
1823
6.30k
  if (!msg)
1824
3.83k
    return;
1825
2.47k
  optional<string> name;
1826
2.47k
  IWORKStylePtr_t parent;
1827
2.47k
  IWORKPropertyMap props;
1828
1829
2.47k
  using namespace property;
1830
1831
2.47k
  if (get(msg).message(1))
1832
2.27k
  {
1833
2.27k
    const IWAMessageField &styleInfo = get(msg).message(1).message(1);
1834
2.27k
    if (styleInfo)
1835
2.19k
    {
1836
2.19k
      name = styleInfo.string(2).optional();
1837
2.19k
      const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1838
2.19k
      if (parentRef)
1839
1.38k
        parent = queryGraphicStyle(get(parentRef));
1840
2.19k
    }
1841
1842
2.27k
    const IWAMessageField &styleProps = get(msg).message(1).message(11);
1843
2.27k
    if (styleProps)
1844
2.05k
    {
1845
2.05k
      if (styleProps.message(1))
1846
1.17k
      {
1847
1.17k
        IWORKFill fill;
1848
1.17k
        if (readFill(get(styleProps.message(1)), fill))
1849
536
          props.put<Fill>(fill);
1850
642
        else
1851
642
          props.clear<Fill>();
1852
1.17k
      }
1853
2.05k
      if (styleProps.message(2))
1854
872
      {
1855
872
        IWORKStroke stroke;
1856
872
        readStroke(get(styleProps.message(2)), stroke);
1857
872
        props.put<Stroke>(stroke);
1858
872
      }
1859
2.05k
      if (styleProps.float_(3))
1860
639
        props.put<Opacity>(get(styleProps.float_(3)));
1861
2.05k
      if (styleProps.message(4))
1862
831
      {
1863
831
        IWORKShadow shadow;
1864
831
        readShadow(get(styleProps.message(4)),shadow);
1865
831
        props.put<Shadow>(shadow);
1866
831
      }
1867
5.96k
      for (size_t st=0; st<2; ++st)
1868
3.91k
      {
1869
3.91k
        if (!get(styleProps).message(st+6)) continue;
1870
142
        parseArrowProperties(get(get(styleProps).message(st+6)),props, st==0);
1871
142
      }
1872
2.05k
    }
1873
2.27k
  }
1874
1875
2.47k
  if (get(msg).message(11))
1876
2.12k
  {
1877
2.12k
    const IWAMessageField &layout = get(msg).message(11);
1878
2.12k
    auto vAlign=layout.uint32(2);
1879
2.12k
    if (vAlign)
1880
889
    {
1881
889
      IWORKVerticalAlignment const aligns[]=
1882
889
      {IWORK_VERTICAL_ALIGNMENT_TOP, IWORK_VERTICAL_ALIGNMENT_MIDDLE,IWORK_VERTICAL_ALIGNMENT_BOTTOM};
1883
889
      if (get(vAlign) < ETONYEK_NUM_ELEMENTS(aligns))
1884
878
      {
1885
878
        props.put<VerticalAlignment>(aligns[get(vAlign)]);
1886
878
      }
1887
11
      else
1888
11
      {
1889
11
        ETONYEK_DEBUG_MSG(("IWAParser::parseGraphicStyle: unknown vAlign %u\n", get(vAlign)));
1890
11
      }
1891
889
    }
1892
2.12k
    if (get(layout).message(6))
1893
693
    {
1894
693
      IWORKPadding padding;
1895
693
      readPadding(get(get(layout).message(6)), padding);
1896
693
      props.put<LayoutMargins>(padding);
1897
693
    }
1898
2.12k
    const optional<unsigned> &paraRef = readRef(get(layout), 10);
1899
2.12k
    if (paraRef)
1900
706
    {
1901
706
      const IWORKStylePtr_t &paraStyle = queryParagraphStyle(get(paraRef));
1902
706
      if (paraStyle)
1903
668
        props.put<LayoutParagraphStyle>(paraStyle);
1904
706
    }
1905
1906
    // TODO: other layout props: 1: shrink text, 4: columns
1907
2.12k
  }
1908
1909
2.47k
  style = std::make_shared<IWORKStyle>(props, name, parent);
1910
2.47k
}
1911
1912
void IWAParser::parseMediaStyle(const unsigned id, IWORKStylePtr_t &style)
1913
215
{
1914
215
  const ObjectMessage msg(*this, id, IWAObjectType::MediaStyle);
1915
215
  if (!msg)
1916
117
    return;
1917
98
  optional<string> name;
1918
98
  IWORKStylePtr_t parent;
1919
98
  IWORKPropertyMap props;
1920
1921
98
  using namespace property;
1922
98
  const IWAMessageField &styleInfo = get(msg).message(1);
1923
98
  if (styleInfo)
1924
97
  {
1925
97
    name = styleInfo.string(2).optional();
1926
97
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1927
97
    if (parentRef)
1928
0
      parent = queryMediaStyle(get(parentRef));
1929
97
  }
1930
98
  const IWAMessageField &styleProps = get(msg).message(11);
1931
98
  if (styleProps)
1932
75
  {
1933
75
    if (styleProps.message(1))
1934
73
    {
1935
73
      IWORKStroke stroke;
1936
73
      readStroke(get(styleProps.message(1)), stroke);
1937
73
      props.put<Stroke>(stroke);
1938
73
    }
1939
75
    if (styleProps.float_(2))
1940
70
      props.put<Opacity>(get(styleProps.float_(2)));
1941
75
    if (styleProps.message(3))
1942
68
    {
1943
68
      IWORKShadow shadow;
1944
68
      readShadow(get(styleProps.message(3)),shadow);
1945
68
      props.put<Shadow>(shadow);
1946
68
    }
1947
    // 4: reflection
1948
75
  }
1949
98
  style = std::make_shared<IWORKStyle>(props, name, parent);
1950
98
}
1951
1952
void IWAParser::parseCellStyle(const unsigned id, IWORKStylePtr_t &style)
1953
819
{
1954
819
  const ObjectMessage msg(*this, id, IWAObjectType::CellStyle);
1955
819
  if (!msg)
1956
476
    return;
1957
1958
343
  optional<string> name;
1959
343
  IWORKStylePtr_t parent;
1960
343
  IWORKPropertyMap props;
1961
1962
343
  using namespace property;
1963
1964
343
  const IWAMessageField &styleInfo = get(msg).message(1);
1965
343
  if (styleInfo)
1966
332
  {
1967
332
    name = styleInfo.string(2).optional();
1968
332
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1969
332
    if (parentRef)
1970
106
      parent = queryCellStyle(get(parentRef));
1971
332
  }
1972
1973
343
  if (get(msg).message(11))
1974
313
  {
1975
313
    const IWAMessage &properties = get(get(msg).message(11));
1976
313
    if (properties.message(1))
1977
304
    {
1978
304
      IWORKFill fill;
1979
304
      if (readFill(get(properties.message(1)), fill))
1980
260
        props.put<Fill>(fill);
1981
44
      else
1982
44
        props.clear<Fill>();
1983
304
    }
1984
313
    if (properties.uint32(8))
1985
272
    {
1986
272
      auto align=get(properties.uint32(8));
1987
272
      if (align<=2)
1988
271
      {
1989
271
        const IWORKVerticalAlignment aligns[] =
1990
271
        {
1991
271
          IWORK_VERTICAL_ALIGNMENT_TOP, IWORK_VERTICAL_ALIGNMENT_MIDDLE, IWORK_VERTICAL_ALIGNMENT_BOTTOM
1992
271
        };
1993
271
        props.put<VerticalAlignment>(aligns[align]);
1994
271
      }
1995
1
      else
1996
1
      {
1997
1
        ETONYEK_DEBUG_MSG(("IWAParser::parseCellStyle: unknown align=%u\n", align));
1998
1
      }
1999
272
    }
2000
313
    if (properties.message(9))
2001
192
    {
2002
192
      IWORKPadding padding;
2003
192
      readPadding(get(properties.message(9)), padding);
2004
192
      props.put<Padding>(padding);
2005
192
    }
2006
313
    if (properties.message(10))
2007
0
    {
2008
0
      IWORKStroke stroke;
2009
0
      readStroke(get(properties.message(10)), stroke);
2010
0
      props.put<TopBorder>(stroke);
2011
0
    }
2012
313
    if (properties.message(11))
2013
0
    {
2014
0
      IWORKStroke stroke;
2015
0
      readStroke(get(properties.message(11)), stroke);
2016
0
      props.put<RightBorder>(stroke);
2017
0
    }
2018
313
    if (properties.message(12))
2019
0
    {
2020
0
      IWORKStroke stroke;
2021
0
      readStroke(get(properties.message(12)), stroke);
2022
0
      props.put<BottomBorder>(stroke);
2023
0
    }
2024
313
    if (properties.message(13))
2025
0
    {
2026
0
      IWORKStroke stroke;
2027
0
      readStroke(get(properties.message(13)), stroke);
2028
0
      props.put<LeftBorder>(stroke);
2029
0
    }
2030
313
  }
2031
2032
343
  style = std::make_shared<IWORKStyle>(props, name, parent);
2033
343
}
2034
2035
void IWAParser::parseTableStyle(const unsigned id, IWORKStylePtr_t &style)
2036
244
{
2037
244
  const ObjectMessage msg(*this, id, IWAObjectType::TableStyle);
2038
244
  if (!msg)
2039
141
    return;
2040
2041
103
  optional<string> name;
2042
103
  IWORKStylePtr_t parent;
2043
103
  IWORKPropertyMap props;
2044
2045
103
  using namespace property;
2046
2047
103
  const IWAMessageField &styleInfo = get(msg).message(1);
2048
103
  if (styleInfo)
2049
101
  {
2050
101
    name = styleInfo.string(2).optional();
2051
101
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
2052
101
    if (parentRef)
2053
51
      parent = queryTableStyle(get(parentRef));
2054
101
  }
2055
2056
103
  if (get(msg).message(11))
2057
95
  {
2058
95
    const IWAMessage &properties = get(get(msg).message(11));
2059
2060
95
    if (properties.bool_(1))
2061
50
      props.put<SFTTableBandedRowsProperty>(get(properties.bool_(1)));
2062
95
    if (properties.message(2))
2063
77
    {
2064
77
      IWORKFill fill;
2065
77
      if (readFill(get(properties.message(2)), fill))
2066
68
        props.put<SFTTableBandedCellFillProperty>(fill);
2067
9
      else
2068
9
        props.clear<SFTTableBandedCellFillProperty>();
2069
77
    }
2070
95
    if (properties.bool_(22))
2071
70
      props.put<SFTAutoResizeProperty>(get(properties.bool_(22)));
2072
95
    if (properties.string(41))
2073
37
      props.put<FontName>(get(properties.string(41)));
2074
95
  }
2075
2076
103
  style = std::make_shared<IWORKStyle>(props, name, parent);
2077
103
}
2078
2079
void IWAParser::parseListStyle(const unsigned id, IWORKStylePtr_t &style)
2080
2.22k
{
2081
2.22k
  const ObjectMessage msg(*this, id, IWAObjectType::ListStyle);
2082
2.22k
  if (!msg)
2083
1.38k
    return;
2084
2085
841
  optional<string> name;
2086
841
  IWORKStylePtr_t parent;
2087
2088
841
  using namespace property;
2089
2090
841
  const IWAMessageField &styleInfo = get(msg).message(1);
2091
841
  if (styleInfo)
2092
827
  {
2093
827
    name = styleInfo.string(2).optional();
2094
827
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
2095
827
    if (parentRef)
2096
217
      parent = queryListStyle(get(parentRef));
2097
827
  }
2098
2099
841
  unsigned level = 0;
2100
2101
841
  map<unsigned, IWORKPropertyMap> levelProps;
2102
2103
841
  const IWAUInt32Field &numberFormats = get(msg).uint32(15);
2104
841
  const IWAStringField &bullets = get(msg).string(16);
2105
841
  const IWAMessageField &images = get(msg).message(17);
2106
841
  const IWABoolField &tiered = get(msg).bool_(25);
2107
13.1k
  for (IWAUInt32Field::const_iterator it = get(msg).uint32(11).begin(); it != get(msg).uint32(11).end(); ++it, ++level)
2108
12.2k
  {
2109
12.2k
    switch (*it)
2110
12.2k
    {
2111
4.50k
    default :
2112
4.50k
      ETONYEK_DEBUG_MSG(("parseListStyle: unknown label type %u\n", *it));
2113
4.50k
      ETONYEK_FALLTHROUGH;
2114
9.88k
    case 0 :
2115
      // no label
2116
9.88k
      levelProps[level].put<ListLabelTypeInfo>(true);
2117
9.88k
      break;
2118
416
    case 1 :
2119
416
    {
2120
      // try to find the image, and revert to a default bullet if we find nothing
2121
416
      char const defBullet[]= {char(0xe2), char(0x80), char(0xa2),0};
2122
416
      if (level >= images.size())
2123
340
      {
2124
        // FIXME, in fact, the image is in the parent style...
2125
340
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2126
340
        break;
2127
340
      }
2128
76
      auto ref=readRef(images[level],3);
2129
76
      if (!ref)
2130
20
      {
2131
20
        ETONYEK_DEBUG_MSG(("parseListStyle: can not find image ref for level %u\n", level));
2132
20
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2133
20
        break;
2134
20
      }
2135
56
      const IWORKMediaContentPtr_t content = make_shared<IWORKMediaContent>();
2136
56
      auto stream = queryFile(get(ref));
2137
56
      if (!stream)
2138
56
      {
2139
        // the image is probably in the theme model
2140
56
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2141
56
        break;
2142
56
      }
2143
0
      const IWORKDataPtr_t data = make_shared<IWORKData>();
2144
0
      data->m_stream = stream;
2145
0
      content->m_data = data;
2146
0
      levelProps[level].put<ListLabelTypeInfo>(content);
2147
0
      break;
2148
56
    }
2149
1.15k
    case 2 :
2150
1.15k
      if (level < bullets.size())
2151
624
        levelProps[level].put<ListLabelTypeInfo>(bullets[level]);
2152
1.15k
      break;
2153
843
    case 3 :
2154
843
    {
2155
843
      IWORKTextLabel label;
2156
843
      if (level < numberFormats.size())
2157
316
      {
2158
316
        switch (numberFormats[level] / 3)
2159
316
        {
2160
67
        case 0 :
2161
67
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_NUMERIC;
2162
67
          break;
2163
15
        case 1 :
2164
15
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ROMAN;
2165
15
          break;
2166
30
        case 2 :
2167
30
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ROMAN_LOWERCASE;
2168
30
          break;
2169
137
        case 3 :
2170
137
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ALPHA;
2171
137
          break;
2172
27
        case 4 :
2173
27
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ALPHA_LOWERCASE;
2174
27
          break;
2175
40
        default :
2176
40
          ETONYEK_DEBUG_MSG(("parseListStyle: unknown label number format %u\n", numberFormats[level]));
2177
40
          break;
2178
316
        }
2179
316
        switch (numberFormats[level] % 3)
2180
316
        {
2181
204
        case 0 :
2182
204
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_DOT;
2183
204
          break;
2184
89
        case 1 :
2185
89
          label.m_format.m_prefix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2186
89
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2187
89
          break;
2188
23
        case 2 :
2189
23
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2190
23
          break;
2191
0
        default:
2192
0
          break;
2193
316
        }
2194
316
      }
2195
843
      if (level < tiered.size())
2196
176
        label.m_tiered = tiered[level];
2197
843
      levelProps[level].put<ListLabelTypeInfo>(label);
2198
843
      break;
2199
843
    }
2200
12.2k
    }
2201
12.2k
  }
2202
2203
841
  level = 0;
2204
5.81k
  for (IWAFloatField::const_iterator it = get(msg).float_(12).begin(); it != get(msg).float_(12).end(); ++it, ++level)
2205
4.97k
    levelProps[level].put<ListTextIndent>(*it);
2206
2207
841
  level = 0;
2208
6.05k
  for (IWAFloatField::const_iterator it = get(msg).float_(13).begin(); it != get(msg).float_(13).end(); ++it, ++level)
2209
5.21k
    levelProps[level].put<ListLabelIndent>(*it);
2210
2211
841
  level = 0;
2212
6.46k
  for (IWAMessageField::const_iterator it = get(msg).message(14).begin(); it != get(msg).message(14).end(); ++it, ++level)
2213
5.62k
  {
2214
5.62k
    IWORKListLabelGeometry geometry;
2215
5.62k
    if (it->float_(1))
2216
5.27k
      geometry.m_scale = get(it->float_(1));
2217
5.62k
    levelProps[level].put<ListLabelGeometry>(geometry);
2218
5.62k
  }
2219
2220
841
  if (bool(parent) && parent->has<ListLevelStyles>())
2221
199
  {
2222
199
    const IWORKListLevels_t &parentStyle = parent->get<ListLevelStyles>();
2223
199
    for (const auto &it : parentStyle)
2224
1.93k
      levelProps[it.first].setParent(&it.second->getPropertyMap());
2225
199
  }
2226
2227
841
  IWORKListLevels_t listStyle;
2228
14.6k
  for (map<unsigned, IWORKPropertyMap>::const_iterator it = levelProps.begin(); it != levelProps.end(); ++it)
2229
13.7k
    listStyle[it->first] = std::make_shared<IWORKStyle>(it->second, none, none);
2230
2231
841
  IWORKPropertyMap props;
2232
841
  props.put<ListLevelStyles>(listStyle);
2233
841
  style = std::make_shared<IWORKStyle>(props, name, none);
2234
841
}
2235
2236
void IWAParser::parseCharacterProperties(const IWAMessage &msg, IWORKPropertyMap &props)
2237
1.88k
{
2238
1.88k
  using namespace property;
2239
2240
1.88k
  if (msg.bool_(1))
2241
1.57k
    props.put<Bold>(get(msg.bool_(1)));
2242
1.88k
  if (msg.bool_(2))
2243
1.55k
    props.put<Italic>(get(msg.bool_(2)));
2244
1.88k
  if (msg.float_(3))
2245
1.58k
    props.put<FontSize>(get(msg.float_(3)));
2246
1.88k
  if (msg.string(5))
2247
1.52k
    props.put<FontName>(get(msg.string(5)));
2248
1.88k
  const optional<IWORKColor> &fontColor = readColor(msg, 7);
2249
1.88k
  if (fontColor)
2250
1.31k
    props.put<FontColor>(get(fontColor));
2251
1.88k
  if (msg.uint32(10))
2252
1.42k
    putEnum<Baseline>(props, get(msg.uint32(10)));
2253
1.88k
  if (msg.bool_(11))
2254
1.40k
    props.put<Underline>(get(msg.bool_(11)));
2255
1.88k
  if (msg.bool_(12))
2256
1.40k
    props.put<Strikethru>(get(msg.bool_(12)));
2257
1.88k
  if (msg.uint32(13))
2258
1.40k
    putEnum<Capitalization>(props, get(msg.uint32(13)));
2259
1.88k
  if (msg.float_(14))
2260
1.40k
    props.put<BaselineShift>(get(msg.float_(14)));
2261
1.88k
  if (msg.float_(19)) // CHECKME
2262
1.40k
    props.put<Outline>(get(msg.float_(19))>0);
2263
1.88k
  if (msg.message(21))
2264
226
  {
2265
226
    IWORKShadow shadow;
2266
226
    readShadow(get(msg.message(21)),shadow);
2267
226
    props.put<TextShadow>(shadow);
2268
226
  }
2269
1.88k
  const auto bgColor = readColor(msg, 26);
2270
1.88k
  if (bgColor)
2271
0
    props.put<TextBackground>(get(bgColor));
2272
1.88k
  if (msg.float_(27))
2273
1.38k
    props.put<Tracking>(get(msg.float_(27)));
2274
1.88k
}
2275
2276
void IWAParser::parseColumnsProperties(const IWAMessage &msg, IWORKPropertyMap &props)
2277
0
{
2278
0
  using namespace property;
2279
  // 1,2,3,5 bool
2280
  // 4 float 0
2281
0
  if (msg.message(7))
2282
0
  {
2283
0
    auto columnsMsg=get(msg.message(7));
2284
0
    IWORKColumns columns;
2285
0
    columns.m_columns.clear();
2286
0
    if (columnsMsg.message(1))   // same columns size
2287
0
    {
2288
0
      auto columnDef=get(columnsMsg.message(1));
2289
0
      columns.m_equal=true;
2290
0
      auto n=get_optional_value_or(columnDef.uint32(1).optional(),0);
2291
0
      auto s=get_optional_value_or(columnDef.float_(2).optional(),0.f);
2292
0
      if (n>=1 && n<20)
2293
0
      {
2294
0
        IWORKColumns::Column column;
2295
0
        column.m_width=(1.-(n-1)*double(s))/double(n);
2296
0
        column.m_spacing=s;
2297
0
        columns.m_columns.resize(size_t(n), column);
2298
0
      }
2299
0
    }
2300
0
    else if (columnsMsg.message(2))
2301
0
    {
2302
0
      auto columnsDef=get(columnsMsg.message(2));
2303
0
      columns.m_equal=false;
2304
0
      IWORKColumns::Column column;
2305
0
      column.m_width=get_optional_value_or(columnsDef.float_(1).optional(),0.f);
2306
0
      columns.m_columns.push_back(column);
2307
0
      for (const auto &it : columnsDef.message(2))
2308
0
      {
2309
0
        column.m_spacing=get_optional_value_or(it.float_(1).optional(),0.f);
2310
0
        column.m_width=get_optional_value_or(it.float_(2).optional(),0.f);
2311
0
        columns.m_columns.push_back(column);
2312
0
      }
2313
0
    }
2314
0
    if (!columns.m_columns.empty())
2315
0
      props.put<property::Columns>(columns);
2316
0
  }
2317
0
}
2318
2319
void IWAParser::parsePageMaster(unsigned id, PageMaster &pageMaster)
2320
0
{
2321
0
  const ObjectMessage msg(*this, id, IWAObjectType::PageMaster);
2322
0
  if (!msg)
2323
0
    return;
2324
0
  if (get(msg).bool_(17))
2325
0
    pageMaster.m_headerFootersSameAsPrevious=get(get(msg).bool_(17));
2326
0
  bool hideHeaderOnFirstPage=false;
2327
0
  if (get(msg).bool_(28))
2328
0
    hideHeaderOnFirstPage=get(get(msg).bool_(28));
2329
  // 18-22: some bool ?
2330
0
  IWORKPropertyMap props;
2331
0
  for (unsigned i=0; i<3; ++i)
2332
0
  {
2333
0
    auto hfRef=readRef(get(msg),23+i);
2334
0
    if (!hfRef) continue;
2335
0
    IWORKPageMaster pMaster;
2336
0
    parseHeaderAndFooter(get(hfRef), pMaster);
2337
0
    if (pMaster.m_header.empty() && pMaster.m_footer.empty())
2338
0
      continue;
2339
    // only the last pagemaster seem used...
2340
0
    if (i!=2) continue;
2341
0
    props.put<property::OddPageMaster>(pMaster);
2342
0
    props.put<property::EvenPageMaster>(pMaster);
2343
0
    if (!hideHeaderOnFirstPage)
2344
0
      props.put<property::FirstPageMaster>(pMaster);
2345
0
  }
2346
  // readRef(get(msg),30); type 10016, field 1 a bool, [field 2] a ref to type 3047
2347
0
  const IWAMessageField &background = get(msg).message(30);
2348
0
  if (background)
2349
0
  {
2350
0
    IWORKFill fill;
2351
0
    if (readFill(get(background), fill))
2352
0
      props.put<property::Fill>(fill);
2353
0
  }
2354
0
  pageMaster.m_style = std::make_shared<IWORKStyle>(props, none, none);
2355
0
}
2356
2357
void IWAParser::parseHeaderAndFooter(unsigned id, IWORKPageMaster &hf)
2358
0
{
2359
0
  const ObjectMessage msg(*this, id, IWAObjectType::HeadersAndFooters);
2360
0
  if (!msg)
2361
0
    return;
2362
0
  for (size_t wh=0; wh<2; ++wh)
2363
0
  {
2364
0
    bool empty=true;
2365
0
    std::stringstream name;
2366
0
    name << (wh==0 ? "PMHeader" : "PMFooter") << id;
2367
0
    for (auto it = get(msg).message(wh+1).begin(); it != get(msg).message(wh+1).end(); ++it)
2368
0
    {
2369
0
      auto ref=it->uint32(1).optional();
2370
0
      if (!ref) continue;
2371
0
      auto currentText=m_currentText;
2372
0
      m_currentText = m_collector.createText(m_langManager, true);
2373
0
      parseText(get(ref));
2374
0
      if (!m_currentText->empty())
2375
0
      {
2376
0
        m_collector.collectText(m_currentText);
2377
0
        if (wh==0)
2378
0
          m_collector.collectHeader(name.str());
2379
0
        else
2380
0
          m_collector.collectFooter(name.str());
2381
0
        empty=false;
2382
0
      }
2383
0
      m_currentText=currentText;
2384
0
    }
2385
0
    if (empty) continue;
2386
0
    if (wh==0)
2387
0
      hf.m_header=name.str();
2388
0
    else
2389
0
      hf.m_footer=name.str();
2390
0
  }
2391
2392
0
}
2393
2394
bool IWAParser::parseImage(const IWAMessage &msg)
2395
533
{
2396
533
  m_collector.startLevel();
2397
533
  IWORKGeometryPtr_t geometry;
2398
533
  if (msg.message(1))
2399
392
  {
2400
392
    boost::optional<unsigned> flags;
2401
392
    parseShapePlacement(get(msg.message(1)), geometry, flags);
2402
392
    m_collector.collectGeometry(geometry);
2403
392
  }
2404
2405
533
  const optional<unsigned> styleRef = readRef(msg, 3);
2406
533
  if (styleRef)
2407
459
    m_collector.setGraphicStyle(queryMediaStyle(get(styleRef)));
2408
2409
533
  IWORKGeometryPtr_t cropGeometry;
2410
533
  const optional<unsigned> cropRef = readRef(msg, 5);
2411
533
  if (cropRef)
2412
219
  {
2413
219
    IWORKPathPtr_t path;
2414
219
    parseMask(get(cropRef), cropGeometry, path);
2415
219
  }
2416
533
  if (cropGeometry && geometry)
2417
126
  {
2418
    // CHANGEME: collector suppose that cropGeometry position is the
2419
    //   final position but mask is relative to the original picture
2420
126
    cropGeometry->m_position.m_x += geometry->m_position.m_x;
2421
126
    cropGeometry->m_position.m_y += geometry->m_position.m_y;
2422
126
  }
2423
2424
533
  const IWORKMediaContentPtr_t content = make_shared<IWORKMediaContent>();
2425
  // 15: filtered, 11: basic image?, 12: small image, 13: ?
2426
533
  unsigned const ids[]= {15, 13, 11, 12};
2427
533
  for (auto id : ids)
2428
1.95k
  {
2429
1.95k
    auto const &ref=readRef(msg, id);
2430
1.95k
    if (!ref) continue;
2431
894
    auto stream = queryFile(get(ref));
2432
894
    if (!stream) continue;
2433
120
    const IWORKDataPtr_t data = make_shared<IWORKData>();
2434
120
    data->m_stream = stream;
2435
120
    content->m_data = data;
2436
120
    break;
2437
894
  }
2438
533
  content->m_size = readSize(msg, 9);
2439
533
  if (!content->m_size)
2440
57
    content->m_size = readSize(msg, 4);
2441
2442
533
  m_collector.collectMedia(content, cropGeometry);
2443
533
  m_collector.endLevel();
2444
2445
533
  return true;
2446
533
}
2447
2448
void IWAParser::parseAuthorInComment(unsigned id)
2449
52
{
2450
52
  assert(bool(m_currentText));
2451
52
  const ObjectMessage msg(*this, id, IWAObjectType::AuthorStorage);
2452
52
  if (!msg)
2453
17
    return;
2454
35
  if (get(msg).string(1))
2455
24
  {
2456
24
    auto str=get(get(msg).string(1));
2457
24
    auto len=str.size();
2458
24
    if (len==0) return;
2459
23
    IWAText text(str+"\t", m_langManager);
2460
23
    std::map<unsigned, IWORKStylePtr_t> spans;
2461
23
    IWORKPropertyMap props;
2462
    // normally yellow, but blue may be better in LO
2463
23
    props.put<property::FontColor>(IWORKColor(0,0,1,1));
2464
23
    spans[0]=std::make_shared<IWORKStyle>(props, boost::none, IWORKStylePtr_t());
2465
    // reset color to default, if not, comment will be blue colored
2466
23
    props.put<property::FontColor>(IWORKColor(0,0,0,1));
2467
23
    spans[unsigned(len)]=std::make_shared<IWORKStyle>(props, boost::none, IWORKStylePtr_t());
2468
23
    text.setSpans(spans);
2469
23
    text.parse(*m_currentText);
2470
23
  }
2471
35
}
2472
2473
void IWAParser::parseComment(const unsigned id)
2474
111
{
2475
111
  assert(bool(m_currentText));
2476
2477
111
  unsigned actId=id;
2478
111
  std::set<unsigned> seens;
2479
163
  while (1)
2480
155
  {
2481
155
    if (seens.find(actId)!=seens.end())
2482
3
    {
2483
3
      ETONYEK_DEBUG_MSG(("IWAParser::parseComment: find a loop\n"));
2484
3
      break;
2485
3
    }
2486
152
    seens.insert(actId);
2487
152
    const ObjectMessage msg(*this, actId, IWAObjectType::Comment);
2488
152
    if (!msg)
2489
43
      return;
2490
109
    auto authorRef=readRef(get(msg), 3);
2491
109
    if (authorRef)
2492
52
      parseAuthorInComment(*authorRef);
2493
    // TODO add date which is in position 2
2494
109
    if (get(msg).string(1))
2495
80
    {
2496
80
      IWAText text(get(get(msg).string(1)), m_langManager);
2497
80
      text.parse(*m_currentText);
2498
80
    }
2499
2500
109
    auto nextRef=readRef(get(msg), 4);
2501
109
    if (!nextRef) break;
2502
52
    actId=*nextRef;
2503
52
  }
2504
111
}
2505
2506
bool IWAParser::parseTabularInfo(const IWAMessage &msg)
2507
203
{
2508
203
  m_collector.startLevel();
2509
203
  if (msg.message(1))
2510
201
    parseShapePlacement(get(msg.message(1)));
2511
203
  const optional<unsigned> &modelRef = readRef(msg, 2);
2512
203
  if (modelRef)
2513
200
    parseTabularModel(get(modelRef));
2514
203
  m_collector.endLevel();
2515
203
  return bool(modelRef);
2516
203
}
2517
2518
void IWAParser::parseTabularModel(const unsigned id)
2519
200
{
2520
200
  const ObjectMessage msg(*this, id, IWAObjectType::TabularModel);
2521
200
  if (!msg)
2522
2
    return;
2523
198
  const IWAUInt32Field &rows = get(msg).uint32(6);
2524
198
  const IWAUInt32Field &columns = get(msg).uint32(7);
2525
198
  if (!rows || !columns)
2526
2
    return;
2527
2528
196
  m_currentTable = std::make_shared<TableInfo>(m_collector.createTable(m_tableNameMap, m_formatNameMap, m_langManager), get(columns), get(rows));
2529
196
  m_currentTable->m_table->setSize(get(columns), get(rows));
2530
2531
196
  IWORKStylePtr_t tableStyle;
2532
196
  const optional<unsigned> tableStyleRef = readRef(get(msg), 3);
2533
196
  if (tableStyleRef)
2534
193
    tableStyle = queryTableStyle(get(tableStyleRef));
2535
196
  if (bool(tableStyle))
2536
52
  {
2537
52
    m_currentTable->m_style = tableStyle;
2538
52
    m_currentTable->m_table->setStyle(tableStyle);
2539
2540
52
    if (tableStyle->has<property::SFTTableBandedCellFillProperty>())
2541
41
    {
2542
41
      IWORKPropertyMap props;
2543
41
      props.put<property::Fill>(tableStyle->get<property::SFTTableBandedCellFillProperty>());
2544
41
      m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ALTERNATE_BODY, make_shared<IWORKStyle>(props, none, none));
2545
41
    }
2546
52
  }
2547
2548
196
  std::map<unsigned,unsigned> idToTileRefMap;
2549
196
  std::map<unsigned,unsigned> idToTileDecalRowMap;
2550
2551
196
  if (get(msg).message(4))
2552
192
  {
2553
192
    const IWAMessage &grid = get(get(msg).message(4));
2554
2555
192
    if (grid.message(1))
2556
190
    {
2557
190
      const optional<unsigned> &rowHeadersRef = readRef(get(grid.message(1)), 2);
2558
190
      if (rowHeadersRef)
2559
186
        parseTableHeaders(get(rowHeadersRef), m_currentTable->m_rowHeader);
2560
190
    }
2561
192
    const optional<unsigned> &columnHeadersRef = readRef(grid, 2);
2562
192
    if (columnHeadersRef)
2563
184
      parseTableHeaders(get(columnHeadersRef), m_currentTable->m_columnHeader);
2564
2565
192
    const optional<unsigned> &simpleTextListRef = readRef(grid, 4);
2566
192
    if (simpleTextListRef)
2567
183
      parseDataList(get(simpleTextListRef), m_currentTable->m_simpleTextList);
2568
192
    const optional<unsigned> &cellStyleListRef = readRef(grid, 5);
2569
192
    if (cellStyleListRef)
2570
174
      parseDataList(get(cellStyleListRef), m_currentTable->m_cellStyleList);
2571
192
    const optional<unsigned> &formulaListRef = readRef(grid, 6);
2572
192
    if (formulaListRef)
2573
169
      parseDataList(get(formulaListRef), m_currentTable->m_formulaList);
2574
192
    const optional<unsigned> &formatListRef = readRef(grid, 11);
2575
192
    if (formatListRef)
2576
169
      parseDataList(get(formatListRef), m_currentTable->m_formatList);
2577
192
    const optional<unsigned> &paraTextListRef = readRef(grid, 17);
2578
192
    if (paraTextListRef)
2579
165
      parseDataList(get(paraTextListRef), m_currentTable->m_formattedTextList);
2580
192
    const optional<unsigned> &conditionStyleListRef = readRef(grid, 18);
2581
192
    if (conditionStyleListRef)
2582
164
    {
2583
164
      DataList_t conditionStyles;
2584
164
      parseDataList(get(conditionStyleListRef), conditionStyles);
2585
164
      for (auto const &it : conditionStyles)
2586
0
      {
2587
0
        if (auto ref = *boost::get<unsigned>(&it.second))
2588
0
        {
2589
0
          ConditionRule_t rules;
2590
0
          if (parseConditionRules(ref, rules))
2591
0
            m_currentTable->m_conditionStyleList[it.first]=rules;
2592
0
        }
2593
0
      }
2594
164
    }
2595
192
    const optional<unsigned> &commentListRef = readRef(grid, 19);
2596
192
    if (commentListRef)
2597
162
      parseDataList(get(commentListRef), m_currentTable->m_commentList);
2598
192
    const optional<unsigned> &newFormatListRef = readRef(grid, 22);
2599
192
    if (newFormatListRef)
2600
81
      parseDataList(get(newFormatListRef), m_currentTable->m_newFormatList);
2601
2602
192
    m_currentTable->m_table->setSizes(makeSizes(m_currentTable->m_columnHeader.m_sizes), makeSizes(m_currentTable->m_rowHeader.m_sizes));
2603
2604
192
    if (grid.message(3))
2605
167
    {
2606
167
      for (auto const &it : grid.message(3).message(1))
2607
167
      {
2608
167
        auto tileRef = readRef(it, 2);
2609
167
        if (!it.uint32(1) || !bool(tileRef))
2610
8
        {
2611
8
          ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some tile ref\n"));
2612
8
          continue;
2613
8
        }
2614
159
        idToTileRefMap[get(it.uint32(1))]=get(tileRef);
2615
159
      }
2616
167
    }
2617
192
    if (grid.message(9))
2618
164
    {
2619
164
      for (auto const &it : grid.message(9).message(1))
2620
164
      {
2621
164
        if (!it.uint32(1) || !it.uint32(2))
2622
19
        {
2623
19
          ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some grid decal data\n"));
2624
19
          continue;
2625
19
        }
2626
145
        idToTileDecalRowMap[get(it.uint32(2))]=get(it.uint32(1));
2627
145
      }
2628
164
    }
2629
192
  }
2630
196
  if (get(msg).string(8))
2631
169
  {
2632
169
    std::string finalName;
2633
169
    if (bool(m_collector.getWorkSpaceName()))
2634
0
    {
2635
0
      std::stringstream s;
2636
0
      s << get(m_collector.getWorkSpaceName()) << "_" << get(get(msg).string(8));
2637
0
      finalName=s.str();
2638
0
    }
2639
169
    else
2640
169
      finalName=get(get(msg).string(8));
2641
169
    if (m_tableNameMap->find(finalName)!=m_tableNameMap->end())
2642
0
    {
2643
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: a table with name %s already exists\n", finalName.c_str()));
2644
      // let create an unique name
2645
0
      int nId=0;
2646
0
      while (true)
2647
0
      {
2648
0
        std::stringstream s;
2649
0
        s << finalName << "_" << ++nId;
2650
0
        if (m_tableNameMap->find(s.str())!=m_tableNameMap->end()) continue;
2651
0
        finalName=s.str();
2652
0
        break;
2653
0
      }
2654
0
    }
2655
169
    (*m_tableNameMap)[finalName]=finalName;
2656
169
    if (get(msg).string(1))
2657
168
      (*m_tableNameMap)[std::string("SFTGlobalID_")+get(get(msg).string(1))] = finalName;
2658
169
    m_currentTable->m_table->setName(finalName);
2659
169
  }
2660
196
  m_currentTable->m_table->setHeaders(
2661
196
    get_optional_value_or(get(msg).uint32(10).optional(), 0),
2662
196
    get_optional_value_or(get(msg).uint32(9).optional(), 0),
2663
196
    get_optional_value_or(get(msg).uint32(11).optional(), 0)
2664
196
  );
2665
196
  m_currentTable->m_table->setRepeated(
2666
196
    get_optional_value_or(get(msg).bool_(13).optional(), false),
2667
196
    get_optional_value_or(get(msg).bool_(12).optional(), false)
2668
196
  );
2669
196
  if (bool(tableStyle) && tableStyle->has<property::SFTTableBandedRowsProperty>())
2670
37
    m_currentTable->m_table->setBandedRows(tableStyle->get<property::SFTTableBandedRowsProperty>());
2671
2672
  // default cell styles
2673
196
  optional<unsigned> styleRef = readRef(get(msg), 18);
2674
196
  if (styleRef)
2675
160
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_BODY, queryCellStyle(get(styleRef)));
2676
196
  styleRef = readRef(get(msg), 19);
2677
196
  if (styleRef)
2678
159
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ROW_HEADER, queryCellStyle(get(styleRef)));
2679
196
  styleRef = readRef(get(msg), 20);
2680
196
  if (styleRef)
2681
156
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_COLUMN_HEADER, queryCellStyle(get(styleRef)));
2682
196
  styleRef = readRef(get(msg), 21);
2683
196
  if (styleRef)
2684
135
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ROW_FOOTER, queryCellStyle(get(styleRef)));
2685
2686
  // default para styles
2687
196
  styleRef = readRef(get(msg), 24);
2688
196
  if (styleRef)
2689
135
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_BODY, queryParagraphStyle(get(styleRef)));
2690
196
  styleRef = readRef(get(msg), 25);
2691
196
  if (styleRef)
2692
148
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_ROW_HEADER, queryParagraphStyle(get(styleRef)));
2693
196
  styleRef = readRef(get(msg), 26);
2694
196
  if (styleRef)
2695
147
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_COLUMN_HEADER, queryParagraphStyle(get(styleRef)));
2696
196
  styleRef = readRef(get(msg), 27);
2697
196
  if (styleRef)
2698
146
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_ROW_FOOTER, queryParagraphStyle(get(styleRef)));
2699
2700
196
  styleRef = readRef(get(msg), 49);
2701
196
  if (styleRef)
2702
144
  {
2703
144
    IWORKGridLineMap_t gridLines[4];
2704
144
    parseTableGridLines(get(styleRef), gridLines);
2705
144
    m_currentTable->m_table->setBorders(gridLines[0],gridLines[1],gridLines[2],gridLines[3]);
2706
144
  }
2707
2708
  // handle tables
2709
196
  for (auto const &tileIt : idToTileRefMap)
2710
146
  {
2711
146
    auto const &decalIt=idToTileDecalRowMap.find(tileIt.first);
2712
146
    if (decalIt==idToTileDecalRowMap.end())
2713
21
    {
2714
21
      ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some decal for id=%x, assume 0\n", tileIt.first));
2715
21
      parseTile(tileIt.second, 0);
2716
21
    }
2717
125
    else
2718
125
      parseTile(tileIt.second, decalIt->second);
2719
146
  }
2720
196
  m_collector.collectTable(m_currentTable->m_table);
2721
196
  m_currentTable.reset();
2722
196
}
2723
2724
void IWAParser::parseDataList(const unsigned id, DataList_t &dataList)
2725
1.26k
{
2726
1.26k
  const ObjectMessage msg(*this, id, IWAObjectType::DataList);
2727
1.26k
  if (!msg)
2728
256
    return;
2729
2730
  // TODO: it would likely to be more robust to parse everything.
2731
1.01k
  if (!get(msg).uint32(1))
2732
1
    return;
2733
2734
1.01k
  const unsigned type = get(get(msg).uint32(1));
2735
1.01k
  for (const auto &it : get(msg).message(3))
2736
481
  {
2737
481
    if (!it.uint32(1))
2738
8
      continue;
2739
473
    const unsigned index = get(it.uint32(1));
2740
473
    switch (type)
2741
473
    {
2742
105
    case 1 :
2743
105
      if (it.string(3))
2744
100
        dataList[index] = get(it.string(3));
2745
105
      break;
2746
109
    case 2 :
2747
      // it.uint32(2): some type
2748
109
      if (it.message(6))
2749
103
      {
2750
103
        Format format;
2751
103
        if (parseFormat(get(it.message(6)), format))
2752
101
          dataList[index]=format;
2753
103
      }
2754
6
      else
2755
6
      {
2756
6
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the format\n"));
2757
6
      }
2758
109
      break;
2759
2
    case 3 :
2760
2
    case 5 : // invalid formula
2761
2
      if (it.message(5))
2762
0
      {
2763
0
        IWORKFormulaPtr_t formula;
2764
0
        if (parseFormula(get(it.message(5)), formula) && formula)
2765
0
          dataList[index]=formula;
2766
0
      }
2767
2
      else
2768
2
      {
2769
2
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the formula\n"));
2770
2
      }
2771
2
      break;
2772
249
    case 4 :
2773
249
    {
2774
249
      auto styleRef=readRef(it,4);
2775
249
      if (styleRef)
2776
237
        dataList[index]=get(styleRef);
2777
12
      else if (it.uint32(4))
2778
0
        dataList[index] = get(it.uint32(4));
2779
249
      break;
2780
2
    }
2781
0
    case 8 :   // paragraph ref
2782
0
    {
2783
0
      auto textRef=readRef(it,9);
2784
0
      if (textRef)
2785
0
        dataList[index]=get(textRef);
2786
0
      else
2787
0
      {
2788
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the para ref\n"));
2789
0
      }
2790
0
      break;
2791
2
    }
2792
0
    case 9 :   // condition
2793
0
    {
2794
0
      auto condRef=readRef(it,4);
2795
0
      if (condRef)
2796
0
        dataList[index] = get(condRef);
2797
0
      break;
2798
2
    }
2799
0
    case 10 :
2800
0
    {
2801
0
      auto commentRef=readRef(it,10);
2802
0
      if (commentRef)
2803
0
        dataList[index]=get(commentRef);
2804
0
      else
2805
0
      {
2806
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the comment ref\n"));
2807
0
      }
2808
0
      break;
2809
2
    }
2810
8
    default :
2811
8
      ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: unknown data list type %u\n", type));
2812
8
      break;
2813
473
    }
2814
473
  }
2815
1.01k
}
2816
2817
void IWAParser::parseTileDefinition(unsigned row, unsigned column, RVNGInputStreamPtr_t &input, unsigned endPos, bool oldFormat)
2818
756
{
2819
756
  IWORKCellType cellType = IWORK_CELL_TYPE_TEXT;
2820
756
  optional<unsigned> cellStyleId, formatId, paragraphStyleId;
2821
756
  optional<unsigned> commentId, conditionId, formulaId, textId, textFormattedId;
2822
756
  optional<string> text;
2823
756
  bool numberSet=false;
2824
2825
756
  auto begPos=input->tell();
2826
756
  if (begPos+(oldFormat ? 10 : 12)>long(endPos))
2827
510
  {
2828
510
    ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: the zone seems too short\n"));
2829
510
    return;
2830
510
  }
2831
  // 1. Read the cell record
2832
  // NOTE: The structure of the record is still not completely understood,
2833
  // so we catch possible over-reading exceptions and continue.
2834
246
  try
2835
246
  {
2836
    // 0: 4?
2837
246
    input->seek((long) begPos+1, librevenge::RVNG_SEEK_SET);
2838
246
    auto type=readU8(input);
2839
246
    switch (type)
2840
246
    {
2841
5
    case 2:
2842
5
    case 8: // nan
2843
5
    case 10: // devise
2844
5
      cellType=IWORK_CELL_TYPE_NUMBER;
2845
5
      break;
2846
4
    case 7: // duration
2847
4
      cellType=IWORK_CELL_TYPE_DURATION;
2848
4
      break;
2849
162
    case 0: // empty (ok)
2850
228
    case 3: // text (ok)
2851
228
    case 9: // text zone
2852
228
      break;
2853
0
    case 5:
2854
0
      cellType=IWORK_CELL_TYPE_DATE_TIME;
2855
0
      break;
2856
0
    case 6: // other: bool, button, menu
2857
0
      cellType=IWORK_CELL_TYPE_BOOL;
2858
0
      break;
2859
9
    default:
2860
9
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: unknown type %d\n", int(type)));
2861
9
      break;
2862
246
    }
2863
246
    if (oldFormat)
2864
150
    {
2865
      // 2,3: ?
2866
150
      input->seek((long) begPos + 4, librevenge::RVNG_SEEK_SET);
2867
150
      const unsigned flags = readU16(input);
2868
150
      input->seek(6, librevenge::RVNG_SEEK_CUR);
2869
150
      if (flags & 0x2) // cell style
2870
126
        cellStyleId = readU32(input);
2871
150
      if (flags & 0x80)
2872
0
        paragraphStyleId=readU32(input);
2873
150
      if (flags & 0x800) // condition
2874
0
        conditionId=readU32(input);
2875
150
      if (flags & 0x400) // condition 2
2876
0
        readU32(input);
2877
150
      if (flags & 0x4)   // format
2878
15
        formatId=readU32(input);
2879
150
      if (flags & 0x8) // formula
2880
3
        formulaId = readU32(input);
2881
150
      if (flags & 0x1000) // comment
2882
0
        commentId=readU32(input);
2883
150
      if (flags & 0x10) // simple text
2884
12
        textId = readU32(input);
2885
150
      if (flags & 0x20) // number or duration(in second)
2886
0
      {
2887
0
        std::stringstream s;
2888
0
        s << std::setprecision(12) << readDouble(input);
2889
0
        text=s.str();
2890
0
        numberSet=true;
2891
0
      }
2892
150
      if (flags & 0x40) // date
2893
0
      {
2894
0
        std::stringstream s;
2895
0
        s << std::setprecision(12) << readDouble(input);
2896
0
        text=s.str();
2897
0
        numberSet=true;
2898
0
      }
2899
150
      if (flags & 0x200) // formatted text
2900
0
        textFormattedId = readU32(input);
2901
150
    }
2902
96
    else
2903
96
    {
2904
      // 2-7?
2905
96
      input->seek((long) begPos + 8, librevenge::RVNG_SEEK_SET);
2906
96
      const unsigned flags = readU32(input);
2907
96
      if (flags & 1)
2908
0
      {
2909
        // significand 105 bits, exponent (base 10) 14 bits, sign 1 bits
2910
0
        long double mantissa=0;
2911
0
        long double decal=1;
2912
0
        for (int i=0; i<7; ++i)
2913
0
        {
2914
0
          mantissa+=decal*double(readU16(input));
2915
0
          decal*=65536;
2916
0
        }
2917
0
        auto exponent=readU16(input);
2918
0
        if (exponent&1)
2919
0
        {
2920
0
          mantissa+=decal;
2921
0
          exponent&=0xfffe; // need if exponent<12352
2922
0
        }
2923
0
        if (exponent&0x8000)
2924
0
        {
2925
0
          mantissa*=-1;
2926
0
          exponent&=0x7fff;
2927
0
        }
2928
0
        std::stringstream s;
2929
0
        s << std::setprecision(12) << mantissa *std::pow(10, (exponent-12352)/2); // 3040 mean 0
2930
0
        text=s.str();
2931
0
        numberSet=true;
2932
0
      }
2933
96
      if (flags & 2)   // bool
2934
0
      {
2935
0
        std::stringstream s;
2936
0
        s << readDouble(input);
2937
0
        text=s.str();
2938
0
        numberSet=true;
2939
0
      }
2940
96
      if (flags & 4)   // date
2941
0
      {
2942
0
        std::stringstream s;
2943
0
        s << std::setprecision(12) << readDouble(input);
2944
0
        text=s.str();
2945
0
        numberSet=true;
2946
0
      }
2947
96
      if (flags & 8)
2948
60
        textId = readU32(input);
2949
96
      if (flags & 0x10)
2950
2
        textFormattedId=readU32(input);
2951
96
      if (flags & 0x20) // cell style
2952
31
        cellStyleId = readU32(input);
2953
96
      if (flags & 0x40) // cell paragraph style
2954
0
        paragraphStyleId=readU32(input);
2955
96
      if (flags & 0x80) // conditional
2956
0
        conditionId=readU32(input);
2957
96
      if (flags & 0x100) // conditional(unknown)
2958
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2959
96
      if (flags & 0x200)
2960
2
        formulaId = readU32(input);
2961
96
      if (flags & 0x400) // button menu
2962
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2963
96
      if (flags & 0x800) // unknown: check size
2964
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2965
96
      unsigned resType=0;
2966
96
      if (flags & 0x1000)   // type of the result
2967
60
      {
2968
60
        resType=readU32(input);
2969
60
        switch (resType)
2970
60
        {
2971
0
        case 1:
2972
0
          cellType=IWORK_CELL_TYPE_NUMBER;
2973
0
          break;
2974
0
        case 2: // devise(changeme)
2975
0
          cellType=IWORK_CELL_TYPE_NUMBER;
2976
0
          break;
2977
0
        case 3:
2978
0
          cellType=IWORK_CELL_TYPE_DATE_TIME;
2979
0
          break;
2980
0
        case 4:
2981
0
          cellType=IWORK_CELL_TYPE_DURATION;
2982
0
          break;
2983
57
        case 5:
2984
57
          cellType=IWORK_CELL_TYPE_TEXT;
2985
57
          break;
2986
0
        case 6: // other
2987
0
          break;
2988
3
        default:
2989
3
          ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: unknown type %d\n", int(resType)));
2990
3
          break;
2991
60
        }
2992
60
      }
2993
669
      for (unsigned w=0, hBytes=0x2000; w<6; ++w, hBytes<<=1)
2994
573
      {
2995
573
        if ((flags & hBytes)==0)
2996
507
          continue;
2997
66
        const unsigned id=readU32(input);
2998
        // checkme, unclear which format id we need to choose when resType=2 or 6
2999
66
        if (w+1!=resType)
3000
6
          continue;
3001
60
        formatId=id;
3002
60
      }
3003
96
      if (flags & 0x80000)
3004
0
        commentId=readU32(input);
3005
96
    }
3006
246
  }
3007
246
  catch (...)
3008
246
  {
3009
    // ignore failure to read the last record
3010
15
  }
3011
3012
246
  IWORKFormulaPtr_t formula;
3013
246
  if (bool(formulaId))
3014
5
  {
3015
5
    auto const formulaIt = m_currentTable->m_formulaList.find(get(formulaId));
3016
5
    if (formulaIt !=m_currentTable->m_formulaList.end())
3017
0
    {
3018
0
      if (auto ref = boost::get<IWORKFormulaPtr_t>(&formulaIt->second))
3019
0
        formula=*ref;
3020
0
    }
3021
5
    else
3022
5
    {
3023
5
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: can not find formula %d\n", int(get(formulaId))));
3024
5
    }
3025
5
  }
3026
246
  if (numberSet && cellType == IWORK_CELL_TYPE_TEXT)
3027
0
    cellType = IWORK_CELL_TYPE_NUMBER;
3028
246
  bool textSet=false;
3029
246
  if (bool(textId))
3030
66
  {
3031
66
    const DataList_t::const_iterator listIt = m_currentTable->m_simpleTextList.find(get(textId));
3032
66
    if (listIt != m_currentTable->m_simpleTextList.end())
3033
43
    {
3034
43
      if (const string *const s = boost::get<string>(&listIt->second))
3035
43
      {
3036
43
        cellType = IWORK_CELL_TYPE_TEXT;
3037
43
        text = *s;
3038
43
        textSet=true;
3039
43
      }
3040
43
    }
3041
23
    else
3042
23
    {
3043
23
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: can not find text %d\n", int(get(textId))));
3044
23
    }
3045
66
  }
3046
246
  optional<unsigned> textRef;
3047
246
  if (bool(textFormattedId))
3048
2
  {
3049
2
    const DataList_t::const_iterator listIt = m_currentTable->m_formattedTextList.find(get(textFormattedId));
3050
2
    if (listIt != m_currentTable->m_formattedTextList.end())
3051
0
    {
3052
0
      if (const unsigned *const ref = boost::get<unsigned>(&listIt->second))
3053
0
      {
3054
0
        cellType = IWORK_CELL_TYPE_TEXT;
3055
0
        textRef = *ref;
3056
0
        textSet=true;
3057
0
      }
3058
0
    }
3059
2
    else
3060
2
    {
3061
2
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: can not find formatted text %d\n", int(get(textFormattedId))));
3062
2
    }
3063
2
  }
3064
3065
246
  IWORKStylePtr_t cellStyle;
3066
246
  if (bool(cellStyleId))
3067
157
  {
3068
157
    const DataList_t::const_iterator listIt = m_currentTable->m_cellStyleList.find(get(cellStyleId));
3069
157
    if (listIt != m_currentTable->m_cellStyleList.end())
3070
125
    {
3071
125
      if (const unsigned *const ref = boost::get<unsigned>(&listIt->second))
3072
125
        cellStyle = queryCellStyle(*ref);
3073
125
    }
3074
157
  }
3075
246
  IWORKStylePtr_t paragraphStyle;
3076
246
  if (bool(paragraphStyleId))
3077
0
  {
3078
0
    const DataList_t::const_iterator listIt = m_currentTable->m_cellStyleList.find(paragraphStyleId.get());
3079
0
    if (listIt != m_currentTable->m_cellStyleList.end())
3080
0
    {
3081
0
      if (const unsigned *const ref = boost::get<unsigned>(&listIt->second))
3082
0
        paragraphStyle = queryParagraphStyle(*ref);
3083
0
    }
3084
0
  }
3085
246
  optional<Format> format;
3086
246
  if (bool(formatId))
3087
66
  {
3088
66
    auto const &formatList=oldFormat ? m_currentTable->m_formatList : m_currentTable->m_newFormatList;
3089
66
    auto const formatIt=formatList.find(get(formatId));
3090
66
    if (formatIt != formatList.end())
3091
49
    {
3092
49
      if (auto ref = boost::get<Format>(&formatIt->second))
3093
49
      {
3094
49
        format=*ref;
3095
49
        if (format->m_type && get(format->m_type)==IWORK_CELL_TYPE_NUMBER && cellType!=IWORK_CELL_TYPE_TEXT)
3096
0
          format->m_type=cellType;
3097
49
      }
3098
49
    }
3099
17
    else
3100
17
    {
3101
17
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: can not find format %d\n", int(get(formatId))));
3102
17
    }
3103
66
  }
3104
246
  IWORKPropertyMap props;
3105
246
  bool addPropsToCellStyle=false;
3106
246
  if (format && !textSet)
3107
18
  {
3108
18
    if (get(format).m_type)
3109
18
    {
3110
18
      auto type=get(get(format).m_type);
3111
18
      if (!numberSet || (type!=IWORK_CELL_TYPE_TEXT && type!=IWORK_CELL_TYPE_NUMBER)) cellType=type;
3112
18
    }
3113
18
    addPropsToCellStyle=true;
3114
18
    if (cellType==IWORK_CELL_TYPE_DATE_TIME && boost::get<IWORKDateTimeFormat>(&get(format).m_format))
3115
0
      props.put<property::SFTCellStylePropertyDateTimeFormat>(*boost::get<IWORKDateTimeFormat>(&get(format).m_format));
3116
18
    else if (cellType==IWORK_CELL_TYPE_DURATION && boost::get<IWORKDurationFormat>(&get(format).m_format))
3117
0
      props.put<property::SFTCellStylePropertyDurationFormat>(*boost::get<IWORKDurationFormat>(&get(format).m_format));
3118
18
    else if (cellType!=IWORK_CELL_TYPE_TEXT && boost::get<IWORKNumberFormat>(&get(format).m_format))
3119
0
      props.put<property::SFTCellStylePropertyNumberFormat>(*boost::get<IWORKNumberFormat>(&get(format).m_format));
3120
18
    else
3121
18
      addPropsToCellStyle=false;
3122
18
  }
3123
3124
246
  bool needText=bool(textRef) || (bool(text) && !formula && cellType == IWORK_CELL_TYPE_TEXT);
3125
246
  if (needText)
3126
43
  {
3127
43
    assert(!m_currentText);
3128
43
    m_currentText = m_collector.createText(m_langManager);
3129
43
    if (bool(textRef))
3130
0
      parseText(get(textRef));
3131
43
    else
3132
43
    {
3133
43
      m_currentText = m_collector.createText(m_langManager);
3134
      // update the style
3135
43
      m_currentText->pushBaseLayoutStyle(m_currentTable->m_table->getDefaultLayoutStyle(column, row));
3136
43
      m_currentText->pushBaseParagraphStyle(m_currentTable->m_table->getDefaultParagraphStyle(column, row));
3137
43
      if (paragraphStyle)
3138
0
        m_currentText->setParagraphStyle(paragraphStyle);
3139
43
      m_currentText->insertText(get(text));
3140
43
      m_currentText->flushSpan();
3141
43
      m_currentText->flushParagraph();
3142
43
    }
3143
43
  }
3144
246
  if (!needText && paragraphStyle)
3145
0
  {
3146
0
    addPropsToCellStyle=true;
3147
0
    props.put<property::SFTCellStylePropertyParagraphStyle>(paragraphStyle);
3148
0
  }
3149
  /* TODO: when librevenge will allow to define cell styles, check if conditionId is defined*/
3150
246
  if (addPropsToCellStyle)
3151
0
    cellStyle.reset(new IWORKStyle(props, none, cellStyle));
3152
246
  optional<IWORKDateTimeData> dateTime;
3153
246
  m_currentTable->m_table->insertCell(column, row, text, m_currentText, dateTime, 1, 1, formula, unsigned(row*256+column), cellStyle, cellType);
3154
246
  if (bool(commentId))
3155
0
  {
3156
0
    auto const commentIt = m_currentTable->m_commentList.find(get(commentId));
3157
0
    if (commentIt !=m_currentTable->m_commentList.end())
3158
0
    {
3159
0
      auto currentText=m_currentText;
3160
0
      m_currentText = m_collector.createText(m_langManager);
3161
0
      parseComment(boost::get<unsigned>(commentIt->second));
3162
0
      IWORKOutputElements elements;
3163
0
      m_currentText->draw(elements);
3164
0
      m_currentText=currentText;
3165
0
      m_currentTable->m_table->setComment(column, row, elements);
3166
0
    }
3167
0
    else
3168
0
    {
3169
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: can not find comment %d\n", get(commentId)));
3170
0
    }
3171
0
  }
3172
3173
246
  m_currentText.reset();
3174
3175
246
}
3176
3177
void IWAParser::parseTile(const unsigned id, const unsigned decalY)
3178
146
{
3179
146
  const ObjectMessage msg(*this, id, IWAObjectType::Tile);
3180
146
  if (!msg)
3181
22
    return;
3182
3183
  // rows must be fed to the collector in order
3184
124
  typedef map<unsigned, const IWAMessage *> Rows_t;
3185
124
  Rows_t rows;
3186
3187
  // save rows
3188
124
  for (const auto &it : get(msg).message(5))
3189
365
  {
3190
365
    if (!it.uint32(1) || !it.bytes(3) || !it.bytes(4))
3191
114
      continue;
3192
251
    const unsigned row = get(it.uint32(1))+decalY;
3193
251
    if (row >= m_currentTable->m_rows)
3194
0
    {
3195
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseTile: invalid row: %u\n", row));
3196
0
      continue;
3197
0
    }
3198
251
    rows[row] = &it;
3199
251
  }
3200
3201
  // process rows
3202
124
  for (auto it : rows)
3203
246
  {
3204
246
    bool useNewFormat=false;
3205
246
    map<unsigned,unsigned> offsets;
3206
246
    RVNGInputStreamPtr_t input;
3207
246
    unsigned length=0;
3208
3209
    /* first check if we can use the new definitions: data=6,offset=7,flag=[8],
3210
       if this is not possible, use the old definitions: data=3,offset=4 */
3211
246
    for (auto wh :
3212
246
         {
3213
246
           6, 3
3214
246
         })
3215
396
    {
3216
396
      offsets.clear();
3217
3218
396
      if (!bool(it.second->bytes(unsigned(wh)+1)))
3219
149
        continue;
3220
247
      input = get(it.second->bytes(unsigned(wh)));
3221
247
      if (!input) continue;
3222
247
      length = unsigned(getLength(input));
3223
247
      unsigned factor=1;
3224
247
      if (wh==6 && it.second->bool_(8) && get(it.second->bool_(8)))
3225
0
        factor=4;
3226
3227
247
      if (length >= factor*0xffff)
3228
0
      {
3229
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseTile: invalid column data length: %u\n", length));
3230
0
        length = factor*0xffff;
3231
0
      }
3232
3233
247
      useNewFormat=wh==6;
3234
247
      if (!parseColumnOffsets(get(it.second->bytes(unsigned(wh)+1)), length, offsets, factor))
3235
1
        continue;
3236
246
      break;
3237
247
    }
3238
3239
1.00k
    for (auto offIt=offsets.begin(); offIt!=offsets.end() ;)
3240
756
    {
3241
756
      unsigned column=offIt->first;
3242
756
      auto begPos=offIt->second;
3243
756
      ++offIt;
3244
756
      unsigned endPos=offIt==offsets.end() ? length : offIt->second;
3245
756
      input->seek((long) begPos, librevenge::RVNG_SEEK_SET);
3246
756
      parseTileDefinition(it.first, column, input, endPos, !useNewFormat);
3247
756
    }
3248
246
  }
3249
124
}
3250
3251
void IWAParser::parseTableHeaders(const unsigned id, TableHeader &header)
3252
370
{
3253
370
  const ObjectMessage msg(*this, id, IWAObjectType::Headers);
3254
370
  if (!msg)
3255
69
    return;
3256
3257
301
  for (const auto &it : get(msg).message(2))
3258
1.39k
  {
3259
1.39k
    if (it.uint32(1))
3260
1.31k
    {
3261
1.31k
      const unsigned index = get(it.uint32(1));
3262
1.31k
      if (index >= header.m_sizes.max_key())
3263
54
      {
3264
54
        ETONYEK_DEBUG_MSG(("IWAParser::parseTableHeaders: invalid row/column index %u\n", index));
3265
54
        continue;
3266
54
      }
3267
1.26k
      if (it.float_(2))
3268
1.20k
        header.m_sizes.insert_back(index, index + 1, get(it.float_(2)));
3269
1.26k
      if (it.bool_(3))
3270
1.19k
        header.m_hidden.insert_back(index, index + 1, get(it.bool_(3)));
3271
1.26k
    }
3272
1.39k
  }
3273
301
}
3274
3275
void IWAParser::parseTableGridLines(unsigned id, IWORKGridLineMap_t (&gridLines)[4])
3276
144
{
3277
144
  const ObjectMessage msg(*this, id, IWAObjectType::GridLines);
3278
144
  if (!msg)
3279
12
  {
3280
12
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLInes: can not find grid lines for index %u\n", id));
3281
12
    return;
3282
12
  }
3283
650
  for (unsigned wh=0; wh<4; ++wh)
3284
518
  {
3285
518
    if (get(msg).message(wh+4).empty()) continue;
3286
280
    auto const &lineRefs = readRefs(get(msg), wh+4);
3287
280
    auto &gridLine=gridLines[wh];
3288
280
    std::for_each(lineRefs.begin(), lineRefs.end(),
3289
280
                  [this,&gridLine](unsigned lineId)
3290
346
    {
3291
346
      parseTableGridLine(lineId, gridLine);
3292
346
    }
3293
280
                 );
3294
280
  }
3295
132
}
3296
3297
void IWAParser::parseTableGridLine(unsigned id, IWORKGridLineMap_t &gridLine)
3298
346
{
3299
346
  const ObjectMessage msg(*this, id, IWAObjectType::GridLine);
3300
346
  if (!msg)
3301
37
  {
3302
37
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find grid line for index %u\n", id));
3303
37
    return;
3304
37
  }
3305
309
  if (!get(msg).uint32(1))
3306
7
  {
3307
7
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find the main position index %u\n", id));
3308
7
    return;
3309
7
  }
3310
302
  auto pos1=get(get(msg).uint32(1));
3311
302
  const deque<IWAMessage> &lines = get(msg).message(2).repeated();
3312
302
  if (gridLine.find(pos1)==gridLine.end())
3313
300
    gridLine.insert(IWORKGridLineMap_t::value_type(pos1,IWORKGridLine_t(0,4096,nullptr)));
3314
302
  auto &flatSegments=gridLine.find(pos1)->second;
3315
302
  for (auto it : lines)
3316
317
  {
3317
317
    if (!it.uint32(1) || !it.uint32(2))
3318
20
    {
3319
20
      ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find the second position index %u\n", id));
3320
20
      continue;
3321
20
    }
3322
297
    IWORKPropertyMap props;
3323
297
    if (it.message(3))
3324
285
    {
3325
285
      IWORKStroke stroke;
3326
285
      readStroke(get(it.message(3)), stroke);
3327
285
      props.put<property::SFTStrokeProperty>(stroke);
3328
285
    }
3329
297
    flatSegments.insert_back(get(it.uint32(1)),get(it.uint32(1))+get(it.uint32(2)), std::make_shared<IWORKStyle>(props,none,none));
3330
297
  }
3331
302
}
3332
3333
bool IWAParser::parseFormat(const IWAMessage &msg, IWAParser::Format &format)
3334
103
{
3335
103
  if (!msg.uint32(1))
3336
1
  {
3337
1
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find the main type\n"));
3338
1
    return false;
3339
1
  }
3340
102
  auto uid=readUID(msg,41);
3341
102
  if (uid)
3342
0
  {
3343
0
    auto it= m_uidFormatMap.find(get(uid));
3344
0
    if (it==m_uidFormatMap.end())
3345
0
    {
3346
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find the format %x\n", unsigned(get(uid))));
3347
0
      return false;
3348
0
    }
3349
0
    format=it->second;
3350
0
    return true;
3351
0
  }
3352
3353
102
  auto type=get(msg.uint32(1));
3354
102
  format.m_type=IWORK_CELL_TYPE_NUMBER;
3355
102
  IWORKCellNumberType nType=IWORK_CELL_NUMBER_TYPE_DOUBLE;
3356
102
  if (type==265)   // check if data's type is defined
3357
0
  {
3358
0
    if (msg.uint32(24))
3359
0
      type=get(msg.uint32(24));
3360
0
  }
3361
102
  switch (type)
3362
102
  {
3363
0
  case 1: // automatic
3364
0
    return true;
3365
0
  case 263: // checkbox
3366
0
    format.m_type=IWORK_CELL_TYPE_BOOL;
3367
0
    return true;
3368
0
  case 264: // stepper
3369
0
  case 265: // slider
3370
0
  case 266: // pop-up menu
3371
0
  case 267:   // star rating
3372
0
  {
3373
0
    static bool first=true;
3374
0
    if (first)
3375
0
    {
3376
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: using type=%d is not implemented\n", int(type)));
3377
0
      first=false;
3378
0
    }
3379
0
    return false;
3380
0
  }
3381
0
  case 257: // currency
3382
0
    nType=IWORK_CELL_NUMBER_TYPE_CURRENCY;
3383
0
    break;
3384
0
  case 258: // percentage
3385
0
    nType=IWORK_CELL_NUMBER_TYPE_PERCENTAGE;
3386
0
    break;
3387
0
  case 259: // scientific
3388
0
    nType=IWORK_CELL_NUMBER_TYPE_SCIENTIFIC;
3389
0
    break;
3390
0
  case 262: // fraction
3391
0
    nType=IWORK_CELL_NUMBER_TYPE_FRACTION;
3392
0
    break;
3393
0
  case 256: // number
3394
0
  case 269: // numeral system
3395
0
    break;
3396
0
  case 270: // custom number
3397
    // TODO
3398
0
    break;
3399
101
  case 260:
3400
101
    format.m_type=IWORK_CELL_TYPE_TEXT;
3401
101
    return true;
3402
0
  case 271: // custom text
3403
    // normally, we use msg.string(18) as text for a not empty cell
3404
    // ie. store it when we see it in a custom format cell
3405
    //     then use it to define the cell content
3406
0
    format.m_type=IWORK_CELL_TYPE_TEXT;
3407
0
    return true;
3408
0
  case 261:
3409
0
    format.m_type=IWORK_CELL_TYPE_DATE_TIME;
3410
0
    if (msg.string(14))
3411
0
    {
3412
0
      IWORKDateTimeFormat dtFormat;
3413
0
      dtFormat.m_format=get(msg.string(14));
3414
0
      format.m_format=dtFormat;
3415
0
      return true;
3416
0
    }
3417
0
    else
3418
0
    {
3419
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find the format string\n"));
3420
0
      return false;
3421
0
    }
3422
0
    break;
3423
0
  case 272:
3424
0
    format.m_type=IWORK_CELL_TYPE_DATE_TIME;
3425
0
    if (msg.string(18))
3426
0
    {
3427
0
      IWORKDateTimeFormat dtFormat;
3428
0
      dtFormat.m_format=get(msg.string(18));
3429
0
      format.m_format=dtFormat;
3430
0
      return true;
3431
0
    }
3432
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find the date/time format\n"));
3433
0
    return false;
3434
0
  case 268:
3435
0
  {
3436
0
    format.m_type=IWORK_CELL_TYPE_DURATION;
3437
    // read 7: style and then 15 and 16
3438
0
    IWORKDurationFormat dFormat;
3439
0
    dFormat.m_format="%H:%M:%S";
3440
0
    format.m_format=dFormat;
3441
0
    return true;
3442
0
  }
3443
1
  default:
3444
1
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: find unknown type=%d\n", int(type)));
3445
1
    return false;
3446
102
  }
3447
0
  IWORKNumberFormat nFormat;
3448
0
  nFormat.m_type=nType;
3449
0
  if (msg.uint32(2)) nFormat.m_decimalPlaces=int(get(msg.uint32(2)));
3450
0
  else if (msg.uint32(9)) nFormat.m_decimalPlaces=int(get(msg.uint32(9)));
3451
0
  if (nFormat.m_decimalPlaces>128) nFormat.m_decimalPlaces=-1; // 253 means automatic?
3452
0
  if (msg.string(3)) nFormat.m_currencyCode=get(msg.string(3));
3453
0
  if (msg.bool_(4)) nFormat.m_thousandsSeparator=get(msg.bool_(4));
3454
0
  if (msg.bool_(5)) nFormat.m_accountingStyle=get(msg.bool_(5));
3455
0
  if (msg.uint32(8)) nFormat.m_base=int(get(msg.uint32(8)));
3456
0
  if (msg.uint32(11)) nFormat.m_fractionAccuracy=int(get(msg.uint32(11)));
3457
0
  format.m_format=nFormat;
3458
0
  return true;
3459
102
}
3460
3461
void IWAParser::parseCustomFormat(unsigned id)
3462
0
{
3463
0
  const ObjectMessage msg(*this, id, IWAObjectType::CustomDateTimeFormat);
3464
0
  if (!msg) return;
3465
0
  auto const &uidLists = readUIDs(get(msg),1);
3466
0
  auto const &formatList = get(msg).message(2).repeated();
3467
0
  if (uidLists.size()!=formatList.size())
3468
0
  {
3469
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: find unexpected data size\n"));
3470
0
    return;
3471
0
  }
3472
0
  for (size_t i=0; i<uidLists.size(); ++i)
3473
0
  {
3474
0
    auto const &formatMsg=formatList[i];
3475
    // 1: name, 2: type
3476
0
    auto const &formatDef=formatMsg.message(3);
3477
0
    Format format;
3478
0
    if (!formatDef || !parseFormat(get(formatDef), format))
3479
0
    {
3480
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find string zone\n"));
3481
0
      continue;
3482
0
    }
3483
0
    m_uidFormatMap[uidLists[i]]=format;
3484
0
  }
3485
0
}
3486
3487
bool IWAParser::parseConditionRules(unsigned id, IWAParser::ConditionRule_t &rules)
3488
0
{
3489
0
  const ObjectMessage ruleMsg(*this, id, IWAObjectType::ConditionStyle);
3490
0
  if (!ruleMsg)
3491
0
  {
3492
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseConditionRules: oops, can not find a condition for id=%x\n", id));
3493
0
    return false;
3494
0
  }
3495
0
  for (const auto &ruleIt : get(ruleMsg).message(2))
3496
0
  {
3497
0
    ConditionRule rule;
3498
0
    auto formulaMsg=ruleIt.message(1);
3499
0
    if (!formulaMsg || !get(formulaMsg).message(1) || !parseFormula(get(get(formulaMsg).message(1)), rule.m_formula) || !rule.m_formula)
3500
0
    {
3501
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseConditionRules: oops, can not read a formula for id=%x\n", id));
3502
0
      return false;
3503
0
    }
3504
0
    rule.m_cellStyleRef=readRef(ruleIt, 2);
3505
0
    rule.m_paragraphStyleRef=readRef(ruleIt, 3);
3506
0
    rules.push_back(rule);
3507
0
  }
3508
0
  return true;
3509
0
}
3510
3511
bool IWAParser::parseFormula(const IWAMessage &msg, IWORKFormulaPtr_t &formula)
3512
0
{
3513
0
  if (!msg.message(1))
3514
0
  {
3515
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not find the token table\n"));
3516
0
    return false;
3517
0
  }
3518
0
  const deque<IWAMessage> &tokens = get(msg.message(1)).message(1).repeated();
3519
3520
0
  typedef std::vector<IWORKFormula::Token> Formula;
3521
0
  std::vector<Formula> stack;
3522
0
  bool ok=true;
3523
0
  for (auto it : tokens)
3524
0
  {
3525
0
    auto type=it.uint32(1).optional();
3526
0
    if (!type)
3527
0
    {
3528
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not find the token type\n"));
3529
0
      ok=false;
3530
0
      break;
3531
0
    }
3532
0
    switch (get(type))
3533
0
    {
3534
0
    case 16:
3535
0
      if (it.uint32(2) && it.uint32(3))
3536
0
      {
3537
0
        static std::map<unsigned,std::string> functionsMap=
3538
0
        {
3539
0
          {1, "Abs"}, {2, "Accrint"}, {3, "AccrintM"}, {4, "Acos"}, {5, "Acosh"},
3540
0
          {6, "IWORKFormula::Address"}, {7, "And"}, {8, "Areas"}, {9, "Asin"}, {10, "AsinH"},
3541
0
          {11, "Atan"}, {12, "Atan2"}, {13, "AtanH"}, {14, "AverageDev"}, {15, "Average"},
3542
0
          {16, "AverageA"}, {17, "Ceiling"}, {18, "Char"}, {19,"Choose"}, {20, "Clean"},
3543
0
          {21, "Code"}, {22, "Column"}, {23, "Columns"}, {24, "ComBin"}, {25, "Concatenate"},
3544
0
          {26, "Confidence"}, {27, "Correl"}, {28, "Cos"}, {29, "CosH"}, {30, "Count"},
3545
0
          {31, "CountA"}, {32, "CountBlank"}, {33, "CountIf"}, {34, "CoupDayBs"}, {35, "CoupDays"},
3546
0
          {36, "CoupDaySNC"}, {37, "CoupNum"}, {38, "CoVar"}, {39, "Date"}, {40, "DateDif"},
3547
0
          {41, "Day"}, {42, "DB"}, {43, "DDB"}, {44, "Degrees"}, {45, "Disc"},
3548
0
          {46, "Dollar"}, {47, "EDate"}, {48, "Even"}, {49, "Exact"}, {50, "Exp"},
3549
0
          {51, "Fact"}, {52, "False"}, {53, "Find"}, {54, "Fixed"}, {55, "Floor"},
3550
0
          {56, "Forecast"}, {57, "Frequency"}, {58, "GCD"}, {59, "HLookUp"}, {60, "Hour"},
3551
0
          {61, "HyperLink"}, {62, "If"}, {63, "Index"}, {64, "Indirect"}, {65, "Int"},
3552
0
          {66, "Intercept"}, {67, "IPMT"}, {68, "Irr"}, {69, "IsBlank"}, {70, "IsError"},
3553
0
          {71, "IsEven"}, {72, "IsOdd"}, {73, "IsPMT"}, {74, "Large"}, {75, "LCM"},
3554
0
          {76, "Left"}, {77, "Len"}, {78, "LN"}, {79, "Log"}, {80, "Log10"},
3555
0
          {81, "LookUp"}, {82, "Lower"}, {83, "Match"}, {84, "Max"}, {85, "MaxA"},
3556
0
          {86, "Median"}, {87, "Mid"}, {88, "Min"}, {89, "MinA"}, {90, "Minute"},
3557
0
          {91, "Mirr"}, {92, "Mod"}, {93, "Mode"}, {94, "Month"}, {95, "MRound"},
3558
0
          {96, "Not"}, {97, "Now"}, {98, "NPer"}, {99, "NPV"}, {100, "Odd"},
3559
0
          {101, "Offset"}, {102, "Or"}, {103, "Percentile"}, {104, "Pi"}, {105, "PMT"},
3560
0
          {106, "Poisson"}, {107, "Power"}, {108, "PPMT"}, {109, "Price"}, {110, "PriceDist"},
3561
0
          {111, "PriceMat"}, {112, "Prob"}, {113, "Product"}, {114, "Proper"}, {115, "PV"},
3562
0
          {116, "Quotient"}, {117, "Radians"}, {118, "Rand"}, {119, "RandBetween"}, {120, "Rank"},
3563
0
          {121, "Rate"}, {122, "Replace"}, {123, "Repeat"}, {124, "Right"}, {125, "Roman"},
3564
0
          {126, "Round"}, {127, "RoundDown"}, {128, "RoundUp"}, {129, "Row"}, {130, "Rows"},
3565
0
          {131, "Search"}, {132, "Second"}, {133, "Sign"}, {134, "Sin"}, {135, "SinH"},
3566
0
          {136, "SLN"}, {137, "Slope"}, {138, "Small"}, {139, "Sqrt"}, {140, "STDEV"},
3567
0
          {141, "STDEVA"}, {142, "STDEVP"}, {143, "STDEVPA"}, {144, "Substitute"}, {145, "SumIf"},
3568
0
          {146, "SumProduct"}, {147, "SumSqrt"}, {148, "Syd"}, {149, "T"}, {150, "Tan"},
3569
0
          {151, "TanH"}, {152, "Time"}, {153, "TimeValue"}, {154, "Today"}, {155, "Trim"},
3570
0
          {156, "True"}, {157, "Trunc"}, {158, "Upper"}, {159, "Value"}, {160, "Var"},
3571
0
          {161, "VarA"}, {162, "VarP"}, {163, "VarPA"}, {164, "VDB"}, {165, "VLookup"},
3572
0
          {166, "WeekDay"}, {167, "Year"}, {168, "Sum"},
3573
0
          {185, "Effect"},
3574
0
          {186, "Nominal"}, {187, "NormDist"}, {188, "NormsDist"}, {189, "NormInv"},  {190, "NormsInv"},
3575
0
          {191, "Yield"}, {192, "YieldDist"}, {193, "YieldMat"}, {194, "BondDuration"}, {195, "BondMDuration"},
3576
0
          {196, "Erf"}, {197, "ErfC"}, {198, "Standardize"}, {199, "IntRate"}, {200, "Received"},
3577
0
          {201, "CUMIPMT"}, {202, "CUMPRINC"}, {203, "EOMonth"}, {204, "WorkDay"}, {205, "MonthName"},
3578
0
          {206, "WeekNum"}, {207, "Dur2Hours"}, {208, "Dur2Minutes"}, {209, "Dur2Seconds"}, {210, "Dur2Days"},
3579
0
          {211, "Dur2Weeks"}, {212, "Duration"}, {213, "ExpOnDist"}, {214, "YearFrac"}, {215, "ZTest"},
3580
0
          {216, "SumX2MY2"}, {217, "SumX2PY2"}, {218, "SumXMY2"}, {219, "SqrtPi"}, {220, "Transpose"},
3581
0
          {221, "DevSQ"}, {222, "FV"}, {223, "Delta"}, {224, "FactDouble"}, {225, "GEStep"},
3582
0
          {226, "PercentRank"},{227, "GammaLN"},{228, "DateValue"},{229, "GammaDist"},{230, "GammaInv"},
3583
0
          {231, "SumIfs"}, {232, "AverageIfs"}, {233, "CountIfs"}, {234, "AverageIf"}, {235, "IfError"},
3584
0
          {236, "DayName"}, {237, "BesselJ"}, {238,"BesselY"}, {239,"LogNormDist"}, {240,"LogInv"},
3585
0
          {241, "TDist"}, {242, "BinomDist"}, {243, "NegBinomDist"}, {244, "FDist"}, {245, "Permut"},
3586
0
          {246, "ChiDist"}, {247, "ChiTest"}, {248, "TTest"}, {249, "Quartile"}, {250, "Multinomial"},
3587
0
          {251, "CritBinom"}, {252, "BaseToNum"}, {253, "NumToBase"}, {254, "TInv"}, {255, "Convert"},
3588
0
          {256, "ChiInv"}, {257, "FInv"}, {258, "BetaDist"}, {259, "BetaInv"}, {260, "NetWorkDays"},
3589
0
          {261, "Days360"}, {262, "HarMean"}, {263, "GeoMin"}, {264, "Dec2Hex"}, {265, "Dec2Bin"},
3590
0
          {266, "Dec2Oct"}, {267, "Bin2Hex"}, {268, "Bin2Dec"}, {269, "Bin2Oct"}, {270, "Oct2Bin"},
3591
0
          {271, "Oct2Dec"}, {272, "Oct2Hex"}, {273, "Hex2Bin"}, {274, "Hex2Dec"}, {275, "Hex2Oct"},
3592
0
          {276, "Linest"}, {277, "Dur2Milliseconds"}, {278, "StripDuration"}, {280, "Intercept.Ranges"},
3593
0
          {285, "Union.Ranges"},
3594
0
          {286, "SeriesSum"}, {287, "Polynomial"}, {288, "WeiBull"},
3595
0
          {297, "PlainText"}, {298, "Stock"}, {299, "StockH"}, {300, "Currency"},
3596
0
          {301, "CurrencyH"}, {302, "CurrencyConvert"}, {303, "CurrencyCode"},
3597
0
    {304, "IsNumber"}, {305, "IsText"}, {306, "IsDate"},
3598
0
    {309, "MaxIfs"}, {310, "MinIfs"}, {311, "XIRR"}, {312, "XNPV"}, {313, "Ifs"},
3599
0
    {314, "XLookup"}, {315, "XMatch"}, {316, "Subtotal"}, {317, "CountMatches"},
3600
0
    {318, "TextBefore"}, {319, "TextBetween"}, {320, "TextAfter"},
3601
0
    {321, "Regex"}, {322, "Reference.Name"}, {323, "FormulaText"}, {324, "Regex.Extract"},
3602
0
    {325, "GetPivotData"}, {328, "TextJoin"}, {329, "Concat"},
3603
0
    {330, "BitAnd"}, {331, "BitOr"}, {332, "BitXor"}, {333, "BitLShift"}, {334, "BitRShift"},
3604
0
    {335, "ISOWeekNum"}, {336, "Switch"}
3605
3606
0
        };
3607
0
        Formula child;
3608
0
        std::ostringstream s;
3609
0
        if (functionsMap.find(get(it.uint32(2)))!=functionsMap.end())
3610
0
          s << functionsMap.find(get(it.uint32(2)))->second;
3611
0
        else
3612
0
          s << "Funct" << get(it.uint32(2));
3613
0
        child.push_back(IWORKFormula::Token(s.str(), IWORKFormula::Token::Function));
3614
3615
0
        size_t numArgs=get(it.uint32(3));
3616
0
        size_t numData=stack.size();
3617
0
        if (numData<numArgs)
3618
0
        {
3619
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: bad stack for function\n"));
3620
0
          ok=false;
3621
0
          break;
3622
0
        }
3623
0
        child.push_back(IWORKFormula::Token("(", IWORKFormula::Token::Operator));
3624
0
        for (size_t i=numData-numArgs; i<numData; ++i)
3625
0
        {
3626
0
          if (i!=numData-numArgs) child.push_back(IWORKFormula::Token(";", IWORKFormula::Token::Operator));
3627
0
          child.insert(child.end(), stack[i].begin(),stack[i].end());
3628
0
        }
3629
0
        child.push_back(IWORKFormula::Token(")", IWORKFormula::Token::Operator));
3630
0
        stack.resize(numData-numArgs);
3631
0
        stack.push_back(child);
3632
0
      }
3633
0
      else
3634
0
      {
3635
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a function\n"));
3636
0
        ok=false;
3637
0
      }
3638
0
      break;
3639
0
    case 17:
3640
0
      if (it.double_(4))
3641
0
        stack.push_back({IWORKFormula::Token(get(it.double_(4)))});
3642
0
      else
3643
0
      {
3644
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a double\n"));
3645
0
        ok=false;
3646
0
      }
3647
0
      break;
3648
0
    case 18:
3649
0
      if (it.bool_(5))
3650
0
      {
3651
0
        stack.push_back({IWORKFormula::Token(get(it.bool_(5)) ? "True" : "False", IWORKFormula::Token::Function),
3652
0
                         IWORKFormula::Token("(", IWORKFormula::Token::Operator), IWORKFormula::Token(")", IWORKFormula::Token::Operator)
3653
0
                        });
3654
0
      }
3655
0
      else
3656
0
      {
3657
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a bool\n"));
3658
0
        ok=false;
3659
0
      }
3660
0
      break;
3661
0
    case 19:
3662
0
      if (it.string(6))
3663
0
        stack.push_back({IWORKFormula::Token(get(it.string(6)),IWORKFormula::Token::String)});
3664
0
      else
3665
0
      {
3666
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a string\n"));
3667
0
        ok=false;
3668
0
      }
3669
0
      break;
3670
0
    case 22: // empty?
3671
0
      stack.push_back({});
3672
0
      break;
3673
0
    case 23:
3674
0
      if (it.bool_(10))
3675
0
        stack.push_back({});
3676
0
      else
3677
0
      {
3678
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a optional argument\n"));
3679
0
        ok=false;
3680
0
      }
3681
0
      break;
3682
0
    case 25:
3683
0
      if (it.uint32(13))
3684
0
      {
3685
0
        size_t numArgs=get(it.uint32(13));
3686
0
        size_t numData=stack.size();
3687
0
        if (numData<numArgs)
3688
0
        {
3689
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: bad stack for ()\n"));
3690
0
          ok=false;
3691
0
          break;
3692
0
        }
3693
0
        Formula child;
3694
0
        child.push_back(IWORKFormula::Token("(", IWORKFormula::Token::Operator));
3695
0
        for (size_t i=numData-numArgs; i<numData; ++i)
3696
0
        {
3697
0
          if (i!=numData-numArgs) child.push_back(IWORKFormula::Token(";", IWORKFormula::Token::Operator));
3698
0
          child.insert(child.end(), stack[i].begin(),stack[i].end());
3699
0
        }
3700
0
        child.push_back(IWORKFormula::Token(")", IWORKFormula::Token::Operator));
3701
0
        stack.resize(numData-numArgs);
3702
0
        stack.push_back(child);
3703
0
      }
3704
0
      else
3705
0
      {
3706
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a ()\n"));
3707
0
        ok=false;
3708
0
      }
3709
0
      break;
3710
0
    case 32: // separator,
3711
0
    case 33:
3712
0
      break;
3713
0
    case 36:
3714
0
    {
3715
0
      IWORKFormula::Address address;
3716
0
      for (unsigned i=0; i<2; ++i)
3717
0
      {
3718
0
        auto pos=it.message(26+i);
3719
0
        if (!pos) continue;
3720
0
        if (!get(pos).sint32(1))
3721
0
        {
3722
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not read a position\n"));
3723
0
          ok=false;
3724
0
          break;
3725
0
        }
3726
0
        IWORKFormula::Coord coord;
3727
0
        coord.m_absolute = get_optional_value_or(get(pos).bool_(2).optional(), false);
3728
0
        coord.m_coord=get(get(pos).sint32(1))+1;
3729
0
        if (i==0)
3730
0
          address.m_column=coord;
3731
0
        else
3732
0
          address.m_row=coord;
3733
0
      }
3734
0
      address.m_table=readUUID(it,28);
3735
      // readUUID(it,38); filename ?
3736
0
      if (!ok)
3737
0
        break;
3738
0
      stack.push_back({IWORKFormula::Token(address)});
3739
0
      break;
3740
0
    }
3741
0
    case 34: // arg begin
3742
0
    case 35: // arg end
3743
0
      break;
3744
0
    case 63:   // current cell, use in condition formula
3745
0
    {
3746
0
      IWORKFormula::Address address;
3747
0
      address.m_row=address.m_column=IWORKFormula::Coord();
3748
0
      address.m_table=readUUID(it,28);
3749
0
      stack.push_back({IWORKFormula::Token(address)});
3750
0
      break;
3751
0
    }
3752
0
    default:
3753
0
      if ((get(type)>=1 && get(type)<=15) || get(type)==29 || get(type)==45)
3754
0
      {
3755
0
        char const *wh[] =
3756
0
        {
3757
0
          nullptr, "+", "-", "*", "/",
3758
0
          "^", "&", ">", ">=",
3759
0
          "<", "<=", "=", "<>",
3760
0
          "-", "+", "%"
3761
0
        };
3762
0
        if (get(type)<=15 && !wh[get(type)])
3763
0
        {
3764
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: find unexpected type=%u\n", get(type)));
3765
0
          ok=false;
3766
0
          break;
3767
0
        }
3768
0
        size_t numArgs=(get(type)<13 || get(type)>=29) ? 2 : 1;
3769
0
        size_t numData=stack.size();
3770
0
        if (numData<numArgs)
3771
0
        {
3772
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: bad stack for type=%u\n", get(type)));
3773
0
          ok=false;
3774
0
          break;
3775
0
        }
3776
0
        Formula child;
3777
0
        if (numArgs==2)
3778
0
        {
3779
0
          child.insert(child.end(), stack[numData-2].begin(),stack[numData-2].end());
3780
0
          child.push_back(IWORKFormula::Token(get(type)>=29 ? ":" : wh[get(type)], IWORKFormula::Token::Operator));
3781
0
          child.insert(child.end(), stack[numData-1].begin(),stack[numData-1].end());
3782
0
        }
3783
0
        else if (get(type)<15)
3784
0
        {
3785
0
          child.push_back(IWORKFormula::Token(wh[get(type)], IWORKFormula::Token::Operator));
3786
0
          child.insert(child.end(), stack[numData-1].begin(),stack[numData-1].end());
3787
0
        }
3788
0
        else
3789
0
        {
3790
0
          child.insert(child.end(), stack[numData-1].begin(),stack[numData-1].end());
3791
0
          child.push_back(IWORKFormula::Token(wh[get(type)], IWORKFormula::Token::Operator));
3792
0
        }
3793
0
        stack.resize(numData-numArgs);
3794
0
        stack.push_back(child);
3795
0
        break;
3796
0
      }
3797
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: find unexpected type=%u\n", get(type)));
3798
0
      ok=false;
3799
0
      break;
3800
0
    }
3801
0
    if (!ok)
3802
0
      break;
3803
0
  }
3804
0
  if (stack.size()!=1) ok=false;
3805
0
  if (!ok)
3806
0
  {
3807
0
    std::ostringstream readData;
3808
0
    for (auto const &form : stack)
3809
0
      for (auto const &dt : form)
3810
0
        readData << dt << ",";
3811
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormula: can not find parse a formula=%s\n", readData.str().c_str()));
3812
0
  }
3813
0
  else
3814
0
  {
3815
0
    formula.reset(new IWORKFormula(boost::make_optional(0u)));
3816
0
    formula->parse(stack[0]);
3817
0
  }
3818
0
  return ok;
3819
0
}
3820
3821
void IWAParser::parseLink(const unsigned id, std::string &url)
3822
53
{
3823
53
  const ObjectMessage msg(*this, id, IWAObjectType::Link);
3824
53
  if (!msg)
3825
53
    return;
3826
3827
0
  if (get(msg).string(2))
3828
0
    url = get(get(msg).string(2));
3829
0
}
3830
3831
}
3832
3833
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */