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