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