Coverage Report

Created: 2026-04-29 07:28

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
24
{
58
24
  if (point1 && point2)
59
24
    return approxEqual(get(point1).m_x, get(point2).m_x) && approxEqual(get(point1).m_y, get(point2).m_y);
60
0
  return true;
61
24
}
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.18k
{
72
5.18k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
5.18k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
5.18k
  if (converted)
75
2.61k
    props.put<P>(get(converted));
76
5.18k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Alignment>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.35k
{
72
1.35k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.35k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.35k
  if (converted)
75
1.34k
    props.put<P>(get(converted));
76
1.35k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::ParagraphBorderType>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.27k
{
72
1.27k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.27k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.27k
  if (converted)
75
6
    props.put<P>(get(converted));
76
1.27k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Baseline>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.27k
{
72
1.27k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.27k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.27k
  if (converted)
75
0
    props.put<P>(get(converted));
76
1.27k
}
IWAParser.cpp:void libetonyek::(anonymous namespace)::putEnum<libetonyek::property::Capitalization>(libetonyek::IWORKPropertyMap&, unsigned int)
Line
Count
Source
71
1.27k
{
72
1.27k
  typedef typename IWORKPropertyInfo<P>::ValueType ValueType;
73
1.27k
  const optional<ValueType> &converted = IWORKNumberConverter<ValueType>::convert(value);
74
1.27k
  if (converted)
75
1.25k
    props.put<P>(get(converted));
76
1.27k
}
77
78
bool parseColumnOffsets(const RVNGInputStreamPtr_t &input, const unsigned length, map<unsigned,unsigned> &offsets, unsigned factor=1)
79
139
{
80
139
  try
81
139
  {
82
139
    unsigned col=0;
83
35.5k
    while (!input->isEnd())
84
35.4k
    {
85
35.4k
      const unsigned offset = readU16(input);
86
35.4k
      if (factor*offset<length && factor*offset+4 < length)
87
144
        offsets[col]=factor*offset;
88
35.3k
      else if (offset!=0xffff)
89
18
      {
90
18
        if (col==0 && offset==0x9ff0) // the content: f09fa4a0 seems to mean undef
91
0
          return false;
92
18
        ETONYEK_DEBUG_MSG(("parseColumnOffsets[IWAParser]: find %x>%x\n", offset, length));
93
18
      }
94
35.4k
      ++col;
95
35.4k
    }
96
139
  }
97
139
  catch (...)
98
139
  {
99
    // ignore failure to read the last word: it can only happen in a broken file
100
0
    return false;
101
0
  }
102
139
  return true;
103
139
}
104
105
deque<IWORKColumnRowSize> makeSizes(const mdds::flat_segment_tree<unsigned, float> &sizes)
106
178
{
107
178
  IWORKColumnRowSize defVal;
108
178
  if (sizes.default_value()>0) defVal=IWORKColumnRowSize(sizes.default_value(),false);
109
178
  deque<IWORKColumnRowSize> out(sizes.max_key(), IWORKColumnRowSize());
110
602
  for (mdds::flat_segment_tree<unsigned, float>::const_iterator it = sizes.begin(); it != sizes.end();)
111
424
  {
112
424
    const deque<IWORKColumnRowSize>::iterator start(out.begin() + deque<double>::iterator::difference_type(it->first));
113
424
    const double size = it->second;
114
424
    ++it;
115
424
    const deque<IWORKColumnRowSize>::iterator end(it == sizes.end() ? out.end() : out.begin() +  deque<double>::iterator::difference_type(it->first));
116
424
    std::fill(start, end, size>0 ? IWORKColumnRowSize(size) : defVal);
117
424
  }
118
178
  return out;
119
178
}
120
121
}
122
123
IWAParser::Format::Format()
124
92
  : m_type()
125
92
  , m_format()
126
92
{
127
92
}
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
198
  : m_sizes(0, count, defValue)
137
198
  , m_hidden(0, count, false)
138
198
{
139
198
}
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
99
  : m_table(table)
150
99
  , m_columns(columns)
151
99
  , m_rows(rows)
152
99
  , m_style()
153
99
  , m_columnHeader(columns)
154
99
  , m_rowHeader(rows,20)
155
99
  , m_simpleTextList()
156
99
  , m_cellStyleList()
157
99
  , m_conditionStyleList()
158
99
  , m_formattedTextList()
159
99
  , m_formulaList()
160
99
  , m_formatList()
161
99
  , m_newFormatList()
162
99
  , m_commentList()
163
99
{
164
99
}
165
166
IWAParser::IWAParser(const RVNGInputStreamPtr_t &fragments, const RVNGInputStreamPtr_t &package, IWORKCollector &collector)
167
1.77k
  : m_formatNameMap()
168
1.77k
  , m_langManager()
169
1.77k
  , m_tableNameMap(std::make_shared<IWORKTableNameMap_t>())
170
1.77k
  , m_currentText()
171
1.77k
  , m_collector(collector)
172
1.77k
  , m_index(fragments, package)
173
1.77k
  , m_visited()
174
1.77k
  , m_charStyles()
175
1.77k
  , m_dropCapStyles()
176
1.77k
  , m_paraStyles()
177
1.77k
  , m_sectionStyles()
178
1.77k
  , m_graphicStyles()
179
1.77k
  , m_mediaStyles()
180
1.77k
  , m_cellStyles()
181
1.77k
  , m_tableStyles()
182
1.77k
  , m_listStyles()
183
1.77k
  , m_currentTable()
184
1.77k
  , m_uidFormatMap()
185
1.77k
{
186
1.77k
}
187
188
bool IWAParser::parse()
189
1.77k
{
190
1.77k
  parseObjectIndex();
191
1.77k
  return parseDocument();
192
1.77k
}
193
194
IWAParser::ObjectMessage::ObjectMessage(IWAParser &parser, const unsigned id, const unsigned type)
195
63.8k
  : m_parser(parser)
196
63.8k
  , m_message()
197
63.8k
  , m_id(id)
198
63.8k
  , m_type(0)
199
63.8k
{
200
63.8k
  std::deque<unsigned>::const_iterator it = find(m_parser.m_visited.begin(), m_parser.m_visited.end(), m_id);
201
63.8k
  if (it == m_parser.m_visited.end())
202
63.8k
  {
203
63.8k
    optional<IWAMessage> msg;
204
63.8k
    m_parser.queryObject(m_id, m_type, msg);
205
63.8k
    if (msg)
206
54.9k
    {
207
54.9k
      if ((m_type == type) || (type == 0))
208
54.8k
      {
209
54.8k
        m_message = msg;
210
54.8k
        m_parser.m_visited.push_back(m_id);
211
54.8k
      }
212
161
      else
213
161
      {
214
161
        ETONYEK_DEBUG_MSG(("IWAParser::ObjectMessage::ObjectMessage: type mismatch for object %u: expected %u, got %u\n", id, type, m_type));
215
161
      }
216
54.9k
    }
217
63.8k
  }
218
71
  else
219
71
  {
220
71
    ETONYEK_DEBUG_MSG(("IWAParser::ObjectMessage::ObjectMessage: object %u is actually visited\n", id));
221
71
  }
222
63.8k
}
223
224
IWAParser::ObjectMessage::~ObjectMessage()
225
63.7k
{
226
63.7k
  if (m_message)
227
54.8k
  {
228
54.8k
    assert(!m_parser.m_visited.empty());
229
54.8k
    assert(m_parser.m_visited.back() == m_id);
230
54.8k
    m_parser.m_visited.pop_back();
231
54.8k
  }
232
63.7k
}
233
234
IWAParser::ObjectMessage::operator bool() const
235
63.7k
{
236
63.7k
  return bool(m_message);
237
63.7k
}
238
239
const IWAMessage &IWAParser::ObjectMessage::get() const
240
207k
{
241
207k
  return m_message.get();
242
207k
}
243
244
unsigned IWAParser::ObjectMessage::getType() const
245
39.1k
{
246
39.1k
  return m_type;
247
39.1k
}
248
249
void IWAParser::queryObject(const unsigned id, unsigned &type, boost::optional<IWAMessage> &msg) const
250
63.8k
{
251
63.8k
  m_index.queryObject(id, type, msg);
252
63.8k
}
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.35k
{
261
1.35k
  return m_index.queryFile(id);
262
1.35k
}
263
264
boost::optional<unsigned> IWAParser::readRef(const IWAMessage &msg, const unsigned field)
265
80.2k
{
266
80.2k
  if (msg.message(field))
267
64.3k
    return msg.message(field).uint32(1).optional();
268
15.9k
  return boost::none;
269
80.2k
}
270
271
std::deque<unsigned> IWAParser::readRefs(const IWAMessage &msg, const unsigned field)
272
14.7k
{
273
14.7k
  std::deque<unsigned> refs;
274
14.7k
  if (msg.message(field))
275
10.2k
  {
276
10.2k
    const std::deque<IWAMessage> &objs = msg.message(field).repeated();
277
10.2k
    for (const auto &obj : objs)
278
20.1k
    {
279
20.1k
      if (obj.uint32(1))
280
19.8k
        refs.push_back(obj.uint32(1).get());
281
20.1k
    }
282
10.2k
  }
283
14.7k
  return refs;
284
14.7k
}
285
286
boost::optional<IWORKPosition> IWAParser::readPosition(const IWAMessage &msg, const unsigned field)
287
20.3k
{
288
20.3k
  if (msg.message(field))
289
20.1k
  {
290
20.1k
    const optional<float> &x = msg.message(field).float_(1).optional();
291
20.1k
    const optional<float> &y = msg.message(field).float_(2).optional();
292
20.1k
    return IWORKPosition(get_optional_value_or(x, 0), get_optional_value_or(y, 0));
293
20.1k
  }
294
208
  return boost::none;
295
20.3k
}
296
297
boost::optional<IWORKSize> IWAParser::readSize(const IWAMessage &msg, const unsigned field)
298
14.4k
{
299
14.4k
  if (msg.message(field))
300
13.9k
  {
301
13.9k
    const optional<float> &w = msg.message(field).float_(1).optional();
302
13.9k
    const optional<float> &h = msg.message(field).float_(2).optional();
303
13.9k
    return IWORKSize(get_optional_value_or(w, 0), get_optional_value_or(h, 0));
304
13.9k
  }
305
560
  return boost::none;
306
14.4k
}
307
308
boost::optional<IWORKColor> IWAParser::readColor(const IWAMessage &msg, const unsigned field)
309
15.1k
{
310
15.1k
  const IWAMessageField &color = msg.message(field);
311
15.1k
  if (color)
312
9.64k
  {
313
9.64k
    if (color.float_(3) && color.float_(4) && color.float_(5))
314
9.41k
      return IWORKColor(get(color.float_(3)), get(color.float_(4)), get(color.float_(5)), get_optional_value_or(color.float_(6), 0));
315
9.64k
  }
316
5.69k
  return boost::none;
317
15.1k
}
318
319
boost::optional<uint64_t> IWAParser::readUID(const IWAMessage &msg, unsigned field)
320
92
{
321
92
  const IWAMessageField &id = msg.message(field);
322
92
  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.03k
{
378
1.03k
  const optional<IWORKColor> &color = readColor(msg, 1);
379
1.03k
  if (color)
380
1.01k
    stroke.m_color = get(color);
381
1.03k
  stroke.m_width = get(msg.float_(2));
382
1.03k
  if (msg.uint32(3))
383
969
  {
384
969
    switch (get(msg.uint32(3)))
385
969
    {
386
0
    default :
387
0
      ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown cap value: %u", get(msg.uint32(3))));
388
0
      ETONYEK_FALLTHROUGH;
389
853
    case 0 :
390
853
      stroke.m_cap = IWORK_LINE_CAP_BUTT;
391
853
      break;
392
116
    case 1 :
393
116
      stroke.m_cap = IWORK_LINE_CAP_ROUND;
394
116
      break;
395
969
    }
396
969
  }
397
1.03k
  if (msg.uint32(4))
398
986
  {
399
986
    switch (get(msg.uint32(4)))
400
986
    {
401
0
    default :
402
0
      ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown join value: %u", get(msg.uint32(4))));
403
0
      ETONYEK_FALLTHROUGH;
404
870
    case 0 :
405
870
      stroke.m_join = IWORK_LINE_JOIN_MITER;
406
870
      break;
407
116
    case 1 :
408
116
      stroke.m_join = IWORK_LINE_JOIN_ROUND;
409
116
      break;
410
986
    }
411
986
  }
412
1.03k
  if (msg.message(6))
413
960
  {
414
960
    stroke.m_pattern.m_type = IWORK_STROKE_TYPE_SOLID;
415
960
    if (msg.message(6).uint32(1))
416
957
    {
417
957
      switch (get(msg.message(6).uint32(1)))
418
957
      {
419
44
      default :
420
44
        ETONYEK_DEBUG_MSG(("IWAParser::readStroke: unknown stroke value: %u", get(msg.message(6).uint32(1))));
421
44
        ETONYEK_FALLTHROUGH;
422
276
      case 1:
423
276
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_SOLID;
424
276
        break;
425
245
      case 0:
426
245
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_DASHED;
427
245
        break;
428
436
      case 2:
429
436
        stroke.m_pattern.m_type = IWORK_STROKE_TYPE_NONE;
430
436
        break;
431
957
      }
432
957
    }
433
960
    unsigned remaining = 0;
434
960
    if (msg.message(6).uint32(3))
435
844
      remaining = get(msg.message(6).uint32(3));
436
960
    const deque<float> &elements = msg.message(6).float_(4).repeated();
437
2.00k
    for (auto it = elements.begin(); it != elements.end() && remaining != 0; ++it)
438
1.04k
      stroke.m_pattern.m_values.push_back(*it);
439
960
  }
440
  // todo: check also if there is a picture frame msg.message(8), if yes, use it as border
441
1.03k
}
442
443
bool IWAParser::readFill(const IWAMessage &msg, IWORKFill &fill)
444
2.03k
{
445
2.03k
  const optional<IWORKColor> &color = readColor(msg, 1);
446
2.03k
  if (color)
447
802
  {
448
802
    fill = get(color);
449
802
    return true;
450
802
  }
451
1.23k
  else if (msg.message(2))
452
179
  {
453
179
    IWORKGradient gradient;
454
179
    readGradient(get(msg.message(2)), gradient);
455
179
    fill = gradient;
456
179
    return true;
457
179
  }
458
1.05k
  else if (msg.message(3))
459
384
  {
460
384
    IWORKMediaContent bitmap;
461
384
    if (msg.message(3).uint32(2))
462
384
    {
463
384
      switch (get(msg.message(3).uint32(2)))
464
384
      {
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
53
      case 0 :
469
53
        bitmap.m_type = IWORK_IMAGE_TYPE_ORIGINAL_SIZE;
470
53
        break;
471
32
      case 1 :
472
32
        bitmap.m_type = IWORK_IMAGE_TYPE_STRETCH;
473
32
        break;
474
245
      case 2 :
475
245
        bitmap.m_type = IWORK_IMAGE_TYPE_TILE;
476
245
        break;
477
26
      case 3 :
478
26
        bitmap.m_type = IWORK_IMAGE_TYPE_SCALE_TO_FILL;
479
26
        break;
480
28
      case 4 :
481
28
        bitmap.m_type = IWORK_IMAGE_TYPE_SCALE_TO_FIT;
482
28
        break;
483
384
      }
484
384
    }
485
384
    bitmap.m_fillColor = readColor(get(msg.message(3)), 3);
486
384
    if (!bitmap.m_fillColor) // a least in new KeyNote files, the field 9 can also store a color
487
276
      bitmap.m_fillColor = readColor(get(msg.message(3)), 9);
488
384
    bitmap.m_size = readSize(get(msg.message(3)), 4);
489
384
    if (!bitmap.m_size) bitmap.m_size=IWORKSize(); // to do not change result from previous code
490
384
    const optional<unsigned> &fileRef = readRef(get(msg.message(3)), 6);
491
384
    if (fileRef)
492
375
    {
493
      // find also 16 with no file...
494
375
      bitmap.m_data = std::make_shared<IWORKData>();
495
375
      bitmap.m_data->m_stream = queryFile(get(fileRef));
496
375
      if (!bitmap.m_data->m_stream && !bitmap.m_fillColor) bitmap.m_fillColor = m_index.queryFileColor(get(fileRef));
497
375
    }
498
384
    fill = bitmap;
499
384
    return true;
500
384
  }
501
670
  return false;
502
2.03k
}
503
504
void IWAParser::readGradient(const IWAMessage &msg, IWORKGradient &gradient)
505
179
{
506
179
  if (msg.uint32(1))
507
178
  {
508
178
    switch (get(msg.uint32(1)))
509
178
    {
510
1
    default :
511
1
      ETONYEK_DEBUG_MSG(("IWAParser::readGradient: unknown gradient type: %u", get(msg.uint32(1))));
512
1
      ETONYEK_FALLTHROUGH;
513
112
    case 0 :
514
112
      gradient.m_type = IWORK_GRADIENT_TYPE_LINEAR;
515
112
      break;
516
66
    case 1 :
517
66
      gradient.m_type = IWORK_GRADIENT_TYPE_RADIAL;
518
66
      break;
519
178
    }
520
178
  }
521
179
  for (const auto &it : msg.message(2))
522
351
  {
523
351
    IWORKGradientStop stop;
524
351
    const optional<IWORKColor> &color = readColor(it, 1);
525
351
    if (color)
526
333
      stop.m_color = get(color);
527
351
    if (it.float_(2))
528
320
      stop.m_fraction = get(it.float_(2));
529
351
    if (it.float_(3))
530
321
      stop.m_inflection = get(it.float_(3));
531
351
    gradient.m_stops.push_back(stop);
532
351
  }
533
179
  if (msg.message(5) && msg.message(5).float_(2))
534
110
    gradient.m_angle = get(msg.message(5).float_(2));
535
179
}
536
537
void IWAParser::readShadow(const IWAMessage &msg, IWORKShadow &shadow)
538
1.06k
{
539
1.06k
  const optional<IWORKColor> &color = readColor(msg, 1);
540
1.06k
  if (color)
541
1.04k
    shadow.m_color = get(color);
542
1.06k
  if (msg.float_(2))
543
1.05k
    shadow.m_angle = get(msg.float_(2));
544
1.06k
  if (msg.float_(3))
545
1.04k
    shadow.m_offset = get(msg.float_(3));
546
  // 4. blur
547
1.06k
  if (msg.float_(5))
548
1.04k
    shadow.m_opacity = get(msg.float_(5));
549
  // 6: bool true
550
1.06k
  if (msg.bool_(6))
551
1.03k
    shadow.m_visible = get(msg.bool_(6));
552
  // 7: type enum 0: drop,
553
1.06k
}
554
555
void IWAParser::readPadding(const IWAMessage &msg, IWORKPadding &padding)
556
822
{
557
822
  padding.m_left = msg.float_(1).optional();
558
822
  padding.m_top = msg.float_(2).optional();
559
822
  padding.m_right = msg.float_(3).optional();
560
822
  padding.m_bottom = msg.float_(4).optional();
561
822
}
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
10.6k
{
585
10.6k
  const ObjectMessage msg(*this, id);
586
10.6k
  if (!msg)
587
536
    return false;
588
10.1k
  return dispatchShapeWithMessage(get(msg), msg.getType());
589
10.6k
}
590
591
bool IWAParser::dispatchShapeWithMessage(const IWAMessage &msg, unsigned type)
592
10.1k
{
593
10.1k
  switch (type)
594
10.1k
  {
595
0
  case IWAObjectType::ConnectionLine :
596
3.52k
  case IWAObjectType::DrawableShape :
597
3.52k
    return parseDrawableShape(msg, type==IWAObjectType::ConnectionLine);
598
125
  case IWAObjectType::Group :
599
125
    return parseGroup(msg);
600
525
  case IWAObjectType::Image :
601
525
    return parseImage(msg);
602
1.77k
  case IWAObjectType::StickyNote:
603
1.77k
    return parseStickyNote(msg);
604
106
  case IWAObjectType::TabularInfo :
605
106
    return parseTabularInfo(msg);
606
4.06k
  default:
607
4.06k
  {
608
4.06k
    static bool first=true;
609
4.06k
    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.06k
  }
615
10.1k
  }
616
617
4.06k
  return false;
618
10.1k
}
619
620
void IWAParser::updateGeometryUsingTextRef(unsigned id, IWORKGeometry &geometry, unsigned flags)
621
62
{
622
  // no horizontal auto resize or width unknown
623
62
  if ((flags&1)==1 || geometry.m_size.m_width<=0) return;
624
58
  const ObjectMessage msg(*this, id);
625
58
  if (!msg)
626
0
    return;
627
58
  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
58
  if (msg.getType()!=IWAObjectType::Text)
639
0
  {
640
0
    ETONYEK_DEBUG_MSG(("IWAParser::updateGeometryUsingTextRef: unexpected object type, type=%d\n", int(msg.getType())));
641
0
    return;
642
0
  }
643
58
  if (!get(msg).message(5))
644
1
    return;
645
  // ok, let find the paragraph style for pos=0
646
57
  for (const auto &it : get(msg).message(5).message(1))
647
73
  {
648
73
    if (it.uint32(1) && get(it.uint32(1))!=0) continue;
649
57
    const optional<unsigned> &styleRef = readRef(it, 2);
650
57
    if (!styleRef) return;
651
652
57
    const IWORKStylePtr_t &style = queryParagraphStyle(get(styleRef));
653
57
    if (!bool(style)) return;
654
30
    if (geometry.m_size.m_width>0 && style->has<property::Alignment>())
655
24
    {
656
24
      switch (style->get<property::Alignment>())
657
24
      {
658
4
      case IWORK_ALIGNMENT_RIGHT :
659
4
        geometry.m_position.m_x -= geometry.m_size.m_width;
660
4
        break;
661
15
      case IWORK_ALIGNMENT_CENTER :
662
15
        geometry.m_position.m_x -= geometry.m_size.m_width/2.;
663
15
        break;
664
5
      case IWORK_ALIGNMENT_LEFT :
665
5
      case IWORK_ALIGNMENT_JUSTIFY :
666
5
      case IWORK_ALIGNMENT_AUTOMATIC:
667
5
      default:
668
5
        break;
669
24
      }
670
24
    }
671
30
  }
672
57
}
673
674
bool IWAParser::parseText(const unsigned id, bool createNoteAsFootnote, const std::function<void(unsigned, IWORKStylePtr_t)> &openPageFunction)
675
14.4k
{
676
14.4k
  assert(bool(m_currentText));
677
14.4k
  const ObjectMessage msg(*this, id);
678
14.4k
  if (!msg)
679
79
    return false;
680
14.3k
  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
14.3k
  if (msg.getType()!=IWAObjectType::Text)
691
34
  {
692
34
    ETONYEK_DEBUG_MSG(("IWAParser::parseText: unexpected object type, type=%d\n", int(msg.getType())));
693
34
    return false;
694
34
  }
695
14.3k
  std::multimap<unsigned, std::function<void(unsigned, bool &)> > attachments;
696
14.3k
  const IWAStringField &text = get(msg).string(3);
697
  // fixme: in Numbers, if text does not exists, the default text string is still displayed
698
14.3k
  if (text || (openPageFunction && get(msg).message(17)))
699
3.84k
  {
700
    // special case, when the document is empty and openPageFunction is
701
    //          defined, we still need to retrieve the headers/footers
702
3.84k
    IWAText textParser(get_optional_value_or(text," "), m_langManager);
703
3.84k
    const size_t length = text ? get(text).size() : 1;
704
705
3.84k
    if (get(msg).message(5))
706
3.43k
    {
707
3.43k
      map<unsigned, IWORKStylePtr_t> paras;
708
3.43k
      IWORKStylePtr_t style = make_shared<IWORKStyle>(IWORKPropertyMap(), none, none);
709
3.43k
      for (const auto &it : get(msg).message(5).message(1))
710
6.94k
      {
711
6.94k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
712
6.55k
        {
713
6.55k
          const optional<unsigned> &styleRef = readRef(it, 2);
714
6.55k
          if (styleRef)
715
3.29k
          {
716
3.29k
            const IWORKStylePtr_t &newStyle = queryParagraphStyle(get(styleRef));
717
3.29k
            if (bool(newStyle))
718
1.11k
              style = newStyle;
719
3.29k
          }
720
6.55k
          paras.insert(paras.end(), make_pair(get(it.uint32(1)), style));
721
6.55k
        }
722
6.94k
      }
723
3.43k
      textParser.setParagraphs(paras);
724
3.43k
    }
725
726
3.84k
    if (get(msg).message(6))
727
3.53k
    {
728
3.53k
      map<unsigned, unsigned> levels;
729
3.53k
      for (const auto &it : get(msg).message(6).message(1))
730
6.36k
      {
731
6.36k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
732
5.86k
          levels.insert(levels.end(), make_pair(get(it.uint32(1)), get_optional_value_or(it.uint32(2), 0)));
733
6.36k
      }
734
3.53k
      textParser.setListLevels(levels);
735
3.53k
    }
736
737
3.84k
    if (get(msg).message(7))
738
3.33k
    {
739
3.33k
      map<unsigned, IWORKStylePtr_t> lists;
740
3.33k
      for (const auto &it : get(msg).message(7).message(1))
741
3.70k
      {
742
3.70k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
743
3.24k
        {
744
3.24k
          IWORKStylePtr_t style;
745
3.24k
          const optional<unsigned> &styleRef = readRef(it, 2);
746
3.24k
          if (styleRef)
747
3.00k
            style = queryListStyle(get(styleRef));
748
3.24k
          lists.insert(lists.end(), make_pair(get(it.uint32(1)), style));
749
3.24k
        }
750
3.70k
      }
751
3.33k
      textParser.setLists(lists);
752
3.33k
    }
753
754
3.84k
    if (get(msg).message(8))
755
197
    {
756
197
      map<unsigned, IWORKStylePtr_t> spans;
757
197
      for (const auto &it : get(msg).message(8).message(1))
758
505
      {
759
505
        if (it.uint32(1) && (get(it.uint32(1)) < length))
760
473
        {
761
473
          IWORKStylePtr_t style;
762
473
          const optional<unsigned> &styleRef = readRef(it, 2);
763
473
          if (styleRef)
764
277
            style = queryCharacterStyle(get(styleRef));
765
473
          spans.insert(spans.end(), make_pair(get(it.uint32(1)), style));
766
473
        }
767
505
      }
768
197
      textParser.setSpans(spans);
769
197
    }
770
3.84k
    if (get(msg).message(9))
771
235
    {
772
235
      map<unsigned, IWORKFieldType> fields;
773
235
      for (const auto &it : get(msg).message(9).message(1))
774
249
      {
775
249
        if (!it.uint32(1))
776
24
        {
777
24
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: can not find the position\n"));
778
24
          continue;
779
24
        }
780
225
        const optional<unsigned> &ref = readRef(it, 2);
781
225
        if (!ref) continue;
782
201
        const ObjectMessage attachment(*this, get(ref));
783
201
        if (!attachment) continue;
784
163
        switch (attachment.getType())
785
163
        {
786
0
        case IWAObjectType::NoteStart: // the first character of a note seems special, so ...
787
0
          attachments.insert(make_pair(get(it.uint32(1)), [](unsigned, bool &ignore)
788
0
          {
789
0
            ignore=true;
790
0
          }));
791
0
          continue;
792
3
        case IWAObjectType::PageField:
793
3
        {
794
3
          if (!get(attachment).message(1)) break;
795
3
          const auto &field=get(get(attachment).message(1));
796
3
          if (field.uint32(2))
797
3
          {
798
3
            switch (get(field.uint32(2)))
799
3
            {
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
0
            default:
817
0
              ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: unknown field enum=%d\n", int(get(field.uint32(2)))));
818
3
            }
819
3
            continue;
820
3
          }
821
0
          break;
822
3
        }
823
0
        case IWAObjectType::ShapeField:
824
0
          if (!openPageFunction)
825
0
          {
826
0
            ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: find unexpected shape's attachment at pos=%d\n", int(get(it.uint32(1)))));
827
0
          }
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
0
          continue;
836
158
        default:
837
158
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: find unknown object %d at position=%d\n", int(attachment.getType()), int(get(it.uint32(1)))));
838
158
          continue;
839
163
        }
840
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseText[9]: can not read the object at position=%d\n", int(get(it.uint32(1)))));
841
0
      }
842
235
    }
843
3.83k
    if (get(msg).message(11))
844
138
    {
845
      // placeholder:2031 or link:2032 or time field:2034
846
138
      map<unsigned, string> links;
847
138
      for (const auto &it : get(msg).message(11).message(1))
848
310
      {
849
310
        if (it.uint32(1))
850
280
        {
851
280
          string url;
852
280
          const optional<unsigned> &linkRef = readRef(it, 2);
853
280
          if (linkRef)
854
49
            parseLink(get(linkRef), url);
855
280
          links.insert(links.end(), make_pair(get(it.uint32(1)), url));
856
280
        }
857
310
      }
858
138
      textParser.setLinks(links);
859
138
    }
860
3.83k
    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
3.83k
    if (get(msg).message(16))
875
19
    {
876
19
      for (const auto &it : get(msg).message(16).message(1))
877
19
      {
878
19
        if (!it.uint32(1)) continue;
879
8
        const optional<unsigned> &noteRef = readRef(it, 2);
880
8
        if (!noteRef) continue;
881
1
        const ObjectMessage noteMsg(*this, get(noteRef), IWAObjectType::Note);
882
1
        if (!noteMsg) continue;
883
1
        auto textRef=readRef(get(noteMsg), 2);
884
1
        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
1
        else
908
1
        {
909
1
          ETONYEK_DEBUG_MSG(("IWAParser::parseText[16]: can not find a note\n"));
910
1
        }
911
1
      }
912
19
    }
913
3.83k
    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
3.83k
    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.24k
      {
932
2.24k
        if (it.uint32(1))
933
2.17k
          langs.insert(langs.end(), make_pair(get(it.uint32(1)), get_optional_value_or(it.string(2), "")));
934
2.24k
      }
935
1.78k
      textParser.setLanguages(langs);
936
1.78k
    }
937
3.83k
    if (get(msg).message(24))
938
3.59k
    {
939
3.59k
      map<unsigned, bool> rtls;
940
3.59k
      for (const auto &it : get(msg).message(24).message(1))
941
3.67k
      {
942
3.67k
        if (it.uint32(1) && (get(it.uint32(1)) < length))
943
3.33k
        {
944
3.33k
          if (it.bool_(2))
945
3.28k
            rtls[get(it.uint32(1))]=get(it.bool_(2));
946
3.33k
        }
947
3.67k
      }
948
3.59k
      textParser.setRTLs(rtls);
949
3.59k
    }
950
3.83k
    if (get(msg).message(28))
951
31
    {
952
31
      map<unsigned, IWORKStylePtr_t> dropCaps;
953
31
      for (const auto &it : get(msg).message(28).message(1))
954
40
      {
955
40
        if (!it.uint32(1)) continue;
956
13
        const optional<unsigned> &dropCapRef = readRef(it, 2);
957
13
        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
31
      textParser.setDropCaps(dropCaps);
963
31
    }
964
3.83k
    if (get(msg).message(23))
965
26
    {
966
26
      for (const auto &it : get(msg).message(23).message(1))
967
29
      {
968
        // no position
969
29
        if (!it.uint32(1)) continue;
970
12
        if (!it.message(2)) continue; // no text ref means end of comment, ...
971
9
        auto const &commentRef = readRef(it, 2);
972
9
        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
26
    }
999
3.83k
    textParser.setAttachments(attachments);
1000
3.83k
    textParser.parse(*m_currentText, openPageFunction);
1001
3.83k
  }
1002
14.3k
  return true;
1003
14.3k
}
1004
1005
const IWORKStylePtr_t IWAParser::queryStyle(const unsigned id, StyleMap_t &styleMap, StyleParseFun_t parseStyle) const
1006
25.2k
{
1007
25.2k
  StyleMap_t::const_iterator it = styleMap.find(id);
1008
25.2k
  if (it == styleMap.end())
1009
12.6k
  {
1010
12.6k
    IWORKStylePtr_t style;
1011
12.6k
    parseStyle(id, style);
1012
12.6k
    it = styleMap.insert(make_pair(id, style)).first;
1013
12.6k
  }
1014
25.2k
  assert(it != styleMap.end());
1015
25.2k
  return it->second;
1016
25.2k
}
1017
1018
const IWORKStylePtr_t IWAParser::queryCharacterStyle(const unsigned id) const
1019
343
{
1020
343
  return queryStyle(id, m_charStyles, bind(&IWAParser::parseCharacterStyle, const_cast<IWAParser *>(this), _1, _2));
1021
343
}
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
4.63k
{
1030
4.63k
  return queryStyle(id, m_paraStyles, bind(&IWAParser::parseParagraphStyle, const_cast<IWAParser *>(this), _1, _2));
1031
4.63k
}
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
11.0k
{
1040
11.0k
  return queryStyle(id, m_graphicStyles, bind(&IWAParser::parseGraphicStyle, const_cast<IWAParser *>(this), _1, _2));
1041
11.0k
}
1042
1043
const IWORKStylePtr_t IWAParser::queryMediaStyle(const unsigned id) const
1044
467
{
1045
467
  return queryStyle(id, m_mediaStyles, bind(&IWAParser::parseMediaStyle, const_cast<IWAParser *>(this), _1, _2));
1046
467
}
1047
1048
const IWORKStylePtr_t IWAParser::queryCellStyle(const unsigned id) const
1049
499
{
1050
499
  return queryStyle(id, m_cellStyles, bind(&IWAParser::parseCellStyle, const_cast<IWAParser *>(this), _1, _2));
1051
499
}
1052
1053
const IWORKStylePtr_t IWAParser::queryTableStyle(const unsigned id) const
1054
138
{
1055
138
  return queryStyle(id, m_tableStyles, bind(&IWAParser::parseTableStyle, const_cast<IWAParser *>(this), _1, _2));
1056
138
}
1057
1058
const IWORKStylePtr_t IWAParser::queryListStyle(const unsigned id) const
1059
3.20k
{
1060
3.20k
  return queryStyle(id, m_tableStyles, bind(&IWAParser::parseListStyle, const_cast<IWAParser *>(this), _1, _2));
1061
3.20k
}
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
182
{
1162
182
  IWORKMarker marker;
1163
182
  bool hasPath=false;
1164
182
  if (arrow.message(1))
1165
128
  {
1166
128
    const auto &arrowProp=get(arrow.message(1));
1167
128
    IWORKPathPtr_t path;
1168
128
    if (parsePath(arrowProp, path) && path && !path->str().empty())
1169
94
    {
1170
94
      marker.m_path=path->str();
1171
94
      hasPath=true;
1172
94
    }
1173
128
  }
1174
182
  marker.m_endPoint=readPosition(arrow,3);
1175
  // 2: a bool, 4: a bool, 5: name
1176
182
  if (headArrow)
1177
57
  {
1178
57
    if (hasPath)
1179
53
      props.put<property::HeadLineEnd>(marker);
1180
4
    else
1181
4
      props.clear<property::HeadLineEnd>();
1182
57
  }
1183
125
  else
1184
125
  {
1185
125
    if (hasPath)
1186
41
      props.put<property::TailLineEnd>(marker);
1187
84
    else
1188
84
      props.clear<property::TailLineEnd>();
1189
125
  }
1190
182
  return true;
1191
182
}
1192
1193
bool IWAParser::parsePath(const IWAMessage &msg, IWORKPathPtr_t &path)
1194
2.59k
{
1195
2.59k
  const deque<IWAMessage> &elements = msg.message(1).repeated();
1196
2.59k
  bool closed = false;
1197
2.59k
  bool closingMove = false;
1198
2.59k
  path.reset(new IWORKPath());
1199
2.59k
  for (auto it : elements)
1200
14.4k
  {
1201
14.4k
    const auto &type = it.uint32(1).optional();
1202
14.4k
    if (!type)
1203
123
    {
1204
123
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: can not read the type\n"));
1205
123
      continue;
1206
123
    }
1207
14.2k
    if (closed && closingMove)
1208
0
    {
1209
0
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: unexpected element %c after the closing move\n", get(type)));
1210
0
      break;
1211
0
    }
1212
14.2k
    switch (get(type))
1213
14.2k
    {
1214
4.76k
    case 1 :
1215
4.76k
      if (closed)
1216
2.19k
      {
1217
2.19k
        closingMove = true;
1218
2.19k
        break;
1219
2.19k
      }
1220
2.57k
      ETONYEK_FALLTHROUGH;
1221
9.28k
    case 2 :
1222
9.28k
    {
1223
9.28k
      const optional<IWORKPosition> &coords = readPosition(it, 2);
1224
9.28k
      if (!coords)
1225
7
      {
1226
7
        ETONYEK_DEBUG_MSG(("IWAParser::parsePath: missing coordinates for %c element\n", get(type) == 1 ? 'M' : 'L'));
1227
7
        return false;
1228
7
      }
1229
9.27k
      if (get(type) == 1)
1230
2.57k
        path->appendMoveTo(get(coords).m_x, get(coords).m_y);
1231
6.70k
      else
1232
6.70k
      {
1233
6.70k
        if (path->empty())
1234
8
        {
1235
8
          ETONYEK_DEBUG_MSG(("IWAParser::parsePath: missing prior MoveTo subsequent LineTo\n"));
1236
8
          return false;
1237
8
        }
1238
6.69k
        path->appendLineTo(get(coords).m_x, get(coords).m_y);
1239
6.69k
      }
1240
9.26k
      break;
1241
9.27k
    }
1242
9.26k
    case 4 :
1243
581
    {
1244
581
      if (it.message(2))
1245
580
      {
1246
580
        const std::deque<IWAMessage> &positions = it.message(2).repeated();
1247
580
        if (positions.size() >= 3)
1248
579
        {
1249
579
          if (positions.size() > 3)
1250
9
          {
1251
9
            ETONYEK_DEBUG_MSG(("IWAParser::parsePath: a curve has got %u control coords\n", unsigned(positions.size())));
1252
9
          }
1253
579
          const optional<float> &x = positions[0].float_(1).optional();
1254
579
          const optional<float> &y = positions[0].float_(2).optional();
1255
579
          const optional<float> &x1 = positions[1].float_(1).optional();
1256
579
          const optional<float> &y1 = positions[1].float_(2).optional();
1257
579
          const optional<float> &x2 = positions[2].float_(1).optional();
1258
579
          const optional<float> &y2 = positions[2].float_(2).optional();
1259
579
          path->appendCCurveTo(get_optional_value_or(x, 0), get_optional_value_or(y, 0),
1260
579
                               get_optional_value_or(x1, 0), get_optional_value_or(y1, 0),
1261
579
                               get_optional_value_or(x2, 0), get_optional_value_or(y2, 0));
1262
579
        }
1263
1
        else
1264
1
        {
1265
1
          ETONYEK_DEBUG_MSG(("IWAParser::parsePath: %u is not enough coords for a curve\n", unsigned(positions.size())));
1266
1
          return false;
1267
1
        }
1268
580
      }
1269
580
      break;
1270
581
    }
1271
2.22k
    case 5 :
1272
2.22k
      path->appendClose();
1273
2.22k
      closed = true;
1274
2.22k
      break;
1275
12
    default :
1276
12
      ETONYEK_DEBUG_MSG(("IWAParser::parsePath: unknown bezier path element type %u\n", get(type)));
1277
12
      return false;
1278
14.2k
    }
1279
14.2k
  }
1280
2.55k
  return true;
1281
2.59k
}
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
3.52k
{
1291
3.52k
  m_collector.startLevel();
1292
1293
3.52k
  const optional<IWAMessage> &shape = msg.message(1).optional();
1294
3.52k
  const optional<unsigned> &textRef = readRef(msg, 2);
1295
3.52k
  boost::optional<unsigned> resizeFlags;
1296
1297
3.52k
  if (shape)
1298
3.50k
  {
1299
3.50k
    const optional<IWAMessage> &placement = get(shape).message(1).optional();
1300
3.50k
    IWORKStylePtr_t style;
1301
3.50k
    const optional<unsigned> styleRef = readRef(get(shape), 2);
1302
3.50k
    if (styleRef)
1303
3.34k
      style=queryGraphicStyle(get(styleRef));
1304
3.50k
    const optional<IWAMessage> &path = get(shape).message(3).optional();
1305
3.50k
    if (placement)
1306
3.29k
    {
1307
3.29k
      IWORKGeometryPtr_t geometry;
1308
3.29k
      parseShapePlacement(get(placement), geometry, resizeFlags);
1309
3.29k
      if (geometry && (geometry->m_naturalSize.m_width<=0 || geometry->m_naturalSize.m_height<=0) && path)
1310
172
      {
1311
        // try to retrieve the shape's size in the path
1312
172
        std::map<unsigned,unsigned> const cIdToSizeId= { { 3, 3}, { 4, 3}, {5, 2}, { 6, 1}, { 8, 2} };
1313
172
        for (auto const &it : cIdToSizeId)
1314
722
        {
1315
722
          if (!get(path).message(it.first)) continue;
1316
162
          auto const &pathSize=readSize(get(get(path).message(it.first)), it.second);
1317
162
          if (!pathSize || pathSize->m_width<=0 || pathSize->m_height<=0) continue;
1318
67
          geometry->m_naturalSize=geometry->m_size=get(pathSize);
1319
67
          break;
1320
162
        }
1321
172
      }
1322
3.29k
      if (geometry && resizeFlags && (get(resizeFlags) &1)==0 && textRef) // correct horizontal position
1323
62
        updateGeometryUsingTextRef(get(textRef), *geometry, get(resizeFlags));
1324
3.29k
      if (geometry && resizeFlags && (get(resizeFlags)&2)==0 && geometry->m_size.m_height>0 && style && style->has<property::VerticalAlignment>())
1325
59
      {
1326
        // correct vertical position
1327
59
        switch (style->get<property::VerticalAlignment>())
1328
59
        {
1329
44
        case IWORK_VERTICAL_ALIGNMENT_MIDDLE:
1330
44
          geometry->m_position.m_y -= geometry->m_size.m_height/2.;
1331
44
          break;
1332
3
        case IWORK_VERTICAL_ALIGNMENT_BOTTOM:
1333
3
          geometry->m_position.m_y -= geometry->m_size.m_height;
1334
3
          break;
1335
12
        case IWORK_VERTICAL_ALIGNMENT_TOP:
1336
12
        default:
1337
12
          break;
1338
59
        }
1339
59
      }
1340
1341
3.29k
      m_collector.collectGeometry(geometry);
1342
3.29k
    }
1343
1344
    // look for arrow Keynote 6
1345
3.50k
    if (get(shape).message(4) || get(shape).message(5))
1346
58
    {
1347
58
      if (!style)
1348
55
        style=std::make_shared<IWORKStyle>(IWORKPropertyMap(),boost::none, boost::none);
1349
169
      for (size_t st=0; st<2; ++st)
1350
111
      {
1351
111
        if (!get(shape).message(st+4)) continue;
1352
58
        parseArrowProperties(get(get(shape).message(st+4)),style->getPropertyMap(),st==0);
1353
58
      }
1354
58
    }
1355
3.50k
    if (style)
1356
2.05k
      m_collector.setGraphicStyle(style);
1357
1358
3.50k
    if (path)
1359
3.25k
    {
1360
3.25k
      if (get(path).message(3)) // point path
1361
455
      {
1362
455
        const IWAMessage &pointPath = get(path).message(3).get();
1363
455
        const optional<unsigned> &type = pointPath.uint32(1).optional();
1364
455
        const optional<IWORKPosition> &point = readPosition(pointPath, 2);
1365
455
        const optional<IWORKSize> &size = readSize(pointPath, 3);
1366
455
        if (type && point && size)
1367
407
        {
1368
407
          switch (get(type))
1369
407
          {
1370
214
          case 1 :
1371
340
          case 10 :
1372
340
            m_collector.collectArrowPath(get(size), get(point).m_x, get(point).m_y, get(type) == 10);
1373
340
            break;
1374
67
          case 100 :
1375
67
            m_collector.collectStarPath(get(size), unsigned(get(point).m_x+0.4), get(point).m_y);
1376
67
            break;
1377
0
          default :
1378
0
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: unknown point path type %u\n", get(type)));
1379
0
            break;
1380
407
          }
1381
407
        }
1382
455
      }
1383
2.79k
      else if (get(path).message(4)) // scalar path
1384
121
      {
1385
121
        const IWAMessage &scalarPath = get(path).message(4).get();
1386
121
        const optional<unsigned> &type = scalarPath.uint32(1).optional();
1387
121
        const optional<float> &value = scalarPath.float_(2).optional();
1388
121
        const optional<IWORKSize> &size = readSize(scalarPath, 3);
1389
121
        if (type && value && size)
1390
109
        {
1391
109
          switch (get(type))
1392
109
          {
1393
74
          case 0 :
1394
74
            m_collector.collectRoundedRectanglePath(get(size), get(value));
1395
74
            break;
1396
35
          case 1 :
1397
35
            m_collector.collectPolygonPath(get(size), unsigned(get(value)+0.4));
1398
35
            break;
1399
0
          default :
1400
0
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: unknown scalar path type %u\n", get(type)));
1401
0
            break;
1402
109
          }
1403
109
        }
1404
121
      }
1405
2.67k
      else if (get(path).message(5))
1406
2.47k
      {
1407
2.47k
        auto const &bezier = get(path).message(5).get().message(3).optional();
1408
2.47k
        if (bezier)
1409
2.46k
        {
1410
2.46k
          IWORKPathPtr_t bezierPath;
1411
2.46k
          if (parsePath(get(bezier),bezierPath))
1412
2.43k
          {
1413
2.43k
            const optional<IWORKSize> &size = readSize(get(get(path).message(5)), 2);
1414
2.43k
            if (size)
1415
2.41k
            {
1416
2.41k
              double x[2]= {0,0}, y[2]= {0,0};
1417
2.41k
              if (bezierPath)
1418
2.41k
                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
2.41k
              *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
2.41k
            }
1422
2.43k
            m_collector.collectBezier(bezierPath);
1423
2.43k
            m_collector.collectBezierPath();
1424
2.43k
          }
1425
2.46k
        }
1426
2.47k
      }
1427
202
      else if (get(path).message(6)) // callout2 path
1428
117
      {
1429
117
        const IWAMessage &callout2Path = get(path).message(6).get();
1430
117
        const optional<IWORKSize> &size = readSize(callout2Path, 1);
1431
117
        const optional<IWORKPosition> &tailPos = readPosition(callout2Path, 2);
1432
117
        const optional<float> &tailSize = callout2Path.float_(3).optional();
1433
117
        if (size && tailPos && tailSize)
1434
106
        {
1435
106
          const optional<float> &cornerRadius = callout2Path.float_(4).optional();
1436
106
          const optional<bool> &tailAtCenter = callout2Path.bool_(5).optional();
1437
106
          m_collector.collectCalloutPath(get(size), get_optional_value_or(cornerRadius, 0),
1438
106
                                         get(tailSize), get(tailPos).m_x, get(tailPos).m_y,
1439
106
                                         get_optional_value_or(tailAtCenter, false));
1440
106
        }
1441
117
      }
1442
85
      else if (get(path).message(7))
1443
11
      {
1444
11
        auto rootMsg=get(get(path).message(7)).message(1).optional();
1445
11
        if (rootMsg)
1446
7
        {
1447
7
          IWORKConnectionPath cPath;
1448
7
          cPath.m_size=readSize(get(rootMsg), 2);
1449
7
          cPath.m_isSpline=!get_optional_value_or(get(get(path).message(7)).bool_(2),false);
1450
7
          auto const &bezier = rootMsg.get().message(3).optional();
1451
7
          if (bezier)
1452
1
          {
1453
1
            const deque<IWAMessage> &elements = get(bezier).message(1).repeated();
1454
1
            int pos=0;
1455
1
            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
1
            if (pos==3)
1467
0
            {
1468
0
              m_collector.collectConnectionPath(cPath);
1469
0
            }
1470
1
            else
1471
1
            {
1472
1
              ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: oops find unexpected number of points\n"));
1473
1
            }
1474
1
          }
1475
6
          else
1476
6
          {
1477
6
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: can not find the points zone\n"));
1478
6
          }
1479
7
        }
1480
4
        else
1481
4
        {
1482
4
          ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape[connection]: can not find the root bezier zone\n"));
1483
4
        }
1484
11
      }
1485
74
      else if (get(path).message(8)) // editable path
1486
14
      {
1487
14
        const IWAMessageField &pathPoints = get(get(path).message(8)).message(1);
1488
14
        if (pathPoints && !pathPoints.message(1).empty())
1489
4
        {
1490
4
          const IWORKPathPtr_t editablePath(new IWORKPath());
1491
4
          const IWAMessageField &points = pathPoints.message(1);
1492
4
          std::vector<IWORKPosition> positions;
1493
          // cubic bezier patch, [prev pt dir], pt, [next pt dir]
1494
4
          for (auto it : points)
1495
17
          {
1496
17
            const optional<IWORKPosition> &point1 = readPosition(it, 1);
1497
17
            const optional<IWORKPosition> &point2 = readPosition(it, 2);
1498
17
            const optional<IWORKPosition> &point3 = readPosition(it, 3);
1499
            // [4 type: {1: line, 3:curve}
1500
17
            if (!point2)
1501
2
            {
1502
2
              ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: no control points for point2\n"));
1503
2
              continue;
1504
2
            }
1505
15
            positions.push_back(get_optional_value_or(point1, get(point2)));
1506
15
            positions.push_back(get(point2));
1507
15
            positions.push_back(get_optional_value_or(point3, get(point2)));
1508
15
          }
1509
4
          size_t nbPt=positions.size()/3;
1510
4
          if (nbPt<=1)
1511
0
          {
1512
0
            ETONYEK_DEBUG_MSG(("IWAParser::parseDrawableShape: find only %d points\n", int(nbPt)));
1513
0
          }
1514
4
          else
1515
4
          {
1516
4
            editablePath->appendMoveTo(positions[1].m_x, positions[1].m_y);
1517
4
            bool isClosed= get_optional_value_or(pathPoints.bool_(2),false);
1518
16
            for (size_t i=0; i<nbPt; ++i)
1519
12
            {
1520
12
              if (i+1==nbPt && !isClosed) break;
1521
12
              auto const &prevPoint=positions[3*i+1];
1522
12
              auto const &pt1=positions[3*i+2];
1523
12
              auto const &pt2=positions[3*((i+1)%nbPt)];
1524
12
              auto const &pt3=positions[3*((i+1)%nbPt)+1];
1525
12
              if (samePoint(prevPoint,pt1) && samePoint(pt2,pt3))
1526
12
                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
12
            }
1530
4
            if (isClosed)
1531
3
              editablePath->appendClose();
1532
4
            m_collector.collectBezier(editablePath);
1533
4
            m_collector.collectBezierPath();
1534
4
          }
1535
4
        }
1536
14
      }
1537
3.25k
    }
1538
3.50k
  }
1539
3.52k
  bool hasText=false;
1540
3.52k
  if (!isConnectionLine && textRef)
1541
3.32k
  {
1542
3.32k
    m_currentText = m_collector.createText(m_langManager, true);
1543
3.32k
    parseText(get(textRef));
1544
3.32k
    if (!m_currentText->empty())
1545
551
    {
1546
551
      hasText=true;
1547
551
      m_collector.collectText(m_currentText);
1548
551
    }
1549
3.32k
  }
1550
1551
3.52k
  if (shape || hasText)
1552
3.38k
    m_collector.collectShape(boost::none, resizeFlags);
1553
3.52k
  m_currentText.reset();
1554
1555
3.52k
  m_collector.endLevel();
1556
1557
3.52k
  return true;
1558
3.52k
}
1559
1560
bool IWAParser::parseGroup(const IWAMessage &msg)
1561
125
{
1562
125
  m_collector.startLevel();
1563
125
  if (msg.message(1))
1564
124
    parseShapePlacement(get(msg.message(1)));
1565
125
  if (!msg.message(2).empty())
1566
124
  {
1567
124
    m_collector.startGroup();
1568
124
    m_collector.openGroup();
1569
124
    const deque<unsigned> &shapeRefs = readRefs(msg, 2);
1570
124
    std::for_each(shapeRefs.begin(), shapeRefs.end(), bind(&IWAParser::dispatchShape, this, _1));
1571
124
    m_collector.closeGroup();
1572
124
    m_collector.endGroup();
1573
124
  }
1574
125
  m_collector.endLevel();
1575
1576
125
  return true;
1577
125
}
1578
1579
bool IWAParser::parseShapePlacement(const IWAMessage &msg, IWORKGeometryPtr_t &geometry, boost::optional<unsigned> &flags)
1580
10.3k
{
1581
10.3k
  geometry = make_shared<IWORKGeometry>();
1582
10.3k
  flags=3; // no auto resize
1583
1584
10.3k
  const optional<IWAMessage> &g = msg.message(1).optional();
1585
10.3k
  if (g)
1586
10.2k
  {
1587
10.2k
    const optional<IWORKPosition> &pos = readPosition(get(g), 1);
1588
10.2k
    if (pos)
1589
10.1k
      geometry->m_position = get(pos);
1590
10.2k
    const optional<IWORKSize> &size = readSize(get(g), 2);
1591
10.2k
    if (size)
1592
10.1k
      geometry->m_naturalSize = geometry->m_size = get(size);
1593
10.2k
    if (get(g).uint32(3))
1594
10.0k
    {
1595
10.0k
      flags=get(get(g).uint32(3));
1596
      // flags&1 : horizontal position is fixed
1597
      // flags&2 : vertical position is fixed
1598
10.0k
      if (get(flags)&4) // horizontal flip
1599
123
        geometry->m_horizontalFlip = true;
1600
10.0k
      if (get(flags)&0xFFF8)
1601
21
      {
1602
21
        ETONYEK_DEBUG_MSG(("IWAParser::parseShapePlacement: unknown transformation %u\n", get(flags)));
1603
21
      }
1604
10.0k
    }
1605
10.2k
    if (get(g).float_(4))
1606
10.0k
      geometry->m_angle = -deg2rad(get(get(g).float_(4)));
1607
10.2k
  }
1608
10.3k
  geometry->m_aspectRatioLocked = msg.bool_(7).optional();
1609
1610
10.3k
  return true;
1611
10.3k
}
1612
1613
bool IWAParser::parseShapePlacement(const IWAMessage &msg)
1614
229
{
1615
229
  IWORKGeometryPtr_t geometry;
1616
229
  boost::optional<unsigned> flags;
1617
229
  const bool retval = parseShapePlacement(msg, geometry, flags);
1618
229
  m_collector.collectGeometry(geometry);
1619
229
  return retval;
1620
229
}
1621
1622
void IWAParser::parseMask(unsigned id, IWORKGeometryPtr_t &geometry, IWORKPathPtr_t &/*path*/)
1623
230
{
1624
230
  const ObjectMessage msg(*this, id, IWAObjectType::Mask);
1625
230
  if (!msg)
1626
21
    return;
1627
209
  if (get(msg).message(1))
1628
124
  {
1629
124
    boost::optional<unsigned> flags;
1630
124
    parseShapePlacement(get(get(msg).message(1)), geometry, flags);
1631
124
  }
1632
  // if (get(msg).message(2)) same code as parseDrawableShape
1633
209
}
1634
1635
void IWAParser::parseObjectIndex()
1636
1.77k
{
1637
1.77k
  m_index.parse();
1638
1.77k
}
1639
1640
void IWAParser::parseCharacterStyle(const unsigned id, IWORKStylePtr_t &style)
1641
220
{
1642
220
  const ObjectMessage msg(*this, id, IWAObjectType::CharacterStyle);
1643
220
  if (!msg)
1644
110
    return;
1645
1646
110
  optional<string> name;
1647
110
  IWORKStylePtr_t parent;
1648
110
  const IWAMessageField &styleInfo = get(msg).message(1);
1649
110
  if (styleInfo)
1650
110
  {
1651
110
    name = styleInfo.string(2).optional();
1652
110
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1653
110
    if (parentRef)
1654
66
      parent = queryCharacterStyle(get(parentRef));
1655
110
  }
1656
1657
110
  IWORKPropertyMap props;
1658
110
  if (get(msg).message(11))
1659
90
    parseCharacterProperties(get(get(msg).message(11)), props);
1660
1661
110
  style = std::make_shared<IWORKStyle>(props, name, parent);
1662
110
}
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
2.99k
{
1698
2.99k
  const ObjectMessage msg(*this, id, IWAObjectType::ParagraphStyle);
1699
2.99k
  if (!msg)
1700
1.32k
    return;
1701
1702
1.67k
  optional<string> name;
1703
1.67k
  IWORKStylePtr_t parent;
1704
1.67k
  const IWAMessageField &styleInfo = get(msg).message(1);
1705
1.67k
  if (styleInfo)
1706
1.64k
  {
1707
1.64k
    name = styleInfo.string(2).optional();
1708
1.64k
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1709
1.64k
    if (parentRef)
1710
278
      parent = queryParagraphStyle(get(parentRef));
1711
1.64k
  }
1712
1713
1.67k
  IWORKPropertyMap props;
1714
1.67k
  if (get(msg).message(11))
1715
1.61k
    parseCharacterProperties(get(get(msg).message(11)), props);
1716
1.67k
  if (get(msg).message(12))
1717
1.59k
  {
1718
1.59k
    const IWAMessage &paraProps = get(get(msg).message(12));
1719
1.59k
    using namespace property;
1720
1721
1.59k
    if (paraProps.uint32(1))
1722
1.35k
      putEnum<Alignment>(props, get(paraProps.uint32(1)));
1723
1.59k
    const optional<IWORKColor> &fillColor = readColor(paraProps, 6);
1724
1.59k
    if (fillColor)
1725
0
      props.put<ParagraphFill>(get(fillColor));
1726
1.59k
    if (paraProps.float_(7))
1727
1.29k
      props.put<FirstLineIndent>(get(paraProps.float_(7)));
1728
1.59k
    if (paraProps.bool_(8))
1729
1.30k
      props.put<Hyphenate>(get(paraProps.bool_(8)));
1730
1.59k
    if (paraProps.bool_(9))
1731
1.39k
      props.put<KeepLinesTogether>(get(paraProps.bool_(9)));
1732
1.59k
    if (paraProps.bool_(10))
1733
1.29k
      props.put<KeepWithNext>(get(paraProps.bool_(10)));
1734
1.59k
    if (paraProps.float_(11))
1735
1.29k
      props.put<LeftIndent>(get(paraProps.float_(11)));
1736
1.59k
    if (paraProps.message(13))
1737
1.29k
    {
1738
1.29k
      auto const &lineSpace=paraProps.message(13).float_(2);
1739
1.29k
      if (lineSpace)
1740
126
      {
1741
126
        auto const &type=paraProps.message(13).uint32(1).optional();
1742
126
        if (!type) // in line
1743
126
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), true));
1744
0
        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
0
        else if (get(type)==2) // in point
1751
0
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), false));
1752
0
        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
0
        else   // unknown, use heuristic
1755
0
        {
1756
0
          props.put<LineSpacing>(IWORKLineSpacing(get(lineSpace), get(lineSpace)<3));
1757
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseParagraphStyle: unknown type %u\n", get(type)));
1758
0
        }
1759
126
      }
1760
      // TODO what is paraProps.message(13).float_(3);
1761
1.29k
    }
