1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* libwps
3 * Version: MPL 2.0 / LGPLv2.1+
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * Major Contributor(s):
10 * Copyright (C) 2006, 2007 Andrew Ziem
11 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13 *
14 * For minor contributions see the git repository.
15 *
16 * Alternatively, the contents of this file may be used under the terms
17 * of the GNU Lesser General Public License Version 2.1 or later
18 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19 * applicable instead of those above.
20 */
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <cmath>
26 #include <sstream>
27 #include <limits>
28 #include <map>
29 #include <stack>
30 #include <utility>
31
32 #include <librevenge-stream/librevenge-stream.h>
33
34 #include "libwps_internal.h"
35 #include "libwps_tools_win.h"
36
37 #include "WKSChart.h"
38 #include "WKSContentListener.h"
39 #include "WPSEntry.h"
40 #include "WPSFont.h"
41 #include "WPSPosition.h"
42 #include "WPSStream.h"
43
44 #include "Lotus.h"
45 #include "LotusStyleManager.h"
46
47 #include "LotusChart.h"
48
49 namespace LotusChartInternal
50 {
51
52 ///////////////////////////////////////////////////////////////////
53 //! the chart of a Lotus Pro Dos
54 class Chart final : public WKSChart
55 {
56 public:
57 //! constructor
Chart(LotusChart & parser,std::shared_ptr<WPSStream> const & stream)58 explicit Chart(LotusChart &parser, std::shared_ptr<WPSStream> const &stream)
59 : WKSChart()
60 , m_fileType(-1)
61 , m_hasLegend(false)
62 , m_fileSerieStyles(false)
63 , m_parser(parser)
64 , m_stream(stream)
65 {
66 }
67 //! send the zone content (called when the zone is of text type)
68 void sendContent(TextZone const &zone, WPSListenerPtr &listener) const final;
69 //! the chart type
70 int m_fileType;
71 //! a flag to know if we have some legend
72 bool m_hasLegend;
73 //! a flag to know if we have seen some serie style
74 bool m_fileSerieStyles;
75 //! wk3 serie format
76 struct SerieFormat
77 {
78 //! constructor
SerieFormatLotusChartInternal::Chart::SerieFormat79 SerieFormat()
80 : m_color(0)
81 , m_hash(0)
82 , m_yAxis(1)
83 , m_format(0)
84 , m_align(0)
85 {
86 }
87 //! the color
88 int m_color;
89 //! the hash
90 int m_hash;
91 //! the y axis
92 int m_yAxis;
93 //! the format
94 int m_format;
95 //! the label alignement
96 int m_align;
97 };
98
99 //! the series format
100 SerieFormat m_serieFormats[6];
101 protected:
102 //! the parser
103 LotusChart &m_parser;
104 //! the input stream
105 std::shared_ptr<WPSStream> m_stream;
106 };
107
sendContent(TextZone const & zone,WPSListenerPtr & listener) const108 void Chart::sendContent(TextZone const &zone, WPSListenerPtr &listener) const
109 {
110 if (!listener.get())
111 {
112 WPS_DEBUG_MSG(("LotusChartInternal::Chart::sendContent: no listener\n"));
113 return;
114 }
115 long pos = m_stream->m_input->tell();
116 listener->setFont(zone.m_font);
117 bool sendText=false;
118 for (auto const &e : zone.m_textEntryList)
119 {
120 if (!e.valid()) continue;
121 if (sendText) listener->insertEOL(true);
122 m_parser.sendText(m_stream, e);
123 sendText=true;
124 }
125 m_stream->m_input->seek(pos, librevenge::RVNG_SEEK_SET);
126 }
127
128 //! the state of LotusChart
129 struct State
130 {
131 //! constructor
StateLotusChartInternal::State132 State()
133 : m_version(-1)
134 , m_idChartMap()
135 , m_chartId(-1)
136 {
137 }
138 /** returns a chart corresponding to an id, create it if needed.
139
140 \note almost always a chart definition appears before the
141 other chart's structures, but this is not always true...
142 */
getChartLotusChartInternal::State143 std::shared_ptr<Chart> getChart(int id, LotusChart &parser, std::shared_ptr<WPSStream> stream)
144 {
145 if (m_idChartMap.find(id)!=m_idChartMap.end())
146 return m_idChartMap.find(id)->second;
147 std::shared_ptr<Chart> newChart(new LotusChartInternal::Chart(parser, stream));
148 if (id>=0)
149 m_idChartMap[id]=newChart;
150 else
151 {
152 WPS_DEBUG_MSG(("LotusChartInternal::State::getChart: call with id=%d, create temporary chart\n", id));
153 }
154 return newChart;
155
156 }
157 //! the file version
158 int m_version;
159 //! list of chart
160 std::map<int,std::shared_ptr<Chart> > m_idChartMap;
161 //! the current chart id(wps3Mac)
162 int m_chartId;
163 };
164
165 }
166
167 // constructor, destructor
LotusChart(LotusParser & parser)168 LotusChart::LotusChart(LotusParser &parser)
169 : m_listener()
170 , m_mainParser(parser)
171 , m_styleManager(parser.m_styleManager)
172 , m_state(new LotusChartInternal::State)
173 {
174 }
175
~LotusChart()176 LotusChart::~LotusChart()
177 {
178 }
179
cleanState()180 void LotusChart::cleanState()
181 {
182 m_state.reset(new LotusChartInternal::State);
183 }
184
getNameToChartIdMap() const185 std::map<std::string,int> LotusChart::getNameToChartIdMap() const
186 {
187 std::map<std::string,int> res;
188 for (auto it : m_state->m_idChartMap)
189 {
190 auto const &chart = it.second;
191 if (chart || !chart->m_name.empty())
192 res[chart->m_name.cstr()]=it.first;
193 }
194 return res;
195 }
196
updateState()197 void LotusChart::updateState()
198 {
199 std::set<int> toRemoveSet;
200 /* In pc's wk3 files, the current chart is unamed while the others
201 have a name. So if we have more than one chart, suppose that
202 the creator has named all its charts and remove the unamed
203 chart to avoid dupplication...
204
205 In mac's wk3 files, all used files are named, so this must be ok
206 */
207 bool removeNoName=version()==1 && m_state->m_idChartMap.size()>1;
208 for (auto &it : m_state->m_idChartMap)
209 {
210 auto id=it.first;
211 auto chart=it.second;
212 if (!chart || (removeNoName && chart->m_name.empty()))
213 {
214 toRemoveSet.insert(id);
215 continue;
216 }
217 updateChart(*chart, id);
218 // check if the chart contain at least one serie
219 bool findSomeSerie=false;
220 for (auto cIt : chart->getIdSerieMap())
221 {
222 if (cIt.second.m_ranges[0].valid(cIt.second.m_ranges[1]))
223 {
224 findSomeSerie=true;
225 break;
226 }
227 }
228 if (!findSomeSerie)
229 toRemoveSet.insert(id);
230 }
231 for (auto id : toRemoveSet)
232 m_state->m_idChartMap.erase(id);
233 }
234
getNumCharts() const235 int LotusChart::getNumCharts() const
236 {
237 return int(m_state->m_idChartMap.size());
238 }
239
version() const240 int LotusChart::version() const
241 {
242 if (m_state->m_version<0)
243 m_state->m_version=m_mainParser.version();
244 return m_state->m_version;
245 }
246
247 ////////////////////////////////////////////////////////////
248 // low level
249
250 ////////////////////////////////////////////////////////////
251 // general
252 ////////////////////////////////////////////////////////////
253
readChart(std::shared_ptr<WPSStream> stream)254 bool LotusChart::readChart(std::shared_ptr<WPSStream> stream)
255 {
256 RVNGInputStreamPtr &input=stream->m_input;
257 libwps::DebugFile &ascFile=stream->m_ascii;
258 libwps::DebugStream f;
259 long pos = input->tell();
260 auto type = long(libwps::read16(input));
261 if (type != 0x11)
262 {
263 WPS_DEBUG_MSG(("LotusChart::readChart: not a chart name\n"));
264 return false;
265 }
266 auto sz = long(libwps::readU16(input));
267 long endPos=pos+4+sz;
268 f << "Entries(ChartDef):sz=" << sz << ",";
269 if (sz < 0xB2) // find b2 or b3
270 {
271 WPS_DEBUG_MSG(("LotusChart::readChart: chart name is too short\n"));
272 f << "###";
273 ascFile.addPos(pos);
274 ascFile.addNote(f.str().c_str());
275 return true;
276 }
277 auto id=int(libwps::readU8(input));
278 auto chart=m_state->getChart(id, *this, stream);
279 f << "id=" << id << ",";
280 std::string name("");
281 auto fontType=m_mainParser.getDefaultFontType();
282 for (int i=0; i<16; ++i)
283 {
284 unsigned char c = libwps::readU8(input);
285 if (c == '\0') break;
286 name+=char(c);
287 }
288 if (!name.empty())
289 {
290 chart->m_name=libwps_tools_win::Font::unicodeString(name, fontType);
291 f << name << ",";
292 }
293 input->seek(pos+4+17, librevenge::RVNG_SEEK_SET);
294 // group 0: title, 1: axis name, 2: axis data+note+subtitle+legend
295 int val;
296 for (int i=0; i<3; ++i) // useme
297 {
298 val=int(libwps::read8(input));
299 if (val) f << "font[group" << i << "]=" << val << ",";
300 }
301 for (int i=0; i<6; ++i)
302 {
303 chart->m_serieFormats[i].m_color=val=int(libwps::readU8(input));
304 if (!val) continue; // auto
305 if (val==255)
306 f << "serie" << i << "[color]=range,";
307 else if (val==254)
308 f << "serie" << i << "[color]=hidden,";
309 else
310 f << "serie" << i << "[color]=" << val << ",";
311 }
312 val=int(libwps::read8(input));
313 if (val) f << "f0=" << val << ",";
314 for (int i=0; i<6; ++i)
315 {
316 chart->m_serieFormats[i].m_hash=val=int(libwps::readU8(input));
317 if (!val) continue;
318 if (val==255)
319 f << "hash[serie" << i << "]=range,";
320 else
321 f << "hash[serie" << i << "]=" << val << ",";
322 }
323 ascFile.addPos(pos);
324 ascFile.addNote(f.str().c_str());
325
326 pos=input->tell();
327 f.str("");
328 f << "ChartDef-A:";
329 for (int i=0; i<6; ++i) // small number
330 {
331 val=int(libwps::read8(input));
332 if (!val) continue;
333 f << "f" << i << "=" << val << ",";
334 }
335 for (int i=0; i<3; ++i) // useme
336 {
337 val=int(libwps::read8(input));
338 if (val) f << "fSize[group" << i << "]=" << val << ",";
339 }
340 val=int(libwps::readU8(input));
341 bool showGridY=(val&2);
342 if (val&1) // checkme
343 f << "X,";
344 chart->getAxis(0).m_showGrid=(val&1);
345 if (val&2)
346 f << "Y,";
347 chart->getAxis(1).m_showGrid=showGridY;
348 val &= 0xfc;
349 if (val) f << "##grid" << "=" << val << ",";
350 val=int(libwps::readU8(input)); // can be 0,1,2
351 if (val&1) f << "color=bw,";
352 val &= 0xfe;
353 if (val) f << "##color" << "=" << val << ",";
354 auto const &chartType=chart->m_fileType=int(libwps::readU8(input));
355 chart->m_type=WKSChart::Serie::S_Bar;
356 switch (chartType)
357 {
358 case 0: // line
359 chart->m_type=WKSChart::Serie::S_Line;
360 break;
361 case 1: // bar
362 break;
363 case 2: // XY
364 chart->m_type=WKSChart::Serie::S_Scatter;
365 break;
366 case 3: // bar stacked
367 chart->m_dataStacked=true;
368 break;
369 case 4: // pie
370 chart->m_type=WKSChart::Serie::S_Circle;
371 break;
372 case 5: // min-max
373 chart->m_type=WKSChart::Serie::S_Stock;
374 break;
375 case 6: // radar (checkme)
376 chart->m_type=WKSChart::Serie::S_Radar;
377 break;
378 case 7: // mixed
379 break;
380 default:
381 WPS_DEBUG_MSG(("LotusChart::readChart: unknown chart type\n"));
382 f << "###";
383 }
384 f << "type=" << chartType << ",";
385
386 char const *axisNames[]= {"X","Y","YSecond"};
387 for (int i=0; i<3; ++i)
388 {
389 val=int(libwps::read8(input));
390 // 0=auto
391 if (val==-1)
392 {
393 chart->getAxis(i).m_automaticScaling=false;
394 f << "scale[" << axisNames[i] << "]=manual,";
395 }
396 else if (val) f << "###scale[" << axisNames[i] << "]=" << val << ",";
397 }
398 for (auto axisName : axisNames) // useme
399 {
400 val=int(libwps::read8(input));
401 // 0=auto
402 if (val==-1) f << "setExponent[" << axisName << "]=manual,";
403 else if (val) f << "###setExponent[" << axisName << "]=" << val << ",";
404 }
405 for (auto axisName : axisNames) // useme
406 {
407 val=int(libwps::read8(input));
408 // 0=auto
409 if (val==-1) f << "legend[" << axisName << "]=manual,";
410 else if (val==1) f << "legend[" << axisName << "]=none,";
411 else if (val) f << "###legend[" << axisName << "]=" << val << ",";
412 }
413 for (int i=0; i<3; ++i)
414 {
415 val=int(libwps::read8(input));
416 // 0=normal
417 auto &axis=chart->getAxis(i);
418 if (val==1)
419 {
420 f << "axis[" << axisNames[i] << "]=log,";
421 axis.m_type= WKSChart::Axis::A_Logarithmic;
422 }
423 else
424 {
425 axis.m_type= WKSChart::Axis::A_Numeric;
426 if (val) f << "###axis[" << axisNames[i] << "]=" << val << ",";
427 }
428 }
429 for (auto axisName : axisNames)
430 {
431 val=int(libwps::read8(input));
432 // 0=auto
433 if (val==-1) f << "setWidth[" << axisName << "]=manual,";
434 else if (val) f << "###setWidth[" << axisName << "]=" << val << ",";
435 }
436 ascFile.addPos(pos);
437 ascFile.addNote(f.str().c_str());
438
439 pos=input->tell();
440 f.str("");
441 f << "ChartDef-B:";
442 for (int i=0; i<6; ++i)
443 {
444 chart->m_serieFormats[i].m_yAxis=val=int(libwps::read8(input));
445 if (val==1) continue; // primary axis
446 if (val==2)
447 f << "serie" << i << "[axis]=secondary,";
448 else
449 f << "##serie" << i << "[axis]=" << val << ",";
450 }
451 for (int i=0; i<6; ++i) // small number
452 {
453 chart->m_serieFormats[i].m_format=val=int(libwps::readU8(input));
454 if (!val) continue;
455 if (val<5)
456 {
457 char const *wh[]= {"both", "lines", "symbols", "neither", "area"};
458 f << "serie" << i << "[format]=" << wh[val] << ",";
459 }
460 else
461 f << "##serie" << i << "[format]=" << val << ",";
462 }
463 for (int i=0; i<6; ++i) // small number
464 {
465 chart->m_serieFormats[i].m_align=val=int(libwps::readU8(input));
466 if (!val) continue;
467 if (val<5)
468 {
469 char const *wh[]= {"center", "right", "below", "left", "above"};
470 f << "serie" << i << "[align]=" << wh[val] << ",";
471 }
472 else
473 f << "##serie" << i << "[align]=" << val << ",";
474 }
475 ascFile.addPos(pos);
476 ascFile.addNote(f.str().c_str());
477
478 pos=input->tell();
479 f.str("");
480 f << "ChartDef-C:";
481 for (int i=0; i<7; ++i) // small number expect f18=0|4|64
482 {
483 val=int(libwps::read8(input));
484 if (i==0)
485 {
486 chart->getAxis(1).m_showGrid=false;
487 chart->getAxis(2).m_showGrid=false;
488 if (val==0) // 0: means y primary
489 {
490 if (showGridY) chart->getAxis(1).m_showGrid=true;
491 }
492 else if (val==1)
493 {
494 if (showGridY) chart->getAxis(2).m_showGrid=true;
495 f << "grid[hori]=ysecond,";
496 }
497 else if (val==2)
498 {
499 if (showGridY)
500 {
501 chart->getAxis(1).m_showGrid=true;
502 chart->getAxis(2).m_showGrid=true;
503 }
504 f << "grid[hori]=y+ysecond,";
505 }
506 else
507 f << "##grid[hori]=" << val << ",";
508 continue;
509 }
510 else if (i==1)
511 {
512 if (val&1)
513 {
514 chart->m_dataVertical=true;
515 f << "swapXY,"; // Y is horizontal, X vertical
516 }
517 val &= 0xfe;
518 }
519 else if (i==3)
520 {
521 if (val&1)
522 {
523 chart->m_dataPercentStacked=true;
524 f << "percentage,";
525 }
526 val &= 0xfe;
527 }
528 else if (i==4)
529 {
530 if (val&1)
531 {
532 chart->m_dataStacked=true;
533 f << "stacked,";
534 }
535 val &= 0xfe;
536 }
537 else if (i==5)
538 {
539 if (val&1)
540 {
541 chart->m_is3D=true;;
542 f << "drop[shadow],"; // ie. 2.5D
543 }
544 if (val&2)
545 {
546 chart->m_is3D=true;;
547 chart->m_is3DDeep=true;;
548 f << "3d[range],"; // ie 3D
549 }
550 if (val&4) f << "show[table],"; // show data table below the chart
551 // plot area border
552 if (val&0x10) f << "noBorder[L],";
553 if (val&0x20) f << "noBorder[R],";
554 if (val&0x40) f << "noBorder[T],";
555 if (val&0x80) f << "noBorder[B],";
556 val &= 0x8;
557 }
558 if (val) f << "f" << i+6 << "=" << val << ",";
559 }
560 for (int i=0; i<3; ++i)
561 {
562 // useme
563 val=int(libwps::read8(input));
564 if (val) f << "color[group" << i << "]=" << val << ",";
565 }
566 val=int(libwps::read16(input));
567 if (val!=1) f << "ticks=" << val << ",";
568 for (auto axisName : axisNames)
569 {
570 val=int(libwps::read16(input));
571 if (val!=14) f << "width" << axisName << "=" << val << ",";;
572 }
573 for (int i=0; i<2; ++i) // small number expect g2=g3=0|1
574 {
575 val=int(libwps::read16(input));
576 if (val) f << "g" << i+1 << "=" << val << ",";
577 }
578 for (auto axisName : axisNames)
579 {
580 val=int(libwps::read16(input));
581 if (val) f << "exp[manual," << axisName << "]=" << val << ",";;
582 }
583 ascFile.addPos(pos);
584 ascFile.addNote(f.str().c_str());
585
586 pos=input->tell();
587 f.str("");
588 f << "ChartDef-D:";
589 for (auto axisName : axisNames)
590 {
591 // useme
592 f << axisName << "=[";
593 val=int(libwps::readU8(input)); // or 2 bytes
594 if (val!=0x71) f << "fmt=" << std::hex << val << std::dec << ",";
595 for (int j=0; j<3; ++j)
596 {
597 val=int(libwps::readU8(input));
598 if (val) f << "f" << j << "=" << val << ",";
599 }
600 f << "],";
601 }
602 ascFile.addDelimiter(input->tell(),'|');
603 for (int i=0; i<3; ++i)
604 {
605 bool isNan;
606 double value;
607 if (!libwps::readDouble10(input, value, isNan))
608 f << "##min" << axisNames[i] << ",";
609 else
610 {
611 chart->getAxis(i).m_scaling[0]=float(value);
612 if (value<0 || value>0)
613 f << "min" << axisNames[i] << "=" << value << ",";
614 }
615 }
616 for (int i=0; i<3; ++i)
617 {
618 bool isNan;
619 double value;
620 if (!libwps::readDouble10(input, value, isNan))
621 f << "##max" << axisNames[i] << ",";
622 else
623 {
624 chart->getAxis(i).m_scaling[1]=float(value);
625 if (value<0 || value>0)
626 f << "max" << axisNames[i] << "=" << value << ",";
627 }
628 }
629 if (input->tell()!=endPos)
630 {
631 ascFile.addDelimiter(input->tell(),'|');
632 input->seek(endPos, librevenge::RVNG_SEEK_SET);
633 }
634 ascFile.addPos(pos);
635 ascFile.addNote(f.str().c_str());
636 return true;
637 }
638
readChartName(std::shared_ptr<WPSStream> stream)639 bool LotusChart::readChartName(std::shared_ptr<WPSStream> stream)
640 {
641 RVNGInputStreamPtr &input=stream->m_input;
642 libwps::DebugFile &ascFile=stream->m_ascii;
643 libwps::DebugStream f;
644 long pos = input->tell();
645 auto type = long(libwps::read16(input));
646 if (type != 0x12)
647 {
648 WPS_DEBUG_MSG(("LotusChart::readChartName: not a chart name\n"));
649 return false;
650 }
651 auto sz = long(libwps::readU16(input));
652 f << "Entries(ChartName):";
653 if (sz < 3)
654 {
655 WPS_DEBUG_MSG(("LotusChart::readChartName: chart name is too short\n"));
656 f << "###";
657 ascFile.addPos(pos);
658 ascFile.addNote(f.str().c_str());
659 return true;
660 }
661
662 auto cId=int(libwps::readU8(input));
663 f << "chart[id]=" << cId << ",";
664 auto chart=m_state->getChart(cId, *this, stream);
665 auto id=int(libwps::readU8(input));
666 f << "data[id]=" << id << ",";
667 std::string name("");
668 auto fontType=m_mainParser.getDefaultFontType();
669 for (long i = 0; i < sz-2; i++)
670 {
671 unsigned char c = libwps::readU8(input);
672 if (c == '\0') break;
673 name+=char(c);
674 }
675 f << name << ",";
676 if (!name.empty())
677 {
678 auto uniName=libwps_tools_win::Font::unicodeString(name, fontType);
679 switch (id)
680 {
681 case 0:
682 case 1:
683 case 2:
684 case 3:
685 case 4:
686 case 5:
687 {
688 auto *serie=chart->getSerie(id, true);
689 serie->m_legendText=uniName;
690 chart->m_hasLegend=true;
691 break;
692 }
693 case 6:
694 case 7:
695 case 8:
696 chart->getAxis(id-6).m_title=uniName;
697 break;
698 case 9:
699 case 10:
700 case 11:
701 chart->getAxis(id-9).m_subTitle=uniName;
702 break;
703 case 12:
704 case 13:
705 case 14: // note1
706 case 15: // note2
707 {
708 auto wh=id==12 ? WKSChart::TextZone::T_Title :
709 id==13 ? WKSChart::TextZone::T_SubTitle : WKSChart::TextZone::T_Footer;
710 WPSEntry entry;
711 entry.setBegin(pos+6);
712 entry.setEnd(input->tell());
713 auto *textZone=chart->getTextZone(wh, true);
714 textZone->m_contentType=WKSChart::TextZone::C_Text;
715 textZone->m_textEntryList.push_back(entry);
716 break;
717 }
718 default:
719 break;
720 }
721 }
722 if (input->tell()!=pos+4+sz && input->tell()+1!=pos+4+sz)
723 {
724 WPS_DEBUG_MSG(("LotusChart::readChartName: the zone seems too short\n"));
725 f << "##";
726 ascFile.addDelimiter(input->tell(), '|');
727 }
728 ascFile.addPos(pos);
729 ascFile.addNote(f.str().c_str());
730 return true;
731 }
732
733 ////////////////////////////////////////////////////////////
734 // wk3mac
735 ////////////////////////////////////////////////////////////
readMacHeader(std::shared_ptr<WPSStream> stream,long endPos,int & chartId)736 bool LotusChart::readMacHeader(std::shared_ptr<WPSStream> stream, long endPos, int &chartId)
737 {
738 if (!stream) return false;
739 RVNGInputStreamPtr &input = stream->m_input;
740 libwps::DebugFile &ascFile=stream->m_ascii;
741 libwps::DebugStream f;
742 long pos = input->tell();
743 long sz=endPos-pos;
744 f << "Entries(ChartMac):";
745 if (sz<12)
746 {
747 WPS_DEBUG_MSG(("LotusChart::readChartMac: Oops the zone seems too short\n"));
748 f << "###";
749 ascFile.addPos(pos-6);
750 ascFile.addNote(f.str().c_str());
751 m_state->m_chartId=chartId=-1;
752 return true;
753 }
754
755 m_state->m_chartId=chartId=int(libwps::read16(input));
756 f << "chart[id]=" << chartId << ",";
757 auto chart=m_state->getChart(chartId,*this,stream);
758 for (int i=0; i<5; ++i) // f1=[4c][02]5[19], f2=50, f3=14
759 {
760 auto val=int(libwps::read16(input));
761 if (!val) continue;
762 if (i==1)
763 {
764 if (val&0x20)
765 {
766 f << "area[stacked],";
767 chart->m_dataStacked=true;
768 }
769 val&=0xffdf;
770 }
771 if (val)
772 f << "f" << i << "=" << std::hex << val << std::dec << ",";
773 }
774 ascFile.addPos(pos-6);
775 ascFile.addNote(f.str().c_str());
776 input->seek(endPos, librevenge::RVNG_SEEK_SET);
777 return true;
778 }
779
readMacAxis(std::shared_ptr<WPSStream> stream,long endPos)780 bool LotusChart::readMacAxis(std::shared_ptr<WPSStream> stream, long endPos)
781 {
782 if (!stream) return false;
783 RVNGInputStreamPtr &input = stream->m_input;
784 libwps::DebugFile &ascFile=stream->m_ascii;
785 libwps::DebugStream f;
786 long pos = input->tell();
787 long sz=endPos-pos;
788
789 f << "Entries(ChartAxis):id=" << m_state->m_chartId << ",";
790 if (sz!=56)
791 {
792 WPS_DEBUG_MSG(("LotusChart::readMacAxis: the size seems bad\n"));
793 f << "##sz";
794 ascFile.addPos(pos-6);
795 ascFile.addNote(f.str().c_str());
796 return true;
797 }
798 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
799 auto id=int(libwps::readU8(input));
800 if (id<0||id>=3)
801 {
802 WPS_DEBUG_MSG(("LotusChart::readMacAxis: the id seems bad\n"));
803 f << "###";
804 }
805 auto &axis=chart->getAxis((id<0||id>=3) ? 4 : id);
806 f << "id[axis]=" << id << ",";
807 auto format=int(libwps::readU8(input));
808 if ((format&0x20)==0)
809 {
810 f << "hidden[name],";
811 axis.m_showTitle=false;
812 }
813 ascFile.addDelimiter(input->tell(),'|');
814 ascFile.addPos(pos-6);
815 ascFile.addNote(f.str().c_str());
816 input->seek(endPos, librevenge::RVNG_SEEK_SET);
817 return true;
818 }
819
readMacSerie(std::shared_ptr<WPSStream> stream,long endPos)820 bool LotusChart::readMacSerie(std::shared_ptr<WPSStream> stream, long endPos)
821 {
822 if (!stream) return false;
823 RVNGInputStreamPtr &input = stream->m_input;
824 libwps::DebugFile &ascFile=stream->m_ascii;
825 libwps::DebugStream f;
826 long pos = input->tell();
827 long sz=endPos-pos;
828
829 f << "Entries(ChartSerie):id=" << m_state->m_chartId << ",";
830 if (sz!=28)
831 {
832 WPS_DEBUG_MSG(("LotusChart::readMacSerie: the size seems bad\n"));
833 f << "##sz";
834 ascFile.addPos(pos-6);
835 ascFile.addNote(f.str().c_str());
836 return true;
837 }
838 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
839 auto id=int(libwps::readU8(input));
840 f << "id[serie]=" << id << ",";
841 chart->m_fileSerieStyles=true;
842 auto *serie=chart->getSerie(id, true);
843 serie->m_type=chart->m_type;
844 auto format=int(libwps::readU8(input));
845 if (id>=0 && id<6)
846 {
847 auto const &sFormat=chart->m_serieFormats[id];
848 if (sFormat.m_yAxis==2)
849 serie->m_useSecondaryY=true;
850 if (chart->m_fileType<=3 || chart->m_fileType==7)
851 {
852 switch (sFormat.m_format)
853 {
854 case 0: // both
855 if (chart->m_fileType==7 && (format&3)==1) // special case, mixed means last serie as line
856 serie->m_type = WKSChart::Serie::S_Line;
857 serie->m_pointType=WKSChart::Serie::P_Automatic;
858 break;
859 case 1: // lines
860 serie->m_type = WKSChart::Serie::S_Line;
861 break;
862 case 2: // symbol
863 serie->m_pointType=WKSChart::Serie::P_Automatic;
864 serie->m_style.m_lineWidth=0;
865 break;
866 case 3:
867 serie->m_style.m_lineWidth=0;
868 break;
869 case 4:
870 serie->m_type = WKSChart::Serie::S_Area;
871 break;
872 default:
873 break;
874 }
875 }
876 }
877 else
878 {
879 switch (format&3)
880 {
881 case 1:
882 f << "line,";
883 if (chart->m_fileType==7)
884 serie->m_type=serie->S_Line;
885 serie->m_pointType=WKSChart::Serie::P_Automatic;
886 break;
887 case 2: // bar
888 break;
889 default:
890 f << "##format[low]=" << (format&3) << ",";
891 }
892 if (format&4)
893 {
894 if (chart->m_fileType<=3 || chart->m_fileType==7) // force area
895 serie->m_type=serie->S_Area;
896 f << "area,";
897 }
898 }
899 if (format&0xf8) f << "##format[high]=" << (format>>5) << ",";
900 auto val=int(libwps::readU16(input));
901 if ((val>>8)==0x10)
902 f << "L" << (val&0xff) << "[select],";
903 else
904 f << "##L[select]=" << std::hex << val << std::dec << ",";
905 val=int(libwps::readU16(input));
906 if ((val>>8)==0x20)
907 {
908 f << "C" << (val&0xff) << ",";
909 m_styleManager->updateSurfaceStyle(val&0xff, serie->m_style);
910 }
911 else
912 f << "##C=" << std::hex << val << std::dec << ",";
913 for (int i=0; i<2; ++i) // i==0: ? , i==1: surface border
914 {
915 val=int(libwps::readU16(input));
916 if ((val>>8)==0x10)
917 {
918 f << "L" << (val&0xff) << (i==1 ? "[1]" : "") << ",";
919 if (i==0)
920 m_styleManager->updateLineStyle(val&0xff, serie->m_style);
921 }
922 else
923 f << "##L" << (i==1 ? "[1]" : "") << "=" << std::hex << val << std::dec << ",";
924 }
925 val=int(libwps::readU16(input));
926 if ((val>>8)==0x20) // surface external: used for pattern, ...
927 {
928 f << "C" << (val&0xff) << "[ext],";
929 m_styleManager->updateSurfaceStyle(val&0xff, serie->m_style);
930 }
931 else
932 f << "##Cext=" << std::hex << val << std::dec << ",";
933 val=int(libwps::readU16(input));
934 if (val!=id) f << "P" << val << ",";
935 if (serie->m_pointType != WKSChart::Serie::P_None)
936 {
937 switch (val)
938 {
939 case 0: // square
940 case 3: // hollow square
941 serie->m_pointType=WKSChart::Serie::P_Square;
942 break;
943 case 1: // diamond
944 case 4: // hollow diamond
945 serie->m_pointType=WKSChart::Serie::P_Diamond;
946 break;
947 case 2: // triangle
948 case 5: // hollow triangle
949 serie->m_pointType=WKSChart::Serie::P_Arrow_Up;
950 break;
951 case 8: // triangle inverted
952 serie->m_pointType=WKSChart::Serie::P_Arrow_Down;
953 break;
954 case 6: // circle
955 serie->m_pointType=WKSChart::Serie::P_Circle;
956 break;
957 case 7: // star
958 serie->m_pointType=WKSChart::Serie::P_Star;
959 break;
960 case 12: // x
961 serie->m_pointType=WKSChart::Serie::P_X;
962 break;
963 case 14: // *
964 serie->m_pointType=WKSChart::Serie::P_Asterisk;
965 break;
966 case 16: // +
967 serie->m_pointType=WKSChart::Serie::P_Plus;
968 break;
969 case 18: // Y inverted
970 serie->m_pointType=WKSChart::Serie::P_Bow_Tie;
971 break;
972 case 19: // -
973 serie->m_pointType=WKSChart::Serie::P_Horizontal_Bar;
974 break;
975 case 20: // |
976 serie->m_pointType=WKSChart::Serie::P_Vertical_Bar;
977 break;
978 default:
979 break;
980 }
981 }
982
983 for (int i=0; i<7; ++i) // 0
984 {
985 val=int(libwps::readU16(input));
986 if (val)
987 f << "f" << i+1 << "=" << val << ",";
988 }
989 ascFile.addPos(pos-6);
990 ascFile.addNote(f.str().c_str());
991 return true;
992 }
993
readMacPlacement(std::shared_ptr<WPSStream> stream,long endPos)994 bool LotusChart::readMacPlacement(std::shared_ptr<WPSStream> stream, long endPos)
995 {
996 if (!stream) return false;
997 RVNGInputStreamPtr &input = stream->m_input;
998 libwps::DebugFile &ascFile=stream->m_ascii;
999 libwps::DebugStream f;
1000 long pos = input->tell();
1001 long sz=endPos-pos;
1002
1003 f << "Entries(ChartPlacement):id=" << m_state->m_chartId << ",";
1004 if (sz!=8)
1005 {
1006 WPS_DEBUG_MSG(("LotusChart::readMacPlacement: the size seems bad\n"));
1007 f << "##sz";
1008 ascFile.addPos(pos-6);
1009 ascFile.addNote(f.str().c_str());
1010 return true;
1011 }
1012 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
1013 auto val=int(libwps::readU8(input));
1014 if ((val&0x10)==0)
1015 f << "hidden,";
1016 switch (val&3)
1017 {
1018 case 1:
1019 f << "title,";
1020 if ((val&0x10)==0)
1021 {
1022 WKSChart::TextZone::Type allTypes[]= {WKSChart::TextZone::T_Title, WKSChart::TextZone::T_SubTitle};
1023 for (auto type : allTypes)
1024 chart->getTextZone(type,true)->m_show=false;
1025 }
1026 break;
1027 case 2:
1028 f << "note,";
1029 if ((val&0x10)==0) chart->getTextZone(WKSChart::TextZone::T_Footer,true)->m_show=false;
1030 break;
1031 default:
1032 f << "##wh=" << (val&3) << ",";
1033 break;
1034 }
1035 val &= 0xec;
1036 if (val) f << "fl0=" << std::hex << val << std::dec << ",";
1037 val=int(libwps::readU8(input));
1038 if (val&0x10)
1039 f << "manual,";
1040 else if (val!=1)
1041 f << "pos=" << val << ",";
1042 ascFile.addPos(pos-6);
1043 ascFile.addNote(f.str().c_str());
1044 return true;
1045 }
1046
readMacFloor(std::shared_ptr<WPSStream> stream,long endPos)1047 bool LotusChart::readMacFloor(std::shared_ptr<WPSStream> stream, long endPos)
1048 {
1049 if (!stream) return false;
1050 RVNGInputStreamPtr &input = stream->m_input;
1051 libwps::DebugFile &ascFile=stream->m_ascii;
1052 libwps::DebugStream f;
1053 long pos = input->tell();
1054 long sz=endPos-pos;
1055
1056 f << "Entries(ChartFloor):id=" << m_state->m_chartId << ",";
1057 if (sz!=17)
1058 {
1059 WPS_DEBUG_MSG(("LotusChart::readMacFloor: the size seems bad\n"));
1060 f << "##sz";
1061 ascFile.addPos(pos-6);
1062 ascFile.addNote(f.str().c_str());
1063 return true;
1064 }
1065 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
1066 for (int i=0; i<4; ++i)
1067 {
1068 auto val=int(libwps::readU8(input));
1069 int const expected[]= {0xf,0x1e,0x12,0};
1070 if (val!=expected[i])
1071 f << "f" << i << "=" << val << ",";
1072 }
1073 for (int i=0; i<5; ++i) // i=4: floor style
1074 {
1075 auto val=int(libwps::readU16(input));
1076 if ((val>>8)==0x20)
1077 {
1078 f << "C" << (val&0xff) << "[" << i << "],";
1079 if (i==4)
1080 m_styleManager->updateSurfaceStyle(val&0xff,chart->m_floorStyle);
1081 }
1082 else
1083 f << "##C=" << std::hex << val << std::dec << "[" << i << "],";
1084 }
1085 auto val=int(libwps::readU16(input));
1086 if ((val>>8)==0x10)
1087 f << "L" << (val&0xff) << ",";
1088 else
1089 f << "##L=" << std::hex << val << std::dec << ",";
1090 val=int(libwps::readU8(input)); // 0
1091 if (val) f << "f4=" << val << ",";
1092 ascFile.addPos(pos-6);
1093 ascFile.addNote(f.str().c_str());
1094 return true;
1095 }
1096
readMacLegend(std::shared_ptr<WPSStream> stream,long endPos)1097 bool LotusChart::readMacLegend(std::shared_ptr<WPSStream> stream, long endPos)
1098 {
1099 if (!stream) return false;
1100 RVNGInputStreamPtr &input = stream->m_input;
1101 libwps::DebugFile &ascFile=stream->m_ascii;
1102 libwps::DebugStream f;
1103 long pos = input->tell();
1104 long sz=endPos-pos;
1105
1106 f << "Entries(ChartLegend):id=" << m_state->m_chartId << ",";
1107 if (sz!=7)
1108 {
1109 WPS_DEBUG_MSG(("LotusChart::readMacLegend: the size seems bad\n"));
1110 f << "##sz";
1111 ascFile.addPos(pos-6);
1112 ascFile.addNote(f.str().c_str());
1113 return true;
1114 }
1115 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
1116 auto val=int(libwps::readU8(input));
1117 if (val&0x10)
1118 f << "manual,";
1119 val&=0xef;
1120 if (val!=4) f << "f0=" << std::hex << val << std::dec << ",";
1121 val=int(libwps::readU8(input));
1122 if ((val&0x1)==0)
1123 {
1124 f << "hidden,";
1125 chart->m_hasLegend=false;
1126 }
1127 val&=0xfe;
1128 if (val!=2) f << "f1=" << std::hex << val << std::dec << ",";
1129 val=int(libwps::readU16(input));
1130 if ((val>>8)==0x40)
1131 f << "G" << (val&0xff) << ",";
1132 else
1133 f << "##G=" << std::hex << val << std::dec << ",";
1134 val=int(libwps::readU16(input));
1135 if (val!=2)
1136 f << "f2=" << val << ",";
1137 val=int(libwps::readU8(input)); // vertical/horizontal?
1138 if (val) f << "f3=" << val << ",";
1139 ascFile.addPos(pos-6);
1140 ascFile.addNote(f.str().c_str());
1141 return true;
1142 }
1143
readMacPlotArea(std::shared_ptr<WPSStream> stream,long endPos)1144 bool LotusChart::readMacPlotArea(std::shared_ptr<WPSStream> stream, long endPos)
1145 {
1146 if (!stream) return false;
1147 RVNGInputStreamPtr &input = stream->m_input;
1148 libwps::DebugFile &ascFile=stream->m_ascii;
1149 libwps::DebugStream f;
1150 long pos = input->tell();
1151 long sz=endPos-pos;
1152
1153 f << "Entries(ChartPlotArea):id=" << m_state->m_chartId << ",";
1154 if (sz!=18)
1155 {
1156 WPS_DEBUG_MSG(("LotusChart::readMacPlotArea: the size seems bad\n"));
1157 f << "##sz";
1158 ascFile.addPos(pos-6);
1159 ascFile.addNote(f.str().c_str());
1160 return true;
1161 }
1162 auto chart=m_state->getChart(m_state->m_chartId,*this,stream);
1163 auto val=int(libwps::readU16(input));
1164 if ((val>>8)==0x10)
1165 {
1166 f << "L" << (val&0xff) << ",";
1167 m_styleManager->updateLineStyle(val&0xff, chart->m_wallStyle);
1168 }
1169 else
1170 f << "##L[select]=" << std::hex << val << std::dec << ",";
1171 val=int(libwps::readU16(input));
1172 if ((val>>8)==0x20)
1173 {
1174 f << "C" << (val&0xff) << ",";
1175 m_styleManager->updateSurfaceStyle(val&0xff, chart->m_wallStyle);
1176 }
1177 else
1178 f << "##C=" << std::hex << val << std::dec << ",";
1179 val=int(libwps::readU8(input));
1180 if (val&0x10)
1181 f << "manual,";
1182 val&=0xef;
1183 if (val) f << "f0=" << std::hex << val << std::dec << ",";
1184 ascFile.addPos(pos-6);
1185 ascFile.addNote(f.str().c_str());
1186 return true;
1187 }
1188
readMacPosition(std::shared_ptr<WPSStream> stream,long endPos)1189 bool LotusChart::readMacPosition(std::shared_ptr<WPSStream> stream, long endPos)
1190 {
1191 if (!stream) return false;
1192 RVNGInputStreamPtr &input = stream->m_input;
1193 libwps::DebugFile &ascFile=stream->m_ascii;
1194 libwps::DebugStream f;
1195 long pos = input->tell();
1196 long sz=endPos-pos;
1197
1198 f << "Entries(ChartPosition):id=" << m_state->m_chartId << ",";
1199 if (sz!=9)
1200 {
1201 WPS_DEBUG_MSG(("LotusChart::readMacPosition: the size seems bad\n"));
1202 f << "##sz";
1203 ascFile.addPos(pos-6);
1204 ascFile.addNote(f.str().c_str());
1205 return true;
1206 }
1207 int dim[4];
1208 for (auto &d : dim) d=int(libwps::read16(input));
1209 if (dim[2]||dim[3])
1210 {
1211 // USEME
1212 float const scale=1.f/65536.f;
1213 f << "pos=" << Vec2f(scale*float(dim[0]),scale*float(dim[3]))
1214 << "<->" << Vec2f(scale*float(dim[2]),scale*float(dim[1])) << "%,";
1215 }
1216 auto val=int(libwps::readU8(input));
1217 if (val) f << "f0=" << val << ",";
1218 ascFile.addPos(pos-6);
1219 ascFile.addNote(f.str().c_str());
1220 return true;
1221 }
1222 ////////////////////////////////////////////////////////////
1223 // Windows wk3 and wk4 files
1224 ////////////////////////////////////////////////////////////
readSerie(std::shared_ptr<WPSStream> stream,long endPos)1225 bool LotusChart::readSerie(std::shared_ptr<WPSStream> stream, long endPos)
1226 {
1227 if (!stream) return false;
1228 RVNGInputStreamPtr &input = stream->m_input;
1229 libwps::DebugFile &ascFile=stream->m_ascii;
1230 libwps::DebugStream f;
1231 long pos = input->tell();
1232 long sz=endPos-pos;
1233
1234 f << "Entries(ChartSerie):";
1235 if (sz!=22)
1236 {
1237 WPS_DEBUG_MSG(("LotusChart::readSerie: the size seems bad\n"));
1238 f << "##sz";
1239 ascFile.addPos(pos-6);
1240 ascFile.addNote(f.str().c_str());
1241 return true;
1242 }
1243 auto cId=int(libwps::readU8(input));
1244 f << "id[chart]=" << cId << ",";
1245 auto chart=m_state->getChart(cId,*this,stream);
1246 chart->m_fileSerieStyles=true;
1247 for (int i=0; i<3; ++i) // f0=0|b4
1248 {
1249 auto val=int(libwps::readU8(input));
1250 if (val)
1251 f << "f" << i << "=" << val << ",";
1252 }
1253 auto id=int(libwps::readU8(input));
1254 f << "id[serie]=" << id << ",";
1255 auto *serie=chart->getSerie(id, true);
1256 serie->m_type = chart->m_type;
1257 auto format=int(libwps::readU8(input));
1258 if (format==2)
1259 {
1260 serie->m_useSecondaryY=true;
1261 f << "secondary[y],";
1262 }
1263 else if (format!=1)
1264 f << "##yAxis=" << format << ",";
1265 format=int(libwps::readU8(input));
1266 if (format&8)
1267 f << "bar[force],";
1268 else if (chart->m_fileType==7)
1269 serie->m_type = WKSChart::Serie::S_Line;
1270 format &= 0xf7;
1271 serie->m_style.m_lineWidth=1;
1272 if (format>=0 && format<5)
1273 {
1274 char const *wh[]= {"both", "lines", "symbols", "neither", "area"};
1275 f << "format=" << wh[format] << ",";
1276 if (chart->m_fileType<=3 || chart->m_fileType==7)
1277 {
1278 switch (format)
1279 {
1280 case 0: // both
1281 //if (chart->m_fileType==7) // special case, mixed means last serie as line
1282 // serie->m_type = WKSChart::Serie::S_Line;
1283 serie->m_pointType=WKSChart::Serie::P_Automatic;
1284 break;
1285 case 1: // lines
1286 if (chart->m_fileType==7)
1287 serie->m_type = WKSChart::Serie::S_Line;
1288 break;
1289 case 2: // symbol
1290 serie->m_pointType=WKSChart::Serie::P_Automatic;
1291 serie->m_style.m_lineWidth=0;
1292 break;
1293 case 3:
1294 serie->m_style.m_lineWidth=0;
1295 break;
1296 case 4:
1297 if (chart->m_fileType==0)
1298 chart->m_dataStacked=true;
1299 serie->m_type = WKSChart::Serie::S_Area;
1300 break;
1301 default:
1302 break;
1303 }
1304 }
1305 }
1306 else
1307 f << "###format=" << format << ",";
1308 int val;
1309 for (int i=0; i<2; ++i)
1310 {
1311 val=int(libwps::readU8(input));
1312 if (val)
1313 f << "f" << i+3 << "=" << std::hex << val << std::dec << ",";
1314 }
1315 auto col=int(libwps::readU8(input)); // 32|44|46|7c|81|a8|ff: checkme classic color 256 ?
1316 WPSColor color[3]= {WPSColor(255,0,0), WPSColor::white(), WPSColor::black()};
1317 if (m_styleManager->getColor256(col, color[0]))
1318 f << "color=" << color[0] << ",";
1319 else
1320 f << "##color=" << col << ",";
1321 for (int i=0; i<6; ++i) // g0=1|3,g1=0-3,g3=0|-1,g4=0|1
1322 {
1323 val=int(libwps::read8(input));
1324 if (i==0)
1325 {
1326 if (val!=1)
1327 f << "line[style]=" << val << ",";
1328 switch (val)
1329 {
1330 case 0: // hidden
1331 serie->m_style.m_lineWidth=0;
1332 break;
1333 case 2: // long dash
1334 serie->m_style.m_lineDashWidth.push_back(7);
1335 serie->m_style.m_lineDashWidth.push_back(3);
1336 break;
1337 case 3: // dotted line
1338 case 6: // chain dotted line
1339 serie->m_style.m_lineDashWidth.push_back(1);
1340 serie->m_style.m_lineDashWidth.push_back(3);
1341 break;
1342 case 4: // chain dash line ?
1343 case 7: // dash line
1344 serie->m_style.m_lineDashWidth.push_back(3);
1345 serie->m_style.m_lineDashWidth.push_back(3);
1346 break;
1347 case 5: // long dotted
1348 serie->m_style.m_lineDashWidth.push_back(2);
1349 serie->m_style.m_lineDashWidth.push_back(3);
1350 break;
1351 case 1: // solid
1352 default:
1353 break;
1354 }
1355 continue;
1356 }
1357 else if (i==2)
1358 {
1359 if (val!=1)
1360 f << "symbol=" << val << ",";
1361 if (serie->m_pointType != WKSChart::Serie::P_None)
1362 {
1363 switch (val)
1364 {
1365 case 1: // square
1366 case 4: // hollow square
1367 serie->m_pointType=WKSChart::Serie::P_Square;
1368 break;
1369 case 2: // diamond
1370 case 5: // hollow diamond
1371 serie->m_pointType=WKSChart::Serie::P_Diamond;
1372 break;
1373 case 3: // triangle
1374 case 6: // hollow triangle
1375 serie->m_pointType=WKSChart::Serie::P_Circle;
1376 break;
1377 case 13: // x in square
1378 case 16:
1379 serie->m_pointType=WKSChart::Serie::P_X;
1380 break;
1381 case 14: // + in diamond
1382 case 17:
1383 serie->m_pointType=WKSChart::Serie::P_Plus;
1384 break;
1385 case 19: // Y inverted in triangle
1386 case 22: // Y inverted
1387 serie->m_pointType=WKSChart::Serie::P_Bow_Tie;
1388 break;
1389 default:
1390 break;
1391 }
1392 }
1393 continue;
1394 }
1395 else if (i==3)
1396 {
1397 if (m_styleManager->getColor256(uint8_t(val), color[2]))
1398 {
1399 if (!color[2].isBlack())
1400 f << "color[line]=" << color[2] << ",";
1401 }
1402 else
1403 f << "##color[line]=" << uint8_t(val) << ",";
1404 continue;
1405 }
1406 else if (i==4)
1407 {
1408 // 0: none, 1: normal, 2: long dash, 3: dash
1409 if (val!=1) f << "line[style]=" << val << ",";
1410 continue;
1411 }
1412 else if (i==5)
1413 {
1414 if (!val) continue;
1415 if (val>0 && val<8 && serie->m_style.m_lineWidth>0)
1416 serie->m_style.m_lineWidth=float(val+1);
1417 f << "line[width]=" << val+1 << ",";
1418 continue;
1419 }
1420 if (!val) continue;
1421 f << "g" << i << "=" << val << ",";
1422 }
1423 col=int(libwps::readU8(input));
1424 if (m_styleManager->getColor256(col, color[1]))
1425 {
1426 if (!color[1].isWhite())
1427 f << "color[surf]=" << color[1] << ",";
1428 }
1429 else
1430 f << "##color[surf2]=" << col << ",";
1431 int patternId=1;
1432 for (int i=0; i<5; ++i) // h0=f
1433 {
1434 val=int(libwps::read8(input));
1435 if (i==1)
1436 {
1437 patternId=val;
1438 if (val!=1) f << "pattern[id]=" << val << ",";
1439 continue;
1440 }
1441 else if (i==4) // see chartDef
1442 {
1443 // useme: ie. probably better than to use the pattern id
1444
1445 // 1: solid, 2: fine cross, 3: fine double, 4: fine triple
1446 // 5: coarse cross, 6: coarse double, 7: coarse single, 8: hollow
1447 if (val==-1) f << "hash[id]=range,";
1448 else if (val!=1) f << "hash[id]=" << val << ",";
1449 continue;
1450 }
1451 if (!val) continue;
1452 f << "h" << i << "=" << val << ",";
1453 }
1454 WPSGraphicStyle::Pattern pattern;
1455 bool has0D=serie->m_pointType!=WKSChart::Serie::P_None;
1456 bool has1D=serie->is1DStyle() || (chart->m_fileType==2 && serie->m_style.m_lineWidth>0);
1457 bool has2D=!serie->is1DStyle() || (has1D && chart->m_is3D);
1458 if (patternId>0 && m_styleManager->getPattern64(patternId,pattern))
1459 {
1460 if (version()>=3)
1461 {
1462 pattern.m_colors[0]=color[0];
1463 pattern.m_colors[1]=color[1];
1464 }
1465 else
1466 {
1467 pattern.m_colors[0]=WPSColor::white();
1468 pattern.m_colors[1]=color[0];
1469 }
1470 WPSColor finalColor;
1471 if (has0D || has2D)
1472 {
1473 if (pattern.getUniqueColor(finalColor))
1474 serie->m_style.setSurfaceColor(finalColor);
1475 else
1476 serie->m_style.setPattern(pattern);
1477 }
1478 if (has1D && pattern.getAverageColor(finalColor))
1479 serie->m_style.m_lineColor=finalColor;
1480 }
1481 else
1482 {
1483 if (has1D || patternId==0)
1484 serie->m_style.m_lineColor=color[0];
1485 if (has0D || (has2D && patternId!=0))
1486 serie->m_style.setSurfaceColor(color[0]);
1487 }
1488 ascFile.addPos(pos-6);
1489 ascFile.addNote(f.str().c_str());
1490 return true;
1491 }
1492
readSerieName(std::shared_ptr<WPSStream> stream,long endPos)1493 bool LotusChart::readSerieName(std::shared_ptr<WPSStream> stream, long endPos)
1494 {
1495 if (!stream) return false;
1496 RVNGInputStreamPtr &input = stream->m_input;
1497 libwps::DebugFile &ascFile=stream->m_ascii;
1498 libwps::DebugStream f;
1499 long pos = input->tell();
1500 long sz=endPos-pos;
1501
1502 f << "Entries(ChartSerName):";
1503 if (sz<6)
1504 {
1505 WPS_DEBUG_MSG(("LotusChart::readSerieName: the size seems bad\n"));
1506 f << "##sz";
1507 ascFile.addPos(pos-6);
1508 ascFile.addNote(f.str().c_str());
1509 return true;
1510 }
1511 auto cId=int(libwps::readU8(input));
1512 f << "id[chart]=" << cId << ",";
1513 auto chart=m_state->getChart(cId,*this,stream);
1514 for (int i=0; i<3; ++i) // 0
1515 {
1516 auto val=int(libwps::readU8(input));
1517 if (val)
1518 f << "f" << i << "=" << val << ",";
1519 }
1520 auto id=int(libwps::readU8(input));
1521 f << "id[serie]=" << id << ",";
1522 std::string name("");
1523 for (long i = 0; i < sz-5; i++)
1524 {
1525 unsigned char c = libwps::readU8(input);
1526 if (c == '\0') break;
1527 name+=char(c);
1528 }
1529 if (!name.empty())
1530 {
1531 f << name << ",";
1532 auto *serie=chart->getSerie(id, true);
1533 serie->m_legendText=libwps_tools_win::Font::unicodeString(name, m_mainParser.getDefaultFontType());
1534 chart->m_hasLegend=true;
1535 }
1536 ascFile.addPos(pos-6);
1537 ascFile.addNote(f.str().c_str());
1538 return true;
1539 }
1540
readSerieWidth(std::shared_ptr<WPSStream> stream,long endPos)1541 bool LotusChart::readSerieWidth(std::shared_ptr<WPSStream> stream, long endPos)
1542 {
1543 if (!stream) return false;
1544 RVNGInputStreamPtr &input = stream->m_input;
1545 libwps::DebugFile &ascFile=stream->m_ascii;
1546 libwps::DebugStream f;
1547 long pos = input->tell();
1548 long sz=endPos-pos;
1549
1550 f << "Entries(ChartSerWidth):";
1551 if (sz!=8)
1552 {
1553 WPS_DEBUG_MSG(("LotusChart::readSerieWidth: the size seems bad\n"));
1554 f << "##sz";
1555 ascFile.addPos(pos-6);
1556 ascFile.addNote(f.str().c_str());
1557 return true;
1558 }
1559 f << "id[chart]=" << int(libwps::readU8(input)) << ",";
1560 for (int i=0; i<3; ++i) // 0
1561 {
1562 auto val=int(libwps::readU8(input));
1563 if (val)
1564 f << "f" << i << "=" << val << ",";
1565 }
1566 f << "id[serie]=" << int(libwps::readU8(input)) << ",";
1567 // checkme
1568 auto val=int(libwps::readU8(input)); // 0
1569 if (val) f << "f3=" << val << ",";
1570 f << "w[inv]=" << int(libwps::readU16(input)) << ",";
1571 ascFile.addPos(pos-6);
1572 ascFile.addNote(f.str().c_str());
1573 return true;
1574 }
1575
readPlotArea(std::shared_ptr<WPSStream> stream,long endPos)1576 bool LotusChart::readPlotArea(std::shared_ptr<WPSStream> stream, long endPos)
1577 {
1578 if (!stream) return false;
1579 RVNGInputStreamPtr &input = stream->m_input;
1580 libwps::DebugFile &ascFile=stream->m_ascii;
1581 libwps::DebugStream f;
1582 long pos = input->tell();
1583 long sz=endPos-pos;
1584
1585 f << "Entries(ChartPlotArea):";
1586 if (sz!=111)
1587 {
1588 WPS_DEBUG_MSG(("LotusChart::readPlotArea: the size seems bad\n"));
1589 f << "##sz";
1590 ascFile.addPos(pos-6);
1591 ascFile.addNote(f.str().c_str());
1592 return true;
1593 }
1594 auto cId=int(libwps::readU8(input));
1595 f << "id[chart]=" << cId << ",";
1596 auto chart=m_state->getChart(cId,*this,stream);
1597 for (int i=0; i<3; ++i) // f0=0|c,f,3a
1598 {
1599 auto val=int(libwps::readU8(input));
1600 if (val) f << "f" << i << "=" << std::hex << val << std::dec << ",";
1601 }
1602 bool isNan;
1603 double value;
1604 for (int i=0; i<6; ++i)
1605 {
1606 if (!libwps::readDouble10(input, value, isNan))
1607 f << "##value,";
1608 else if (value<0 || value>0)
1609 f << "v" << i << "=" << value << ",";
1610 }
1611 ascFile.addPos(pos-6);
1612 ascFile.addNote(f.str().c_str());
1613
1614 pos=input->tell();
1615 f.str("");
1616 f << "ChartPlotArea-A:";
1617 for (int i=0; i<3; ++i) // f0=0|1, f1=0|1|5, f2=0|1
1618 {
1619 auto val=int(libwps::read16(input));
1620 if (val) f << "f" << i << "=" << val << ",";
1621 }
1622 char const *zonesName[]= {"title", "note", "serie,legend", "plot"};
1623 for (int i=0; i<4; ++i)
1624 {
1625 int dim[4]; // in percent * 65536
1626 for (auto &d : dim) d=int(libwps::readU16(input));
1627 if (dim[0]==0 && dim[1]==0 && dim[2]==0 && dim[3]==0)
1628 continue;
1629 float const scale=1.f/65536.f;
1630 WPSBox2f box(Vec2f(scale*float(dim[0]),1.f-scale*float(dim[1])), Vec2f(scale*float(dim[2]),1.f-scale*float(dim[3])));
1631 f << "pos[" << zonesName[i] << "]=" << box << "%,";
1632 if (i==2)
1633 {
1634 auto &legend=chart->getLegend();
1635 legend.m_autoPosition=false;
1636 chart->m_legendPosition=box;
1637 }
1638 else if (i==3)
1639 chart->m_plotAreaPosition=box;
1640 }
1641 for (int i=0; i<4; ++i) // UseME
1642 {
1643 auto val=int(libwps::readU8(input));
1644 if (!val) continue;
1645 f << "pos[" << zonesName[i] << "]=[";
1646 if (val&0x10)
1647 f << "manual,";
1648 if (i<2)
1649 {
1650 if ((val&0x10)==0)
1651 {
1652 if (val&1) f << "left,";
1653 if (val&2) f << "center,";
1654 if (val&4) f << "right,";
1655 }
1656 val &= 0xf8;
1657 }
1658 else if (i==2)
1659 {
1660 if ((val&0x10)==0)
1661 {
1662 if (val&4) f << "right,";
1663 if (val&8) f << "below,";
1664 }
1665 val &= 0xf3;
1666 }
1667 val &= 0xef;
1668 if (val)
1669 f << "fl=" << std::hex << val << std::dec << ",";
1670 f << "],";
1671 }
1672 char const *axisNames[]= {"X","Y","YSecond"};
1673 for (auto axisName : axisNames) // useme
1674 {
1675 auto val=int(libwps::readU8(input));
1676 if (val==0x10) continue;
1677 f << axisName << "[";
1678 if ((val&0x10)==0) f << "not10,";
1679 if (val&0x40) f << "major,";
1680 if (val&0x80) f << "minor,";
1681 val &= 0x2f;
1682 if (val)
1683 f << "fl=" << std::hex << val << std::dec;
1684 f << "],";
1685 }
1686 auto val=int(libwps::readU8(input));
1687 if (val) f << "fl=" << val << ",";
1688 val=int(libwps::readU8(input));
1689 if (val)
1690 {
1691 f << "type=" << val << ",";
1692 if (val==8)
1693 chart->m_type=WKSChart::Serie::S_Radar;
1694 }
1695 ascFile.addPos(pos);
1696 ascFile.addNote(f.str().c_str());
1697
1698 return true;
1699 }
1700
readFontsStyle(std::shared_ptr<WPSStream> stream,long endPos)1701 bool LotusChart::readFontsStyle(std::shared_ptr<WPSStream> stream, long endPos)
1702 {
1703 if (!stream) return false;
1704 RVNGInputStreamPtr &input = stream->m_input;
1705 libwps::DebugFile &ascFile=stream->m_ascii;
1706 libwps::DebugStream f;
1707 long pos = input->tell();
1708 long sz=endPos-pos;
1709
1710 f << "Entries(ChartFontsStyle):";
1711 if (sz!=38)
1712 {
1713 WPS_DEBUG_MSG(("LotusChart::readFontsStyle: the size seems bad\n"));
1714 f << "##sz";
1715 ascFile.addPos(pos-6);
1716 ascFile.addNote(f.str().c_str());
1717 return true;
1718 }
1719 f << "id[chart]=" << int(libwps::readU8(input)) << ",";
1720 for (int i=0; i<3; ++i) // 0
1721 {
1722 auto val=int(libwps::readU8(input));
1723 if (val)
1724 f << "f" << i << "=" << val << ",";
1725 }
1726 int prev=-1;
1727 f << "val=[";
1728 for (int i=0; i<17; ++i) // 20-3e, 57, ...
1729 {
1730 auto val=int(libwps::readU16(input));
1731 if (val==prev)
1732 f << "=,";
1733 else
1734 f << "F" << val << ",";
1735 prev=val;
1736 }
1737 f << "],";
1738 ascFile.addPos(pos-6);
1739 ascFile.addNote(f.str().c_str());
1740 return true;
1741 }
1742
readFramesStyle(std::shared_ptr<WPSStream> stream,long endPos)1743 bool LotusChart::readFramesStyle(std::shared_ptr<WPSStream> stream, long endPos)
1744 {
1745 if (!stream) return false;
1746 RVNGInputStreamPtr &input = stream->m_input;
1747 libwps::DebugFile &ascFile=stream->m_ascii;
1748 libwps::DebugStream f;
1749 long pos = input->tell();
1750 long sz=endPos-pos;
1751
1752 f << "Entries(ChartFramesStyle):";
1753 if (sz!=102)
1754 {
1755 WPS_DEBUG_MSG(("LotusChart::readFramesStyle: the size seems bad\n"));
1756 f << "##sz";
1757 ascFile.addPos(pos-6);
1758 ascFile.addNote(f.str().c_str());
1759 return true;
1760 }
1761 auto cId=int(libwps::readU8(input));
1762 f << "id[chart]=" << cId << ",";
1763 auto chart=m_state->getChart(cId,*this,stream);
1764 for (int i=0; i<3; ++i) // 0
1765 {
1766 auto val=int(libwps::readU8(input));
1767 if (val)
1768 f << "f" << i << "=" << val << ",";
1769 }
1770 ascFile.addPos(pos-6);
1771 ascFile.addNote(f.str().c_str());
1772
1773 for (int i=0; i<4; ++i)
1774 {
1775 pos=input->tell();
1776 char const *zonesName[]= {"title", "serie,legend", "note", "plot"};
1777 f.str("");
1778 f << "ChartFramesStyle-" << zonesName[i] << ":";
1779 WPSColor color[4]= {WPSColor::black(), WPSColor::white(), WPSColor::black(), WPSColor::black()};
1780 auto val=int(libwps::readU8(input));
1781 WPSGraphicStyle style;
1782 if (!m_styleManager->getColor256(val, color[2]))
1783 f << "col[lineId]=###" << val << ",";
1784 else if (!color[2].isBlack())
1785 {
1786 f << "col[line]=" << color[2] << ",";
1787 style.m_lineColor=color[2];
1788 }
1789 val=int(libwps::readU8(input));
1790 if (val!=1)
1791 {
1792 f << "line[style]=" << val << ",";
1793 if (val==0) style.m_lineWidth=0;
1794 }
1795 val=int(libwps::readU8(input));
1796 if (val)
1797 {
1798 f << "line[width]=" << val+1 << ",";
1799 if (style.m_lineWidth>0)
1800 style.m_lineWidth=float(val+1);
1801 }
1802 for (int j=0; j<2; ++j)
1803 {
1804 val=int(libwps::readU8(input));
1805 if (!m_styleManager->getColor256(val, color[j]))
1806 f << "col[surf" << j << "]=###" << val << ",";
1807 }
1808 auto patId=int(libwps::readU8(input));
1809 WPSGraphicStyle::Pattern pattern;
1810 if (patId>0 && m_styleManager->getPattern64(patId,pattern))
1811 {
1812 pattern.m_colors[0]=color[1];
1813 pattern.m_colors[1]=color[0];
1814
1815 WPSColor finalColor;
1816 if (!pattern.getUniqueColor(finalColor))
1817 {
1818 style.setPattern(pattern);
1819 f << pattern << ",";
1820 }
1821 else
1822 {
1823 style.setSurfaceColor(finalColor);
1824 if (!finalColor.isWhite())
1825 f << "surf=" << finalColor << ",";
1826 }
1827 }
1828 else
1829 f << "pattern[id]=##" << patId << ",";
1830 val=int(libwps::readU8(input));
1831 if (!m_styleManager->getColor256(val, color[3]))
1832 f << "frame[colId]=###" << val << ",";
1833 else if (!color[3].isBlack())
1834 f << "col[frame]=" << color[3] << ",";
1835 val=int(libwps::readU8(input));
1836 if ((i!=3 && val!=2) || (i==3 && val)) f << "type[frame]" << val << ",";
1837 if (i==0)
1838 {
1839 auto title=chart->getTextZone(WKSChart::TextZone::T_Title, true);
1840 title->m_style=style;
1841 }
1842 else if (i==1)
1843 chart->getLegend().m_style = style;
1844 else if (i==3)
1845 chart->m_wallStyle = chart->m_floorStyle = style;
1846 ascFile.addPos(pos);
1847 ascFile.addNote(f.str().c_str());
1848 }
1849 pos=input->tell();
1850 f.str("");
1851 f << "ChartFramesStyle-A:";
1852 int val;
1853 f << "plot[line1]=[";
1854 val=int(libwps::readU8(input));
1855 WPSColor lineColor;
1856 if (!m_styleManager->getColor256(val, lineColor))
1857 f << "colId=###" << val << ",";
1858 else if (!lineColor.isBlack())
1859 f << lineColor << ",";
1860 val=int(libwps::readU8(input));
1861 if (val!=1)
1862 {
1863 f << "style=" << val << ",";
1864 if (val==0) chart->m_floorStyle.m_lineWidth=0;
1865 }
1866 val=int(libwps::readU8(input));
1867 if (val)
1868 f << "width=" << val+1 << ",";
1869 f << "],";
1870 ascFile.addDelimiter(input->tell(),'|');
1871 input->seek(pos+15, librevenge::RVNG_SEEK_SET);
1872 f << "plot[line2]=[";
1873 val=int(libwps::readU8(input));
1874 if (!m_styleManager->getColor256(val, lineColor))
1875 f << "colId=###" << val << ",";
1876 else if (!lineColor.isBlack())
1877 f << lineColor << ",";
1878 val=int(libwps::readU8(input));
1879 if (val!=1)
1880 {
1881 f << "style=" << val << ",";
1882 if (val==0) chart->m_floorStyle.m_lineWidth=0;
1883 }
1884 val=int(libwps::readU8(input));
1885 if (val)
1886 f << "width=" << val+1 << ",";
1887 f << "],";
1888 ascFile.addPos(pos);
1889 ascFile.addNote(f.str().c_str());
1890
1891 pos=input->tell();
1892 f.str("");
1893 f << "ChartFramesStyle-B:";
1894 input->seek(pos+12, librevenge::RVNG_SEEK_SET);
1895 ascFile.addDelimiter(input->tell(),'|');
1896 f << "plot[line3]=[";
1897 val=int(libwps::readU8(input));
1898 if (!m_styleManager->getColor256(val, lineColor))
1899 f << "colId=###" << val << ",";
1900 else if (!lineColor.isBlack())
1901 f << lineColor << ",";
1902 val=int(libwps::readU8(input));
1903 if (val!=1)
1904 {
1905 f << "style=" << val << ",";
1906 if (val==0) chart->m_wallStyle.m_lineWidth=0;
1907 }
1908 val=int(libwps::readU8(input));
1909 if (val)
1910 f << "width=" << val+1 << ",";
1911 f << "],";
1912 ascFile.addDelimiter(input->tell(),'|');
1913 input->seek(pos+24, librevenge::RVNG_SEEK_SET);
1914 ascFile.addPos(pos);
1915 ascFile.addNote(f.str().c_str());
1916
1917 pos=input->tell();
1918 f.str("");
1919 f << "ChartFramesStyle-C:";
1920 input->seek(pos+24, librevenge::RVNG_SEEK_SET);
1921 ascFile.addPos(pos);
1922 ascFile.addNote(f.str().c_str());
1923
1924 return true;
1925 }
1926
1927 ////////////////////////////////////////////////////////////
1928 // send data
1929 ////////////////////////////////////////////////////////////
convert(LotusParser::Link const & link,WKSChart::Position (& positions)[2]) const1930 bool LotusChart::convert(LotusParser::Link const &link, WKSChart::Position(&positions)[2]) const
1931 {
1932 for (int i=0; i<2; ++i)
1933 {
1934 auto const &c = link.m_cells[i];
1935 positions[i].m_pos=Vec2i(c[0], c[1]);
1936 positions[i].m_sheetName = m_mainParser.getSheetName(c[2]);
1937 }
1938 return positions[0].valid(positions[1]);
1939 }
1940
updateChart(LotusChartInternal::Chart & chart,int id)1941 void LotusChart::updateChart(LotusChartInternal::Chart &chart, int id)
1942 {
1943 int const vers=version();
1944 // .wk4 pie chart does no have legend
1945 if (chart.m_hasLegend && (vers<3 || chart.m_fileType!=4))
1946 {
1947 chart.getLegend().m_show=true;
1948 chart.getLegend().m_autoPosition=true;
1949 chart.getLegend().m_relativePosition=WPSBorder::RightBit;
1950 }
1951 else
1952 chart.getLegend().m_show=false;
1953 auto links=m_mainParser.getLinksList(id);
1954 std::map<std::string, LotusParser::Link const &> linkMap;
1955 for (auto const &l : links)
1956 linkMap.emplace(l.m_name, l);
1957
1958 // G[39-3e]: data series 0, 1, ...
1959 // G[40-45]: legend serie 0->5
1960 if (!chart.m_fileSerieStyles) // wks3 dos pc
1961 {
1962 // we must create the serie, if there have data, ...
1963 bool findSerie=false;
1964 for (int i=5; i>=0; --i)
1965 {
1966 std::string dataName("G");
1967 dataName+=char(0x39+i);
1968 WKSChart::Position ranges[2];
1969 if (linkMap.find(dataName)==linkMap.end() || !convert(linkMap.find(dataName)->second,ranges))
1970 continue;
1971 auto *serie=chart.getSerie(i, true);
1972 for (int r=0; r<2; ++r) serie->m_ranges[r]=ranges[r];
1973
1974 // check label
1975 std::string labelName("G");
1976 labelName+=char(0x40+i);
1977 if (linkMap.find(labelName)!=linkMap.end() && convert(linkMap.find(labelName)->second,ranges))
1978 {
1979 for (int r=0; r<2; ++r) serie->m_labelRanges[r]=ranges[r];
1980 }
1981 // now update the style
1982 auto const &format=chart.m_serieFormats[i];
1983 if (format.m_yAxis==2)
1984 serie->m_useSecondaryY=true;
1985 serie->m_type=chart.m_type;
1986 serie->m_style.m_lineWidth=1;
1987 if (chart.m_fileType==0 || chart.m_fileType==2 || chart.m_fileType==3 || chart.m_fileType==7)
1988 {
1989 switch (format.m_format)
1990 {
1991 case 0: // both
1992 if (chart.m_fileType==7 && !findSerie) // special case, mixed means last serie as line
1993 serie->m_type = WKSChart::Serie::S_Line;
1994 serie->m_pointType=WKSChart::Serie::P_Automatic;
1995 break;
1996 case 1: // lines
1997 serie->m_type = WKSChart::Serie::S_Line;
1998 break;
1999 case 2: // symbol
2000 serie->m_pointType=WKSChart::Serie::P_Automatic;
2001 serie->m_style.m_lineWidth=0;
2002 break;
2003 case 3:
2004 serie->m_style.m_lineWidth=0;
2005 break;
2006 case 4:
2007 serie->m_type = WKSChart::Serie::S_Area;
2008 break;
2009 default:
2010 break;
2011 }
2012 }
2013 findSerie=true;
2014 uint32_t const defColor[6] = {0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0x00ffff, 0xff00ff};
2015 WPSColor color=WPSColor(defColor[i]);
2016 if (format.m_color)
2017 m_styleManager->getColor256(format.m_color, color);
2018 // useme m_hash: H8->P2, H3->P3, H4->P4, H5->P12, H7->P6
2019 bool has0D=serie->m_pointType!=WKSChart::Serie::P_None;
2020 bool has1D=serie->is1DStyle();
2021 bool has2D=!serie->is1DStyle();
2022 if (has1D || format.m_hash==0)
2023 serie->m_style.m_lineColor=color;
2024 if (has0D || has2D)
2025 serie->m_style.setSurfaceColor(color);
2026 }
2027 }
2028 else
2029 {
2030 // G[47][22,27,2c,31,36,3b,40,45,4a,4f,54,59,5e]: data serie 6-18 (+1 label)
2031 // G[48][23,28,2d,32]: serie 19-22 (+1 label)
2032 for (auto it=chart.getIdSerieMap().begin(); it!=chart.getIdSerieMap().end(); ++it)
2033 {
2034 int sId=it->first;
2035 if (sId<0 || sId>22)
2036 {
2037 WPS_DEBUG_MSG(("LotusChart::updateChart: find unexpected id=%d\n", sId));
2038 continue;
2039 }
2040 std::string dataName("G"), labelName("G");
2041 if (sId<6)
2042 {
2043 dataName+=char(0x39+sId);
2044 labelName+=char(0x40+sId);
2045 }
2046 else if (sId<=18)
2047 {
2048 dataName+=char(0x47);
2049 dataName+=char(0x22+5*(sId-6));
2050 labelName+=char(0x47);
2051 labelName+=char(0x23+5*(sId-6));
2052 }
2053 else
2054 {
2055 dataName+=char(0x48);
2056 dataName+=char(0x23+5*(sId-19));
2057 labelName+=char(0x48);
2058 labelName+=char(0x24+5*(sId-19));
2059 }
2060 WKSChart::Position ranges[2];
2061 if (linkMap.find(dataName)==linkMap.end() || !convert(linkMap.find(dataName)->second,ranges))
2062 {
2063 if (vers>1)
2064 {
2065 WPS_DEBUG_MSG(("LotusChart::updateChart: can find data for serie %d in chart %d\n", sId, id));
2066 }
2067 continue;
2068 }
2069 auto *serie = chart.getSerie(sId, true);
2070 for (int i=0; i<2; ++i) serie->m_ranges[i]=ranges[i];
2071 if (linkMap.find(labelName)==linkMap.end() || !convert(linkMap.find(labelName)->second,ranges))
2072 continue;
2073 for (int i=0; i<2; ++i) serie->m_labelRanges[i]=ranges[i];
2074 }
2075 }
2076 for (int i=0; i<7; ++i)
2077 {
2078 // G[4f-51]: label axis x,y,ysecond
2079 // G[52-53]: title, subtile
2080 // G[54-55]: note1, note2
2081 std::string name("G");
2082 name+=char(0x4f+i);
2083 WKSChart::Position ranges[2];
2084 if (linkMap.find(name)==linkMap.end() || !convert(linkMap.find(name)->second,ranges))
2085 continue;
2086 if (i<3)
2087 chart.getAxis(i).m_titleRange=ranges[0];
2088 else
2089 {
2090 auto *zone=chart.getTextZone(i==3 ? WKSChart::TextZone::T_Title :
2091 i==4 ? WKSChart::TextZone::T_SubTitle : WKSChart::TextZone::T_Footer, true);
2092 zone->m_contentType=zone->C_Cell;
2093 zone->m_cell=ranges[0];
2094 }
2095 }
2096 // G[3f]: axis 0
2097 std::string name("G");
2098 name+=char(0x3f);
2099 WKSChart::Position ranges[2];
2100 if (linkMap.find(name)!=linkMap.end() && convert(linkMap.find(name)->second,ranges))
2101 {
2102 auto &axis = chart.getAxis(0);
2103 for (int r=0; r<2; ++r) axis.m_labelRanges[r]=ranges[r];
2104 }
2105 else if (chart.m_fileType==2)
2106 {
2107 // if chart is a scatter, the first serie can store the xaxis data...
2108 auto *serie = chart.getSerie(0, false);
2109 if (serie)
2110 {
2111 auto &axis=chart.getAxis(0);
2112 for (int i=0; i<2; ++i)
2113 {
2114 axis.m_labelRanges[i]=serie->m_ranges[i];
2115 serie->m_ranges[i]=WKSChart::Position();
2116 }
2117 }
2118 }
2119 // G[23-28] color series 0->5
2120 // G[2a-2f] hatch series 0->5
2121 // G[4c-4e]: unit axis x,y,ysecond
2122 }
2123
sendText(std::shared_ptr<WPSStream> stream,WPSEntry const & entry)2124 bool LotusChart::sendText(std::shared_ptr<WPSStream> stream, WPSEntry const &entry)
2125 {
2126 if (m_listener.get() == nullptr)
2127 {
2128 WPS_DEBUG_MSG(("LotusChart::sendText: I can not find the listener\n"));
2129 return false;
2130 }
2131 if (stream.get() == nullptr || !entry.valid())
2132 return true;
2133 RVNGInputStreamPtr &input = stream->m_input;
2134 input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2135 m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(input.get(), static_cast<unsigned long>(entry.length()), m_mainParser.getDefaultFontType()));
2136 return true;
2137 }
2138
sendCharts()2139 bool LotusChart::sendCharts()
2140 {
2141 if (m_listener.get() == nullptr)
2142 {
2143 WPS_DEBUG_MSG(("LotusChart::sendCharts: I can not find the listener\n"));
2144 return false;
2145 }
2146 Vec2i actPos(0,0);
2147 int actSquare=0;
2148 WPSGraphicStyle emptyStyle(WPSGraphicStyle::emptyStyle());
2149 for (auto &it : m_state->m_idChartMap)
2150 {
2151 if (!it.second || it.second->getIdSerieMap().empty()) continue;
2152 WPSPosition pos(Vec2f(float(512*actPos[0]),float(350*actPos[1])), Vec2f(512,350), librevenge::RVNG_POINT);
2153 pos.m_anchorTo = WPSPosition::Page;
2154 it.second->m_dimension=Vec2f(512,350); // set basic dimension
2155 sendChart(it.first, pos, emptyStyle);
2156 if (actPos[0]<actSquare)
2157 actPos[0]+=1;
2158 else if (actPos[1]<actSquare)
2159 {
2160 actPos[1]+=1;
2161 actPos[0]=actPos[1]==actSquare ? 0 : actSquare;
2162 }
2163 else
2164 actPos=Vec2i(++actSquare, 0);
2165 }
2166 return true;
2167 }
2168
sendChart(int cId,WPSPosition const & pos,WPSGraphicStyle const & style)2169 bool LotusChart::sendChart(int cId, WPSPosition const &pos, WPSGraphicStyle const &style)
2170 {
2171 if (m_listener.get() == nullptr)
2172 {
2173 WPS_DEBUG_MSG(("LotusChart::sendChart: I can not find the listener\n"));
2174 return false;
2175 }
2176 auto it=m_state->m_idChartMap.find(cId);
2177 if (it==m_state->m_idChartMap.end() || !it->second)
2178 {
2179 WPS_DEBUG_MSG(("LotusChart::sendChart: I can not find the chart with id=%d\n", cId));
2180 return false;
2181 }
2182 if ((it->second->m_dimension[0]<=0 || it->second->m_dimension[1]<=0) &&
2183 pos.size()[0]>0 && pos.size()[1]>0) // set basic dimension
2184 {
2185 float factor=WPSPosition::getScaleFactor(pos.unit(), librevenge::RVNG_POINT);
2186 it->second->m_dimension=factor*pos.size();
2187 }
2188 it->second->m_style=style;
2189 m_listener->insertChart(pos, *it->second);
2190 return true;
2191 }
2192
2193 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
2194