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