1762
1.59k
    if (paraProps.bool_(14))
1763
1.29k
      props.put<PageBreakBefore>(get(paraProps.bool_(14)));
1764
1.59k
    if (paraProps.uint32(15))
1765
1.27k
      putEnum<ParagraphBorderType>(props, get(paraProps.uint32(15)));
1766
    // we need also to read the paragraphborder decal : field 17
1767
1.59k
    if (paraProps.float_(19))
1768
1.28k
      props.put<RightIndent>(get(paraProps.float_(19)));
1769
1.59k
    if (paraProps.float_(20))
1770
1.31k
      props.put<SpaceAfter>(get(paraProps.float_(20)));
1771
1.59k
    if (paraProps.float_(21))
1772
1.35k
      props.put<SpaceBefore>(get(paraProps.float_(21)));
1773
1.59k
    if (paraProps.message(25))
1774
1.30k
    {
1775
1.30k
      IWORKTabStops_t tabs;
1776
1.30k
      const IWAMessageField &tabStops = paraProps.message(25).message(1);
1777
1.30k
      for (const auto &tabStop : tabStops)
1778
0
      {
1779
0
        if (tabStop.float_(1))
1780
0
          tabs.push_back(IWORKTabStop(IWORK_TABULATION_LEFT, get(tabStop.float_(1))));
1781
0
      }
1782
1.30k
    }
1783
1.59k
    if (paraProps.bool_(26))
1784
1.30k
      props.put<WidowControl>(get(paraProps.bool_(26)));
1785
1.59k
    if (paraProps.message(32))
1786
0
    {
1787
0
      IWORKStroke stroke;
1788
0
      readStroke(get(paraProps.message(32)), stroke);
1789
0
      props.put<ParagraphStroke>(stroke);
1790
0
    }
1791
1.59k
  }
1792
1793
1.67k
  style = std::make_shared<IWORKStyle>(props, name, parent);
1794
1.67k
}
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
5.08k
{
1822
5.08k
  const ObjectMessage msg(*this, id, IWAObjectType::GraphicStyle);
1823
5.08k
  if (!msg)
1824
2.57k
    return;
1825
2.51k
  optional<string> name;
1826
2.51k
  IWORKStylePtr_t parent;
1827
2.51k
  IWORKPropertyMap props;
1828
1829
2.51k
  using namespace property;
1830
1831
2.51k
  if (get(msg).message(1))
1832
2.21k
  {
1833
2.21k
    const IWAMessageField &styleInfo = get(msg).message(1).message(1);
1834
2.21k
    if (styleInfo)
1835
2.17k
    {
1836
2.17k
      name = styleInfo.string(2).optional();
1837
2.17k
      const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1838
2.17k
      if (parentRef)
1839
1.41k
        parent = queryGraphicStyle(get(parentRef));
1840
2.17k
    }
1841
1842
2.21k
    const IWAMessageField &styleProps = get(msg).message(1).message(11);
1843
2.21k
    if (styleProps)
1844
2.09k
    {
1845
2.09k
      if (styleProps.message(1))
1846
1.18k
      {
1847
1.18k
        IWORKFill fill;
1848
1.18k
        if (readFill(get(styleProps.message(1)), fill))
1849
579
          props.put<Fill>(fill);
1850
607
        else
1851
607
          props.clear<Fill>();
1852
1.18k
      }
1853
2.09k
      if (styleProps.message(2))
1854
833
      {
1855
833
        IWORKStroke stroke;
1856
833
        readStroke(get(styleProps.message(2)), stroke);
1857
833
        props.put<Stroke>(stroke);
1858
833
      }
1859
2.09k
      if (styleProps.float_(3))
1860
639
        props.put<Opacity>(get(styleProps.float_(3)));
1861
2.09k
      if (styleProps.message(4))
1862
828
      {
1863
828
        IWORKShadow shadow;
1864
828
        readShadow(get(styleProps.message(4)),shadow);
1865
828
        props.put<Shadow>(shadow);
1866
828
      }
1867
6.11k
      for (size_t st=0; st<2; ++st)
1868
4.02k
      {
1869
4.02k
        if (!get(styleProps).message(st+6)) continue;
1870
125
        parseArrowProperties(get(get(styleProps).message(st+6)),props, st==0);
1871
125
      }
1872
2.09k
    }
1873
2.21k
  }
1874
1875
2.51k
  if (get(msg).message(11))
1876
2.08k
  {
1877
2.08k
    const IWAMessageField &layout = get(msg).message(11);
1878
2.08k
    auto vAlign=layout.uint32(2);
1879
2.08k
    if (vAlign)
1880
927
    {
1881
927
      IWORKVerticalAlignment const aligns[]=
1882
927
      {IWORK_VERTICAL_ALIGNMENT_TOP, IWORK_VERTICAL_ALIGNMENT_MIDDLE,IWORK_VERTICAL_ALIGNMENT_BOTTOM};
1883
927
      if (get(vAlign) < ETONYEK_NUM_ELEMENTS(aligns))
1884
925
      {
1885
925
        props.put<VerticalAlignment>(aligns[get(vAlign)]);
1886
925
      }
1887
2
      else
1888
2
      {
1889
2
        ETONYEK_DEBUG_MSG(("IWAParser::parseGraphicStyle: unknown vAlign %u\n", get(vAlign)));
1890
2
      }
1891
927
    }
1892
2.08k
    if (get(layout).message(6))
1893
680
    {
1894
680
      IWORKPadding padding;
1895
680
      readPadding(get(get(layout).message(6)), padding);
1896
680
      props.put<LayoutMargins>(padding);
1897
680
    }
1898
2.08k
    const optional<unsigned> &paraRef = readRef(get(layout), 10);
1899
2.08k
    if (paraRef)
1900
689
    {
1901
689
      const IWORKStylePtr_t &paraStyle = queryParagraphStyle(get(paraRef));
1902
689
      if (paraStyle)
1903
673
        props.put<LayoutParagraphStyle>(paraStyle);
1904
689
    }
1905
1906
    // TODO: other layout props: 1: shrink text, 4: columns
1907
2.08k
  }
1908
1909
2.51k
  style = std::make_shared<IWORKStyle>(props, name, parent);
1910
2.51k
}
1911
1912
void IWAParser::parseMediaStyle(const unsigned id, IWORKStylePtr_t &style)
1913
184
{
1914
184
  const ObjectMessage msg(*this, id, IWAObjectType::MediaStyle);
1915
184
  if (!msg)
1916
94
    return;
1917
90
  optional<string> name;
1918
90
  IWORKStylePtr_t parent;
1919
90
  IWORKPropertyMap props;
1920
1921
90
  using namespace property;
1922
90
  const IWAMessageField &styleInfo = get(msg).message(1);
1923
90
  if (styleInfo)
1924
89
  {
1925
89
    name = styleInfo.string(2).optional();
1926
89
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1927
89
    if (parentRef)
1928
0
      parent = queryMediaStyle(get(parentRef));
1929
89
  }
1930
90
  const IWAMessageField &styleProps = get(msg).message(11);
1931
90
  if (styleProps)
1932
80
  {
1933
80
    if (styleProps.message(1))
1934
78
    {
1935
78
      IWORKStroke stroke;
1936
78
      readStroke(get(styleProps.message(1)), stroke);
1937
78
      props.put<Stroke>(stroke);
1938
78
    }
1939
80
    if (styleProps.float_(2))
1940
77
      props.put<Opacity>(get(styleProps.float_(2)));
1941
80
    if (styleProps.message(3))
1942
76
    {
1943
76
      IWORKShadow shadow;
1944
76
      readShadow(get(styleProps.message(3)),shadow);
1945
76
      props.put<Shadow>(shadow);
1946
76
    }
1947
    // 4: reflection
1948
80
  }
1949
90
  style = std::make_shared<IWORKStyle>(props, name, parent);
1950
90
}
1951
1952
void IWAParser::parseCellStyle(const unsigned id, IWORKStylePtr_t &style)
1953
478
{
1954
478
  const ObjectMessage msg(*this, id, IWAObjectType::CellStyle);
1955
478
  if (!msg)
1956
215
    return;
1957
1958
263
  optional<string> name;
1959
263
  IWORKStylePtr_t parent;
1960
263
  IWORKPropertyMap props;
1961
1962
263
  using namespace property;
1963
1964
263
  const IWAMessageField &styleInfo = get(msg).message(1);
1965
263
  if (styleInfo)
1966
262
  {
1967
262
    name = styleInfo.string(2).optional();
1968
262
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
1969
262
    if (parentRef)
1970
105
      parent = queryCellStyle(get(parentRef));
1971
262
  }
1972
1973
263
  if (get(msg).message(11))
1974
253
  {
1975
253
    const IWAMessage &properties = get(get(msg).message(11));
1976
253
    if (properties.message(1))
1977
249
    {
1978
249
      IWORKFill fill;
1979
249
      if (readFill(get(properties.message(1)), fill))
1980
209
        props.put<Fill>(fill);
1981
40
      else
1982
40
        props.clear<Fill>();
1983
249
    }
1984
253
    if (properties.uint32(8))
1985
220
    {
1986
220
      auto align=get(properties.uint32(8));
1987
220
      if (align<=2)
1988
220
      {
1989
220
        const IWORKVerticalAlignment aligns[] =
1990
220
        {
1991
220
          IWORK_VERTICAL_ALIGNMENT_TOP, IWORK_VERTICAL_ALIGNMENT_MIDDLE, IWORK_VERTICAL_ALIGNMENT_BOTTOM
1992
220
        };
1993
220
        props.put<VerticalAlignment>(aligns[align]);
1994
220
      }
1995
0
      else
1996
0
      {
1997
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseCellStyle: unknown align=%u\n", align));
1998
0
      }
1999
220
    }
2000
253
    if (properties.message(9))
2001
142
    {
2002
142
      IWORKPadding padding;
2003
142
      readPadding(get(properties.message(9)), padding);
2004
142
      props.put<Padding>(padding);
2005
142
    }
2006
253
    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
253
    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
253
    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
253
    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
253
  }
2031
2032
263
  style = std::make_shared<IWORKStyle>(props, name, parent);
2033
263
}
2034
2035
void IWAParser::parseTableStyle(const unsigned id, IWORKStylePtr_t &style)
2036
138
{
2037
138
  const ObjectMessage msg(*this, id, IWAObjectType::TableStyle);
2038
138
  if (!msg)
2039
62
    return;
2040
2041
76
  optional<string> name;
2042
76
  IWORKStylePtr_t parent;
2043
76
  IWORKPropertyMap props;
2044
2045
76
  using namespace property;
2046
2047
76
  const IWAMessageField &styleInfo = get(msg).message(1);
2048
76
  if (styleInfo)
2049
76
  {
2050
76
    name = styleInfo.string(2).optional();
2051
76
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
2052
76
    if (parentRef)
2053
39
      parent = queryTableStyle(get(parentRef));
2054
76
  }
2055
2056
76
  if (get(msg).message(11))
2057
75
  {
2058
75
    const IWAMessage &properties = get(get(msg).message(11));
2059
2060
75
    if (properties.bool_(1))
2061
37
      props.put<SFTTableBandedRowsProperty>(get(properties.bool_(1)));
2062
75
    if (properties.message(2))
2063
63
    {
2064
63
      IWORKFill fill;
2065
63
      if (readFill(get(properties.message(2)), fill))
2066
59
        props.put<SFTTableBandedCellFillProperty>(fill);
2067
4
      else
2068
4
        props.clear<SFTTableBandedCellFillProperty>();
2069
63
    }
2070
75
    if (properties.bool_(22))
2071
59
      props.put<SFTAutoResizeProperty>(get(properties.bool_(22)));
2072
75
    if (properties.string(41))
2073
31
      props.put<FontName>(get(properties.string(41)));
2074
75
  }
2075
2076
76
  style = std::make_shared<IWORKStyle>(props, name, parent);
2077
76
}
2078
2079
void IWAParser::parseListStyle(const unsigned id, IWORKStylePtr_t &style)
2080
1.43k
{
2081
1.43k
  const ObjectMessage msg(*this, id, IWAObjectType::ListStyle);
2082
1.43k
  if (!msg)
2083
747
    return;
2084
2085
688
  optional<string> name;
2086
688
  IWORKStylePtr_t parent;
2087
2088
688
  using namespace property;
2089
2090
688
  const IWAMessageField &styleInfo = get(msg).message(1);
2091
688
  if (styleInfo)
2092
683
  {
2093
683
    name = styleInfo.string(2).optional();
2094
683
    const optional<unsigned> &parentRef = readRef(get(styleInfo), 3);
2095
683
    if (parentRef)
2096
197
      parent = queryListStyle(get(parentRef));
2097
683
  }
2098
2099
688
  unsigned level = 0;
2100
2101
688
  map<unsigned, IWORKPropertyMap> levelProps;
2102
2103
688
  const IWAUInt32Field &numberFormats = get(msg).uint32(15);
2104
688
  const IWAStringField &bullets = get(msg).string(16);
2105
688
  const IWAMessageField &images = get(msg).message(17);
2106
688
  const IWABoolField &tiered = get(msg).bool_(25);
2107
8.65k
  for (IWAUInt32Field::const_iterator it = get(msg).uint32(11).begin(); it != get(msg).uint32(11).end(); ++it, ++level)
2108
7.96k
  {
2109
7.96k
    switch (*it)
2110
7.96k
    {
2111
2.46k
    default :
2112
2.46k
      ETONYEK_DEBUG_MSG(("parseListStyle: unknown label type %u\n", *it));
2113
2.46k
      ETONYEK_FALLTHROUGH;
2114
5.98k
    case 0 :
2115
      // no label
2116
5.98k
      levelProps[level].put<ListLabelTypeInfo>(true);
2117
5.98k
      break;
2118
463
    case 1 :
2119
463
    {
2120
      // try to find the image, and revert to a default bullet if we find nothing
2121
463
      char const defBullet[]= {char(0xe2), char(0x80), char(0xa2),0};
2122
463
      if (level >= images.size())
2123
393
      {
2124
        // FIXME, in fact, the image is in the parent style...
2125
393
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2126
393
        break;
2127
393
      }
2128
70
      auto ref=readRef(images[level],3);
2129
70
      if (!ref)
2130
9
      {
2131
9
        ETONYEK_DEBUG_MSG(("parseListStyle: can not find image ref for level %u\n", level));
2132
9
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2133
9
        break;
2134
9
      }
2135
61
      const IWORKMediaContentPtr_t content = make_shared<IWORKMediaContent>();
2136
61
      auto stream = queryFile(get(ref));
2137
61
      if (!stream)
2138
61
      {
2139
        // the image is probably in the theme model
2140
61
        levelProps[level].put<ListLabelTypeInfo>(std::string(defBullet));
2141
61
        break;
2142
61
      }
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
61
    }
2149
1.17k
    case 2 :
2150
1.17k
      if (level < bullets.size())
2151
714
        levelProps[level].put<ListLabelTypeInfo>(bullets[level]);
2152
1.17k
      break;
2153
340
    case 3 :
2154
340
    {
2155
340
      IWORKTextLabel label;
2156
340
      if (level < numberFormats.size())
2157
182
      {
2158
182
        switch (numberFormats[level] / 3)
2159
182
        {
2160
55
        case 0 :
2161
55
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_NUMERIC;
2162
55
          break;
2163
0
        case 1 :
2164
0
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ROMAN;
2165
0
          break;
2166
25
        case 2 :
2167
25
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ROMAN_LOWERCASE;
2168
25
          break;
2169
102
        case 3 :
2170
102
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ALPHA;
2171
102
          break;
2172
0
        case 4 :
2173
0
          label.m_format.m_format = IWORK_LABEL_NUM_FORMAT_ALPHA_LOWERCASE;
2174
0
          break;
2175
0
        default :
2176
0
          ETONYEK_DEBUG_MSG(("parseListStyle: unknown label number format %u\n", numberFormats[level]));
2177
0
          break;
2178
182
        }
2179
182
        switch (numberFormats[level] % 3)
2180
182
        {
2181
140
        case 0 :
2182
140
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_DOT;
2183
140
          break;
2184
42
        case 1 :
2185
42
          label.m_format.m_prefix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2186
42
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2187
42
          break;
2188
0
        case 2 :
2189
0
          label.m_format.m_suffix = IWORK_LABEL_NUM_FORMAT_SURROUNDING_PARENTHESIS;
2190
0
          break;
2191
0
        default:
2192
0
          break;
2193
182
        }
2194
182
      }
2195
340
      if (level < tiered.size())
2196
136
        label.m_tiered = tiered[level];
2197
340
      levelProps[level].put<ListLabelTypeInfo>(label);
2198
340
      break;
2199
340
    }
2200
7.96k
    }
2201
7.96k
  }
2202
2203
688
  level = 0;
2204
4.51k
  for (IWAFloatField::const_iterator it = get(msg).float_(12).begin(); it != get(msg).float_(12).end(); ++it, ++level)
2205
3.83k
    levelProps[level].put<ListTextIndent>(*it);
2206
2207
688
  level = 0;
2208
4.73k
  for (IWAFloatField::const_iterator it = get(msg).float_(13).begin(); it != get(msg).float_(13).end(); ++it, ++level)
2209
4.04k
    levelProps[level].put<ListLabelIndent>(*it);
2210
2211
688
  level = 0;
2212
4.83k
  for (IWAMessageField::const_iterator it = get(msg).message(14).begin(); it != get(msg).message(14).end(); ++it, ++level)
2213
4.14k
  {
2214
4.14k
    IWORKListLabelGeometry geometry;
2215
4.14k
    if (it->float_(1))
2216
3.97k
      geometry.m_scale = get(it->float_(1));
2217
4.14k
    levelProps[level].put<ListLabelGeometry>(geometry);
2218
4.14k
  }
2219
2220
688
  if (bool(parent) && parent->has<ListLevelStyles>())
2221
192
  {
2222
192
    const IWORKListLevels_t &parentStyle = parent->get<ListLevelStyles>();
2223
192
    for (const auto &it : parentStyle)
2224
1.72k
      levelProps[it.first].setParent(&it.second->getPropertyMap());
2225
192
  }
2226
2227
688
  IWORKListLevels_t listStyle;
2228
9.69k
  for (map<unsigned, IWORKPropertyMap>::const_iterator it = levelProps.begin(); it != levelProps.end(); ++it)
2229
9.00k
    listStyle[it->first] = std::make_shared<IWORKStyle>(it->second, none, none);
2230
2231
688
  IWORKPropertyMap props;
2232
688
  props.put<ListLevelStyles>(listStyle);
2233
688
  style = std::make_shared<IWORKStyle>(props, name, none);
2234
688
}
2235
2236
void IWAParser::parseCharacterProperties(const IWAMessage &msg, IWORKPropertyMap &props)
2237
1.70k
{
2238
1.70k
  using namespace property;
2239
2240
1.70k
  if (msg.bool_(1))
2241
1.39k
    props.put<Bold>(get(msg.bool_(1)));
2242
1.70k
  if (msg.bool_(2))
2243
1.38k
    props.put<Italic>(get(msg.bool_(2)));
2244
1.70k
  if (msg.float_(3))
2245
1.39k
    props.put<FontSize>(get(msg.float_(3)));
2246
1.70k
  if (msg.string(5))
2247
1.33k
    props.put<FontName>(get(msg.string(5)));
2248
1.70k
  const optional<IWORKColor> &fontColor = readColor(msg, 7);
2249
1.70k
  if (fontColor)
2250
1.17k
    props.put<FontColor>(get(fontColor));
2251
1.70k
  if (msg.uint32(10))
2252
1.27k
    putEnum<Baseline>(props, get(msg.uint32(10)));
2253
1.70k
  if (msg.bool_(11))
2254
1.30k
    props.put<Underline>(get(msg.bool_(11)));
2255
1.70k
  if (msg.bool_(12))
2256
1.27k
    props.put<Strikethru>(get(msg.bool_(12)));
2257
1.70k
  if (msg.uint32(13))
2258
1.27k
    putEnum<Capitalization>(props, get(msg.uint32(13)));
2259
1.70k
  if (msg.float_(14))
2260
1.26k
    props.put<BaselineShift>(get(msg.float_(14)));
2261
1.70k
  if (msg.float_(19)) // CHECKME
2262
1.25k
    props.put<Outline>(get(msg.float_(19))>0);
2263
1.70k
  if (msg.message(21))
2264
158
  {
2265
158
    IWORKShadow shadow;
2266
158
    readShadow(get(msg.message(21)),shadow);
2267
158
    props.put<TextShadow>(shadow);
2268
158
  }
2269
1.70k
  const auto bgColor = readColor(msg, 26);
2270
1.70k
  if (bgColor)
2271
0
    props.put<TextBackground>(get(bgColor));
2272
1.70k
  if (msg.float_(27))
2273
1.25k
    props.put<Tracking>(get(msg.float_(27)));
2274
1.70k
}
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
525
{
2396
525
  m_collector.startLevel();
2397
525
  IWORKGeometryPtr_t geometry;
2398
525
  if (msg.message(1))
2399
387
  {
2400
387
    boost::optional<unsigned> flags;
2401
387
    parseShapePlacement(get(msg.message(1)), geometry, flags);
2402
387
    m_collector.collectGeometry(geometry);
2403
387
  }
2404
2405
525
  const optional<unsigned> styleRef = readRef(msg, 3);
2406
525
  if (styleRef)
2407
467
    m_collector.setGraphicStyle(queryMediaStyle(get(styleRef)));
2408
2409
525
  IWORKGeometryPtr_t cropGeometry;
2410
525
  const optional<unsigned> cropRef = readRef(msg, 5);
2411
525
  if (cropRef)
2412
230
  {
2413
230
    IWORKPathPtr_t path;
2414
230
    parseMask(get(cropRef), cropGeometry, path);
2415
230
  }
2416
525
  if (cropGeometry && geometry)
2417
120
  {
2418
    // CHANGEME: collector suppose that cropGeometry position is the
2419
    //   final position but mask is relative to the original picture
2420
120
    cropGeometry->m_position.m_x += geometry->m_position.m_x;
2421
120
    cropGeometry->m_position.m_y += geometry->m_position.m_y;
2422
120
  }
2423
2424
525
  const IWORKMediaContentPtr_t content = make_shared<IWORKMediaContent>();
2425
  // 15: filtered, 11: basic image?, 12: small image, 13: ?
2426
525
  unsigned const ids[]= {15, 13, 11, 12};
2427
525
  for (auto id : ids)
2428
1.93k
  {
2429
1.93k
    auto const &ref=readRef(msg, id);
2430
1.93k
    if (!ref) continue;
2431
925
    auto stream = queryFile(get(ref));
2432
925
    if (!stream) continue;
2433
131
    const IWORKDataPtr_t data = make_shared<IWORKData>();
2434
131
    data->m_stream = stream;
2435
131
    content->m_data = data;
2436
131
    break;
2437
925
  }
2438
525
  content->m_size = readSize(msg, 9);
2439
525
  if (!content->m_size)
2440
39
    content->m_size = readSize(msg, 4);
2441
2442
525
  m_collector.collectMedia(content, cropGeometry);
2443
525
  m_collector.endLevel();
2444
2445
525
  return true;
2446
525
}
2447
2448
void IWAParser::parseAuthorInComment(unsigned id)
2449
18
{
2450
18
  assert(bool(m_currentText));
2451
18
  const ObjectMessage msg(*this, id, IWAObjectType::AuthorStorage);
2452
18
  if (!msg)
2453
9
    return;
2454
9
  if (get(msg).string(1))
2455
9
  {
2456
9
    auto str=get(get(msg).string(1));
2457
9
    auto len=str.size();
2458
9
    if (len==0) return;
2459
9
    IWAText text(str+"\t", m_langManager);
2460
9
    std::map<unsigned, IWORKStylePtr_t> spans;
2461
9
    IWORKPropertyMap props;
2462
    // normally yellow, but blue may be better in LO
2463
9
    props.put<property::FontColor>(IWORKColor(0,0,1,1));
2464
9
    spans[0]=std::make_shared<IWORKStyle>(props, boost::none, IWORKStylePtr_t());
2465
    // reset color to default, if not, comment will be blue colored
2466
9
    props.put<property::FontColor>(IWORKColor(0,0,0,1));
2467
9
    spans[unsigned(len)]=std::make_shared<IWORKStyle>(props, boost::none, IWORKStylePtr_t());
2468
9
    text.setSpans(spans);
2469
9
    text.parse(*m_currentText);
2470
9
  }
2471
9
}
2472
2473
void IWAParser::parseComment(const unsigned id)
2474
58
{
2475
58
  assert(bool(m_currentText));
2476
2477
58
  unsigned actId=id;
2478
58
  std::set<unsigned> seens;
2479
93
  while (1)
2480
90
  {
2481
90
    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
87
    seens.insert(actId);
2487
87
    const ObjectMessage msg(*this, actId, IWAObjectType::Comment);
2488
87
    if (!msg)
2489
24
      return;
2490
63
    auto authorRef=readRef(get(msg), 3);
2491
63
    if (authorRef)
2492
18
      parseAuthorInComment(*authorRef);
2493
    // TODO add date which is in position 2
2494
63
    if (get(msg).string(1))
2495
41
    {
2496
41
      IWAText text(get(get(msg).string(1)), m_langManager);
2497
41
      text.parse(*m_currentText);
2498
41
    }
2499
2500
63
    auto nextRef=readRef(get(msg), 4);
2501
63
    if (!nextRef) break;
2502
35
    actId=*nextRef;
2503
35
  }
2504
58
}
2505
2506
bool IWAParser::parseTabularInfo(const IWAMessage &msg)
2507
106
{
2508
106
  m_collector.startLevel();
2509
106
  if (msg.message(1))
2510
105
    parseShapePlacement(get(msg.message(1)));
2511
106
  const optional<unsigned> &modelRef = readRef(msg, 2);
2512
106
  if (modelRef)
2513
104
    parseTabularModel(get(modelRef));
2514
106
  m_collector.endLevel();
2515
106
  return bool(modelRef);
2516
106
}
2517
2518
void IWAParser::parseTabularModel(const unsigned id)
2519
104
{
2520
104
  const ObjectMessage msg(*this, id, IWAObjectType::TabularModel);
2521
104
  if (!msg)
2522
3
    return;
2523
101
  const IWAUInt32Field &rows = get(msg).uint32(6);
2524
101
  const IWAUInt32Field &columns = get(msg).uint32(7);
2525
101
  if (!rows || !columns)
2526
1
    return;
2527
2528
100
  m_currentTable = std::make_shared<TableInfo>(m_collector.createTable(m_tableNameMap, m_formatNameMap, m_langManager), get(columns), get(rows));
2529
100
  m_currentTable->m_table->setSize(get(columns), get(rows));
2530
2531
100
  IWORKStylePtr_t tableStyle;
2532
100
  const optional<unsigned> tableStyleRef = readRef(get(msg), 3);
2533
100
  if (tableStyleRef)
2534
99
    tableStyle = queryTableStyle(get(tableStyleRef));
2535
100
  if (bool(tableStyle))
2536
39
  {
2537
39
    m_currentTable->m_style = tableStyle;
2538
39
    m_currentTable->m_table->setStyle(tableStyle);
2539
2540
39
    if (tableStyle->has<property::SFTTableBandedCellFillProperty>())
2541
35
    {
2542
35
      IWORKPropertyMap props;
2543
35
      props.put<property::Fill>(tableStyle->get<property::SFTTableBandedCellFillProperty>());
2544
35
      m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ALTERNATE_BODY, make_shared<IWORKStyle>(props, none, none));
2545
35
    }
2546
39
  }
2547
2548
100
  std::map<unsigned,unsigned> idToTileRefMap;
2549
100
  std::map<unsigned,unsigned> idToTileDecalRowMap;
2550
2551
100
  if (get(msg).message(4))
2552
99
  {
2553
99
    const IWAMessage &grid = get(get(msg).message(4));
2554
2555
99
    if (grid.message(1))
2556
99
    {
2557
99
      const optional<unsigned> &rowHeadersRef = readRef(get(grid.message(1)), 2);
2558
99
      if (rowHeadersRef)
2559
99
        parseTableHeaders(get(rowHeadersRef), m_currentTable->m_rowHeader);
2560
99
    }
2561
99
    const optional<unsigned> &columnHeadersRef = readRef(grid, 2);
2562
99
    if (columnHeadersRef)
2563
95
      parseTableHeaders(get(columnHeadersRef), m_currentTable->m_columnHeader);
2564
2565
99
    const optional<unsigned> &simpleTextListRef = readRef(grid, 4);
2566
99
    if (simpleTextListRef)
2567
93
      parseDataList(get(simpleTextListRef), m_currentTable->m_simpleTextList);
2568
99
    const optional<unsigned> &cellStyleListRef = readRef(grid, 5);
2569
99
    if (cellStyleListRef)
2570
88
      parseDataList(get(cellStyleListRef), m_currentTable->m_cellStyleList);
2571
99
    const optional<unsigned> &formulaListRef = readRef(grid, 6);
2572
99
    if (formulaListRef)
2573
86
      parseDataList(get(formulaListRef), m_currentTable->m_formulaList);
2574
99
    const optional<unsigned> &formatListRef = readRef(grid, 11);
2575
99
    if (formatListRef)
2576
89
      parseDataList(get(formatListRef), m_currentTable->m_formatList);
2577
99
    const optional<unsigned> &paraTextListRef = readRef(grid, 17);
2578
99
    if (paraTextListRef)
2579
86
      parseDataList(get(paraTextListRef), m_currentTable->m_formattedTextList);
2580
99
    const optional<unsigned> &conditionStyleListRef = readRef(grid, 18);
2581
99
    if (conditionStyleListRef)
2582
85
    {
2583
85
      DataList_t conditionStyles;
2584
85
      parseDataList(get(conditionStyleListRef), conditionStyles);
2585
85
      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
85
    }
2595
99
    const optional<unsigned> &commentListRef = readRef(grid, 19);
2596
99
    if (commentListRef)
2597
85
      parseDataList(get(commentListRef), m_currentTable->m_commentList);
2598
99
    const optional<unsigned> &newFormatListRef = readRef(grid, 22);
2599
99
    if (newFormatListRef)
2600
61
      parseDataList(get(newFormatListRef), m_currentTable->m_newFormatList);
2601
2602
99
    m_currentTable->m_table->setSizes(makeSizes(m_currentTable->m_columnHeader.m_sizes), makeSizes(m_currentTable->m_rowHeader.m_sizes));
2603
2604
99
    if (grid.message(3))
2605
87
    {
2606
87
      for (auto const &it : grid.message(3).message(1))
2607
87
      {
2608
87
        auto tileRef = readRef(it, 2);
2609
87
        if (!it.uint32(1) || !bool(tileRef))
2610
5
        {
2611
5
          ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some tile ref\n"));
2612
5
          continue;
2613
5
        }
2614
82
        idToTileRefMap[get(it.uint32(1))]=get(tileRef);
2615
82
      }
2616
87
    }
2617
99
    if (grid.message(9))
2618
86
    {
2619
86
      for (auto const &it : grid.message(9).message(1))
2620
85
      {
2621
85
        if (!it.uint32(1) || !it.uint32(2))
2622
5
        {
2623
5
          ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some grid decal data\n"));
2624
5
          continue;
2625
5
        }
2626
80
        idToTileDecalRowMap[get(it.uint32(2))]=get(it.uint32(1));
2627
80
      }
2628
86
    }
2629
99
  }
2630
100
  if (get(msg).string(8))
2631
88
  {
2632
88
    std::string finalName;
2633
88
    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
88
    else
2640
88
      finalName=get(get(msg).string(8));
2641
88
    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
88
    (*m_tableNameMap)[finalName]=finalName;
2656
88
    if (get(msg).string(1))
2657
88
      (*m_tableNameMap)[std::string("SFTGlobalID_")+get(get(msg).string(1))] = finalName;
2658
88
    m_currentTable->m_table->setName(finalName);
2659
88
  }
2660
100
  m_currentTable->m_table->setHeaders(
2661
100
    get_optional_value_or(get(msg).uint32(10).optional(), 0),
2662
100
    get_optional_value_or(get(msg).uint32(9).optional(), 0),
2663
100
    get_optional_value_or(get(msg).uint32(11).optional(), 0)
2664
100
  );
2665
100
  m_currentTable->m_table->setRepeated(
2666
100
    get_optional_value_or(get(msg).bool_(13).optional(), false),
2667
100
    get_optional_value_or(get(msg).bool_(12).optional(), false)
2668
100
  );
2669
100
  if (bool(tableStyle) && tableStyle->has<property::SFTTableBandedRowsProperty>())
2670
33
    m_currentTable->m_table->setBandedRows(tableStyle->get<property::SFTTableBandedRowsProperty>());
2671
2672
  // default cell styles
2673
100
  optional<unsigned> styleRef = readRef(get(msg), 18);
2674
100
  if (styleRef)
2675
86
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_BODY, queryCellStyle(get(styleRef)));
2676
100
  styleRef = readRef(get(msg), 19);
2677
100
  if (styleRef)
2678
86
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ROW_HEADER, queryCellStyle(get(styleRef)));
2679
100
  styleRef = readRef(get(msg), 20);
2680
100
  if (styleRef)
2681
83
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_COLUMN_HEADER, queryCellStyle(get(styleRef)));
2682
100
  styleRef = readRef(get(msg), 21);
2683
100
  if (styleRef)
2684
81
    m_currentTable->m_table->setDefaultCellStyle(IWORKTable::CELL_TYPE_ROW_FOOTER, queryCellStyle(get(styleRef)));
2685
2686
  // default para styles
2687
100
  styleRef = readRef(get(msg), 24);
2688
100
  if (styleRef)
2689
82
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_BODY, queryParagraphStyle(get(styleRef)));
2690
100
  styleRef = readRef(get(msg), 25);
2691
100
  if (styleRef)
2692
80
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_ROW_HEADER, queryParagraphStyle(get(styleRef)));
2693
100
  styleRef = readRef(get(msg), 26);
2694
100
  if (styleRef)
2695
79
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_COLUMN_HEADER, queryParagraphStyle(get(styleRef)));
2696
100
  styleRef = readRef(get(msg), 27);
2697
100
  if (styleRef)
2698
79
    m_currentTable->m_table->setDefaultParagraphStyle(IWORKTable::CELL_TYPE_ROW_FOOTER, queryParagraphStyle(get(styleRef)));
2699
2700
100
  styleRef = readRef(get(msg), 49);
2701
100
  if (styleRef)
2702
79
  {
2703
79
    IWORKGridLineMap_t gridLines[4];
2704
79
    parseTableGridLines(get(styleRef), gridLines);
2705
79
    m_currentTable->m_table->setBorders(gridLines[0],gridLines[1],gridLines[2],gridLines[3]);
2706
79
  }
2707
2708
  // handle tables
2709
100
  for (auto const &tileIt : idToTileRefMap)
2710
77
  {
2711
77
    auto const &decalIt=idToTileDecalRowMap.find(tileIt.first);
2712
77
    if (decalIt==idToTileDecalRowMap.end())
2713
6
    {
2714
6
      ETONYEK_DEBUG_MSG(("IWAParser::parseTabularModel: oops, can not find some decal for id=%x, assume 0\n", tileIt.first));
2715
6
      parseTile(tileIt.second, 0);
2716
6
    }
2717
71
    else
2718
71
      parseTile(tileIt.second, decalIt->second);
2719
77
  }
2720
100
  m_collector.collectTable(m_currentTable->m_table);
2721
100
  m_currentTable.reset();
2722
100
}
2723
2724
void IWAParser::parseDataList(const unsigned id, DataList_t &dataList)
2725
673
{
2726
673
  const ObjectMessage msg(*this, id, IWAObjectType::DataList);
2727
673
  if (!msg)
2728
125
    return;
2729
2730
  // TODO: it would likely to be more robust to parse everything.
2731
548
  if (!get(msg).uint32(1))
2732
2
    return;
2733
2734
546
  const unsigned type = get(get(msg).uint32(1));
2735
546
  for (const auto &it : get(msg).message(3))
2736
278
  {
2737
278
    if (!it.uint32(1))
2738
3
      continue;
2739
275
    const unsigned index = get(it.uint32(1));
2740
275
    switch (type)
2741
275
    {
2742
90
    case 1 :
2743
90
      if (it.string(3))
2744
89
        dataList[index] = get(it.string(3));
2745
90
      break;
2746
96
    case 2 :
2747
      // it.uint32(2): some type
2748
96
      if (it.message(6))
2749
92
      {
2750
92
        Format format;
2751
92
        if (parseFormat(get(it.message(6)), format))
2752
92
          dataList[index]=format;
2753
92
      }
2754
4
      else
2755
4
      {
2756
4
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the format\n"));
2757
4
      }
2758
96
      break;
2759
0
    case 3 :
2760
0
    case 5 : // invalid formula
2761
0
      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
0
      else
2768
0
      {
2769
0
        ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: can not find the formula\n"));
2770
0
      }
2771
0
      break;
2772
89
    case 4 :
2773
89
    {
2774
89
      auto styleRef=readRef(it,4);
2775
89
      if (styleRef)
2776
85
        dataList[index]=get(styleRef);
2777
4
      else if (it.uint32(4))
2778
0
        dataList[index] = get(it.uint32(4));
2779
89
      break;
2780
0
    }
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
0
    }
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
0
    }
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
0
    }
2810
0
    default :
2811
0
      ETONYEK_DEBUG_MSG(("IWAParser::parseDataList: unknown data list type %u\n", type));
2812
0
      break;
2813
275
    }
2814
275
  }
2815
546
}
2816
2817
void IWAParser::parseTileDefinition(unsigned row, unsigned column, RVNGInputStreamPtr_t &input, unsigned endPos, bool oldFormat)
2818
144
{
2819
144
  IWORKCellType cellType = IWORK_CELL_TYPE_TEXT;
2820
144
  optional<unsigned> cellStyleId, formatId, paragraphStyleId;
2821
144
  optional<unsigned> commentId, conditionId, formulaId, textId, textFormattedId;
2822
144
  optional<string> text;
2823
144
  bool numberSet=false;
2824
2825
144
  auto begPos=input->tell();
2826
144
  if (begPos+(oldFormat ? 10 : 12)>long(endPos))
2827
6
  {
2828
6
    ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: the zone seems too short\n"));
2829
6
    return;
2830
6
  }
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
138
  try
2835
138
  {
2836
    // 0: 4?
2837
138
    input->seek((long) begPos+1, librevenge::RVNG_SEEK_SET);
2838
138
    auto type=readU8(input);
2839
138
    switch (type)
2840
138
    {
2841
0
    case 2:
2842
0
    case 8: // nan
2843
0
    case 10: // devise
2844
0
      cellType=IWORK_CELL_TYPE_NUMBER;
2845
0
      break;
2846
0
    case 7: // duration
2847
0
      cellType=IWORK_CELL_TYPE_DURATION;
2848
0
      break;
2849
65
    case 0: // empty (ok)
2850
135
    case 3: // text (ok)
2851
135
    case 9: // text zone
2852
135
      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
3
    default:
2860
3
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: unknown type %d\n", int(type)));
2861
3
      break;
2862
138
    }
2863
138
    if (oldFormat)
2864
64
    {
2865
      // 2,3: ?
2866
64
      input->seek((long) begPos + 4, librevenge::RVNG_SEEK_SET);
2867
64
      const unsigned flags = readU16(input);
2868
64
      input->seek(6, librevenge::RVNG_SEEK_CUR);
2869
64
      if (flags & 0x2) // cell style
2870
53
        cellStyleId = readU32(input);
2871
64
      if (flags & 0x80)
2872
0
        paragraphStyleId=readU32(input);
2873
64
      if (flags & 0x800) // condition
2874
0
        conditionId=readU32(input);
2875
64
      if (flags & 0x400) // condition 2
2876
0
        readU32(input);
2877
64
      if (flags & 0x4)   // format
2878
8
        formatId=readU32(input);
2879
64
      if (flags & 0x8) // formula
2880
0
        formulaId = readU32(input);
2881
64
      if (flags & 0x1000) // comment
2882
0
        commentId=readU32(input);
2883
64
      if (flags & 0x10) // simple text
2884
8
        textId = readU32(input);
2885
64
      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
64
      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
64
      if (flags & 0x200) // formatted text
2900
0
        textFormattedId = readU32(input);
2901
64
    }
2902
74
    else
2903
74
    {
2904
      // 2-7?
2905
74
      input->seek((long) begPos + 8, librevenge::RVNG_SEEK_SET);
2906
74
      const unsigned flags = readU32(input);
2907
74
      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
74
      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
74
      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
74
      if (flags & 8)
2948
62
        textId = readU32(input);
2949
74
      if (flags & 0x10)
2950
2
        textFormattedId=readU32(input);
2951
74
      if (flags & 0x20) // cell style
2952
10
        cellStyleId = readU32(input);
2953
74
      if (flags & 0x40) // cell paragraph style
2954
0
        paragraphStyleId=readU32(input);
2955
74
      if (flags & 0x80) // conditional
2956
0
        conditionId=readU32(input);
2957
74
      if (flags & 0x100) // conditional(unknown)
2958
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2959
74
      if (flags & 0x200)
2960
2
        formulaId = readU32(input);
2961
74
      if (flags & 0x400) // button menu
2962
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2963
74
      if (flags & 0x800) // unknown: check size
2964
0
        input->seek(4, librevenge::RVNG_SEEK_CUR);
2965
74
      unsigned resType=0;
2966
74
      if (flags & 0x1000)   // type of the result
2967
62
      {
2968
62
        resType=readU32(input);
2969
62
        switch (resType)
2970
62
        {
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
62
        case 5:
2984
62
          cellType=IWORK_CELL_TYPE_TEXT;
2985
62
          break;
2986
0
        case 6: // other
2987
0
          break;
2988
0
        default:
2989
0
          ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: unknown type %d\n", int(resType)));
2990
0
          break;
2991
62
        }
2992
62
      }
2993
518
      for (unsigned w=0, hBytes=0x2000; w<6; ++w, hBytes<<=1)
2994
444
      {
2995
444
        if ((flags & hBytes)==0)
2996
382
          continue;
2997
62
        const unsigned id=readU32(input);
2998
        // checkme, unclear which format id we need to choose when resType=2 or 6
2999
62
        if (w+1!=resType)
3000
0
          continue;
3001
62
        formatId=id;
3002
62
      }
3003
74
      if (flags & 0x80000)
3004
0
        commentId=readU32(input);
3005
74
    }
3006
138
  }
3007
138
  catch (...)
3008
138
  {
3009
    // ignore failure to read the last record
3010
0
  }
3011
3012
138
  IWORKFormulaPtr_t formula;
3013
138
  if (bool(formulaId))
3014
2
  {
3015
2
    auto const formulaIt = m_currentTable->m_formulaList.find(get(formulaId));
3016
2
    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
2
    else
3022
2
    {
3023
2
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: can not find formula %d\n", int(get(formulaId))));
3024
2
    }
3025
2
  }
3026
138
  if (numberSet && cellType == IWORK_CELL_TYPE_TEXT)
3027
0
    cellType = IWORK_CELL_TYPE_NUMBER;
3028
138
  bool textSet=false;
3029
138
  if (bool(textId))
3030
70
  {
3031
70
    const DataList_t::const_iterator listIt = m_currentTable->m_simpleTextList.find(get(textId));
3032
70
    if (listIt != m_currentTable->m_simpleTextList.end())
3033
55
    {
3034
55
      if (const string *const s = boost::get<string>(&listIt->second))
3035
55
      {
3036
55
        cellType = IWORK_CELL_TYPE_TEXT;
3037
55
        text = *s;
3038
55
        textSet=true;
3039
55
      }
3040
55
    }
3041
15
    else
3042
15
    {
3043
15
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition[new]: can not find text %d\n", int(get(textId))));
3044
15
    }
3045
70
  }
3046
138
  optional<unsigned> textRef;
3047
138
  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
138
  IWORKStylePtr_t cellStyle;
3066
138
  if (bool(cellStyleId))
3067
63
  {
3068
63
    const DataList_t::const_iterator listIt = m_currentTable->m_cellStyleList.find(get(cellStyleId));
3069
63
    if (listIt != m_currentTable->m_cellStyleList.end())
3070
58
    {
3071
58
      if (const unsigned *const ref = boost::get<unsigned>(&listIt->second))
3072
58
        cellStyle = queryCellStyle(*ref);
3073
58
    }
3074
63
  }
3075
138
  IWORKStylePtr_t paragraphStyle;
3076
138
  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
138
  optional<Format> format;
3086
138
  if (bool(formatId))
3087
70
  {
3088
70
    auto const &formatList=oldFormat ? m_currentTable->m_formatList : m_currentTable->m_newFormatList;
3089
70
    auto const formatIt=formatList.find(get(formatId));
3090
70
    if (formatIt != formatList.end())
3091
51
    {
3092
51
      if (auto ref = boost::get<Format>(&formatIt->second))
3093
51
      {
3094
51
        format=*ref;
3095
51
        if (format->m_type && get(format->m_type)==IWORK_CELL_TYPE_NUMBER && cellType!=IWORK_CELL_TYPE_TEXT)
3096
0
          format->m_type=cellType;
3097
51
      }
3098
51
    }
3099
19
    else
3100
19
    {
3101
19
      ETONYEK_DEBUG_MSG(("IWAParser::parseTileDefinition: can not find format %d\n", int(get(formatId))));
3102
19
    }
3103
70
  }
3104
138
  IWORKPropertyMap props;
3105
138
  bool addPropsToCellStyle=false;
3106
138
  if (format && !textSet)
3107
7
  {
3108
7
    if (get(format).m_type)
3109
7
    {
3110
7
      auto type=get(get(format).m_type);
3111
7
      if (!numberSet || (type!=IWORK_CELL_TYPE_TEXT && type!=IWORK_CELL_TYPE_NUMBER)) cellType=type;
3112
7
    }
3113
7
    addPropsToCellStyle=true;
3114
7
    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
7
    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
7
    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
7
    else
3121
7
      addPropsToCellStyle=false;
3122
7
  }
3123
3124
138
  bool needText=bool(textRef) || (bool(text) && !formula && cellType == IWORK_CELL_TYPE_TEXT);
3125
138
  if (needText)
3126
55
  {
3127
55
    assert(!m_currentText);
3128
55
    m_currentText = m_collector.createText(m_langManager);
3129
55
    if (bool(textRef))
3130
0
      parseText(get(textRef));
3131
55
    else
3132
55
    {
3133
55
      m_currentText = m_collector.createText(m_langManager);
3134
      // update the style
3135
55
      m_currentText->pushBaseLayoutStyle(m_currentTable->m_table->getDefaultLayoutStyle(column, row));
3136
55
      m_currentText->pushBaseParagraphStyle(m_currentTable->m_table->getDefaultParagraphStyle(column, row));
3137
55
      if (paragraphStyle)
3138
0
        m_currentText->setParagraphStyle(paragraphStyle);
3139
55
      m_currentText->insertText(get(text));
3140
55
      m_currentText->flushSpan();
3141
55
      m_currentText->flushParagraph();
3142
55
    }
3143
55
  }
3144
138
  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
138
  if (addPropsToCellStyle)
3151
0
    cellStyle.reset(new IWORKStyle(props, none, cellStyle));
3152
138
  optional<IWORKDateTimeData> dateTime;
3153
138
  m_currentTable->m_table->insertCell(column, row, text, m_currentText, dateTime, 1, 1, formula, unsigned(row*256+column), cellStyle, cellType);
3154
138
  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
138
  m_currentText.reset();
3174
3175
138
}
3176
3177
void IWAParser::parseTile(const unsigned id, const unsigned decalY)
3178
77
{
3179
77
  const ObjectMessage msg(*this, id, IWAObjectType::Tile);
3180
77
  if (!msg)
3181
12
    return;
3182
3183
  // rows must be fed to the collector in order
3184
65
  typedef map<unsigned, const IWAMessage *> Rows_t;
3185
65
  Rows_t rows;
3186
3187
  // save rows
3188
65
  for (const auto &it : get(msg).message(5))
3189
172
  {
3190
172
    if (!it.uint32(1) || !it.bytes(3) || !it.bytes(4))
3191
30
      continue;
3192
142
    const unsigned row = get(it.uint32(1))+decalY;
3193
142
    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
142
    rows[row] = &it;
3199
142
  }
3200
3201
  // process rows
3202
65
  for (auto it : rows)
3203
139
  {
3204
139
    bool useNewFormat=false;
3205
139
    map<unsigned,unsigned> offsets;
3206
139
    RVNGInputStreamPtr_t input;
3207
139
    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
139
    for (auto wh :
3212
139
         {
3213
139
           6, 3
3214
139
         })
3215
203
    {
3216
203
      offsets.clear();
3217
3218
203
      if (!bool(it.second->bytes(unsigned(wh)+1)))
3219
64
        continue;
3220
139
      input = get(it.second->bytes(unsigned(wh)));
3221
139
      if (!input) continue;
3222
139
      length = unsigned(getLength(input));
3223
139
      unsigned factor=1;
3224
139
      if (wh==6 && it.second->bool_(8) && get(it.second->bool_(8)))
3225
0
        factor=4;
3226
3227
139
      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
139
      useNewFormat=wh==6;
3234
139
      if (!parseColumnOffsets(get(it.second->bytes(unsigned(wh)+1)), length, offsets, factor))
3235
0
        continue;
3236
139
      break;
3237
139
    }
3238
3239
283
    for (auto offIt=offsets.begin(); offIt!=offsets.end() ;)
3240
144
    {
3241
144
      unsigned column=offIt->first;
3242
144
      auto begPos=offIt->second;
3243
144
      ++offIt;
3244
144
      unsigned endPos=offIt==offsets.end() ? length : offIt->second;
3245
144
      input->seek((long) begPos, librevenge::RVNG_SEEK_SET);
3246
144
      parseTileDefinition(it.first, column, input, endPos, !useNewFormat);
3247
144
    }
3248
139
  }
3249
65
}
3250
3251
void IWAParser::parseTableHeaders(const unsigned id, TableHeader &header)
3252
194
{
3253
194
  const ObjectMessage msg(*this, id, IWAObjectType::Headers);
3254
194
  if (!msg)
3255
36
    return;
3256
3257
158
  for (const auto &it : get(msg).message(2))
3258
750
  {
3259
750
    if (it.uint32(1))
3260
703
    {
3261
703
      const unsigned index = get(it.uint32(1));
3262
703
      if (index >= header.m_sizes.max_key())
3263
43
      {
3264
43
        ETONYEK_DEBUG_MSG(("IWAParser::parseTableHeaders: invalid row/column index %u\n", index));
3265
43
        continue;
3266
43
      }
3267
660
      if (it.float_(2))
3268
615
        header.m_sizes.insert_back(index, index + 1, get(it.float_(2)));
3269
660
      if (it.bool_(3))
3270
610
        header.m_hidden.insert_back(index, index + 1, get(it.bool_(3)));
3271
660
    }
3272
750
  }
3273
158
}
3274
3275
void IWAParser::parseTableGridLines(unsigned id, IWORKGridLineMap_t (&gridLines)[4])
3276
79
{
3277
79
  const ObjectMessage msg(*this, id, IWAObjectType::GridLines);
3278
79
  if (!msg)
3279
5
  {
3280
5
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLInes: can not find grid lines for index %u\n", id));
3281
5
    return;
3282
5
  }
3283
367
  for (unsigned wh=0; wh<4; ++wh)
3284
293
  {
3285
293
    if (get(msg).message(wh+4).empty()) continue;
3286
105
    auto const &lineRefs = readRefs(get(msg), wh+4);
3287
105
    auto &gridLine=gridLines[wh];
3288
105
    std::for_each(lineRefs.begin(), lineRefs.end(),
3289
105
                  [this,&gridLine](unsigned lineId)
3290
130
    {
3291
130
      parseTableGridLine(lineId, gridLine);
3292
130
    }
3293
105
                 );
3294
105
  }
3295
74
}
3296
3297
void IWAParser::parseTableGridLine(unsigned id, IWORKGridLineMap_t &gridLine)
3298
130
{
3299
130
  const ObjectMessage msg(*this, id, IWAObjectType::GridLine);
3300
130
  if (!msg)
3301
8
  {
3302
8
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find grid line for index %u\n", id));
3303
8
    return;
3304
8
  }
3305
122
  if (!get(msg).uint32(1))
3306
0
  {
3307
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find the main position index %u\n", id));
3308
0
    return;
3309
0
  }
3310
122
  auto pos1=get(get(msg).uint32(1));
3311
122
  const deque<IWAMessage> &lines = get(msg).message(2).repeated();
3312
122
  if (gridLine.find(pos1)==gridLine.end())
3313
122
    gridLine.insert(IWORKGridLineMap_t::value_type(pos1,IWORKGridLine_t(0,4096,nullptr)));
3314
122
  auto &flatSegments=gridLine.find(pos1)->second;
3315
122
  for (auto it : lines)
3316
124
  {
3317
124
    if (!it.uint32(1) || !it.uint32(2))
3318
3
    {
3319
3
      ETONYEK_DEBUG_MSG(("IWAParser::parseTableGridLine: can not find the second position index %u\n", id));
3320
3
      continue;
3321
3
    }
3322
121
    IWORKPropertyMap props;
3323
121
    if (it.message(3))
3324
121
    {
3325
121
      IWORKStroke stroke;
3326
121
      readStroke(get(it.message(3)), stroke);
3327
121
      props.put<property::SFTStrokeProperty>(stroke);
3328
121
    }
3329
121
    flatSegments.insert_back(get(it.uint32(1)),get(it.uint32(1))+get(it.uint32(2)), std::make_shared<IWORKStyle>(props,none,none));
3330
121
  }
3331
122
}
3332
3333
bool IWAParser::parseFormat(const IWAMessage &msg, IWAParser::Format &format)
3334
92
{
3335
92
  if (!msg.uint32(1))
3336
0
  {
3337
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: can not find the main type\n"));
3338
0
    return false;
3339
0
  }
3340
92
  auto uid=readUID(msg,41);
3341
92
  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
92
  auto type=get(msg.uint32(1));
3354
92
  format.m_type=IWORK_CELL_TYPE_NUMBER;
3355
92
  IWORKCellNumberType nType=IWORK_CELL_NUMBER_TYPE_DOUBLE;
3356
92
  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
92
  switch (type)
3362
92
  {
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
92
  case 260:
3400
92
    format.m_type=IWORK_CELL_TYPE_TEXT;
3401
92
    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
0
  default:
3444
0
    ETONYEK_DEBUG_MSG(("IWAParser::parseFormat: find unknown type=%d\n", int(type)));
3445
0
    return false;
3446
92
  }
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
92
}
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
49
{
3823
49
  const ObjectMessage msg(*this, id, IWAObjectType::Link);
3824
49
  if (!msg)
3825
49
    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: */