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 <stack>
29 
30 #include <librevenge-stream/librevenge-stream.h>
31 
32 #include "libwps_internal.h"
33 #include "libwps_tools_win.h"
34 
35 #include "WKSContentListener.h"
36 #include "WKSSubDocument.h"
37 
38 #include "WPSEntry.h"
39 #include "WPSFont.h"
40 #include "WPSGraphicShape.h"
41 #include "WPSGraphicStyle.h"
42 #include "WPSParagraph.h"
43 #include "WPSPosition.h"
44 #include "WPSStream.h"
45 
46 #include "Lotus.h"
47 #include "LotusStyleManager.h"
48 
49 #include "LotusGraph.h"
50 
51 namespace LotusGraphInternal
52 {
53 //! the graphic zone of a LotusGraph for 123 mac
54 struct ZoneMac
55 {
56 	//! the different type
57 	enum Type { Arc, Frame, Line, Rect, Unknown };
58 	//! constructor
ZoneMacLotusGraphInternal::ZoneMac59 	explicit ZoneMac(std::shared_ptr<WPSStream> const &stream)
60 		: m_type(Unknown)
61 		, m_subType(0)
62 		, m_stream(stream)
63 		, m_box()
64 		, m_ordering(0)
65 		, m_lineId(0)
66 		, m_graphicId(0)
67 		, m_surfaceId(0)
68 		, m_hasShadow(false)
69 		, m_chartId(0)
70 		, m_pictureEntry()
71 		, m_textBoxEntry()
72 		, m_extra("")
73 	{
74 		for (int &value : m_values) value=0;
75 	}
76 	//! returns a graphic shape corresponding to the main form (and the origin)
77 	bool getGraphicShape(WPSGraphicShape &shape, WPSPosition &pos) const;
78 	//! operator<<
operator <<(std::ostream & o,ZoneMac const & z)79 	friend std::ostream &operator<<(std::ostream &o, ZoneMac const &z)
80 	{
81 		switch (z.m_type)
82 		{
83 		case Arc:
84 			o << "arc,";
85 			break;
86 		case Frame:
87 			// subType is a small number 2, 3 ?
88 			o << "frame[" << z.m_subType << "],";
89 			break;
90 		case Line:
91 			o << "line,";
92 			break;
93 		case Rect:
94 			if (z.m_subType==1)
95 				o << "rect,";
96 			else if (z.m_subType==2)
97 				o << "rectOval,";
98 			else if (z.m_subType==3)
99 				o << "oval,";
100 			else
101 				o << "rect[#" << z.m_subType << "],";
102 			break;
103 		case Unknown:
104 		default:
105 			break;
106 		}
107 		o << z.m_box << ",";
108 		o << "order=" << z.m_ordering << ",";
109 		if (z.m_lineId)
110 			o << "L" << z.m_lineId << ",";
111 		if (z.m_surfaceId)
112 			o << "Co" << z.m_surfaceId << ",";
113 		if (z.m_graphicId)
114 			o << "G" << z.m_graphicId << ",";
115 		if (z.m_hasShadow)
116 			o << "shadow,";
117 		for (int i=0; i<4; ++i)
118 		{
119 			if (z.m_values[i])
120 				o << "val" << i << "=" << z.m_values[i] << ",";
121 		}
122 		o << z.m_extra << ",";
123 		return o;
124 	}
125 	//! the zone type
126 	Type m_type;
127 	//! the file modifier type
128 	int m_subType;
129 	//! the stream
130 	std::shared_ptr<WPSStream> m_stream;
131 	//! the bdbox
132 	WPSBox2i m_box;
133 	//! the ordering
134 	int m_ordering;
135 	//! the line style id
136 	int m_lineId;
137 	//! the graphic style id, used with rect shadow
138 	int m_graphicId;
139 	//! the surface color style id
140 	int m_surfaceId;
141 	//! a flag to know if we need to add shadow
142 	bool m_hasShadow;
143 	//! the chart id(for chart)
144 	int m_chartId;
145 	//! the picture entry
146 	WPSEntry m_pictureEntry;
147 	//! the text box entry
148 	WPSEntry m_textBoxEntry;
149 	//! unknown other value
150 	int m_values[4];
151 	//! extra data
152 	std::string m_extra;
153 };
154 
getGraphicShape(WPSGraphicShape & shape,WPSPosition & pos) const155 bool ZoneMac::getGraphicShape(WPSGraphicShape &shape, WPSPosition &pos) const
156 {
157 	pos=WPSPosition(Vec2f(m_box[0]),Vec2f(m_box.size()), librevenge::RVNG_POINT);
158 	pos.setRelativePosition(WPSPosition::Page);
159 	WPSBox2f box(Vec2f(0,0), Vec2f(m_box.size()));
160 	switch (m_type)
161 	{
162 	case Line:
163 	{
164 		// we need to recompute the bdbox
165 		int bounds[4]= {m_box[0][0],m_box[0][1],m_box[1][0],m_box[1][1]};
166 		for (int i=0; i<2; ++i)
167 		{
168 			if (bounds[i]<=bounds[i+2]) continue;
169 			bounds[i]=bounds[i+2];
170 			bounds[i+2]=m_box[0][i];
171 		}
172 		WPSBox2i realBox(Vec2i(bounds[0],bounds[1]),Vec2i(bounds[2],bounds[3]));
173 		pos=WPSPosition(Vec2f(realBox[0]),Vec2f(realBox.size()), librevenge::RVNG_POINT);
174 		pos.setRelativePosition(WPSPosition::Page);
175 		shape=WPSGraphicShape::line(Vec2f(m_box[0]-realBox[0]), Vec2f(m_box[1]-realBox[0]));
176 		return true;
177 	}
178 	case Rect:
179 		switch (m_subType)
180 		{
181 		case 2:
182 			shape=WPSGraphicShape::rectangle(box, Vec2f(5,5));
183 			return true;
184 		case 3:
185 			shape=WPSGraphicShape::circle(box);
186 			return true;
187 		default:
188 		case 1:
189 			shape=WPSGraphicShape::rectangle(box);
190 			return true;
191 		}
192 	case Frame:
193 		shape=WPSGraphicShape::rectangle(box);
194 		return true;
195 	case Arc:
196 		// changeme if the shape box if defined with different angle
197 		shape=WPSGraphicShape::arc(box, WPSBox2f(Vec2f(-box[1][0],0),Vec2f(box[1][0],2*box[1][1])), Vec2f(0,90));
198 		return true;
199 	case Unknown:
200 	default:
201 		break;
202 	}
203 	return false;
204 }
205 
206 //! the graphic zone of a LotusGraph : wk4
207 struct ZoneWK4
208 {
209 	//! the different type
210 	enum Type { Border, Chart, Group, Picture, Shape, TextBox, Unknown };
211 	//! constructor
ZoneWK4LotusGraphInternal::ZoneWK4212 	explicit ZoneWK4(std::shared_ptr<WPSStream> const &stream)
213 		: m_type(Unknown)
214 		, m_subType(-1)
215 		, m_id(-1)
216 		, m_cell(0,0)
217 		, m_cellPosition(0,0)
218 		, m_frameSize(0,0)
219 		, m_pictureDim()
220 		, m_pictureName()
221 		, m_shape()
222 		, m_graphicStyle(WPSGraphicStyle::emptyStyle())
223 		, m_hasShadow(false)
224 		, m_textEntry()
225 		, m_stream(stream)
226 	{
227 	}
228 	//! the zone type
229 	int m_type;
230 	//! the sub type
231 	int m_subType;
232 	//! the zone id
233 	int m_id;
234 	//! the cell
235 	Vec2i m_cell;
236 	//! the position in the cell
237 	Vec2f m_cellPosition;
238 	//! the frame dimension
239 	Vec2i m_frameSize;
240 	//! the picture dimension
241 	WPSBox2i m_pictureDim;
242 	//! the picture name
243 	std::string m_pictureName;
244 	//! the graphic shape
245 	WPSGraphicShape m_shape;
246 	//! the graphic style wk4
247 	WPSGraphicStyle m_graphicStyle;
248 	//! a flag to know if we need to add shadow
249 	bool m_hasShadow;
250 	//! the text entry (for textbox and button)
251 	WPSEntry m_textEntry;
252 	//! the stream
253 	std::shared_ptr<WPSStream> m_stream;
254 };
255 
256 //! the graphic zone of a LotusGraph for 123 pc
257 struct ZonePc
258 {
259 	//! the different type
260 	enum Type { Arc, Chart, Ellipse, FreeHand, Line, Picture, Polygon, Rect, Set, TextBox, Unknown };
261 	//! constructor
ZonePcLotusGraphInternal::ZonePc262 	explicit ZonePc(std::shared_ptr<WPSStream> const &stream)
263 		: m_type(Unknown)
264 		, m_isGroup(false)
265 		, m_groupLastPosition(0)
266 		, m_numPoints(0)
267 		, m_vertices()
268 		, m_isRoundRect(false)
269 		, m_stream(stream)
270 		, m_box()
271 		, m_translate(0,0)
272 		, m_rotate(0)
273 		, m_arrows(0)
274 		, m_textBoxEntry()
275 		, m_pictureData()
276 		, m_pictureHeaderRead(0)
277 		, m_isSent(false)
278 		, m_extra("")
279 	{
280 		for (int &i : m_graphicId) i=-1;
281 	}
282 	//! returns a graphic shape corresponding to the main form (and the origin)
283 	bool getGraphicShape(WPSGraphicShape &shape, WPSPosition &pos) const;
284 	//! returns a transformation corresponding to the shape
getTransformationLotusGraphInternal::ZonePc285 	WPSTransformation getTransformation() const
286 	{
287 		WPSTransformation res;
288 		if (m_rotate<0||m_rotate>0)
289 			res = WPSTransformation::rotation(-m_rotate, m_box.center());
290 		if (m_translate!=Vec2f(0,0))
291 			res = WPSTransformation::translation(m_translate) * res;
292 		return res;
293 	}
294 	//! operator<<
operator <<(std::ostream & o,ZonePc const & z)295 	friend std::ostream &operator<<(std::ostream &o, ZonePc const &z)
296 	{
297 		switch (z.m_type)
298 		{
299 		case Arc:
300 			o << "arc,";
301 			break;
302 		case Chart:
303 			o << "chart,";
304 			break;
305 		case Ellipse:
306 			o << "ellipse,";
307 			break;
308 		case FreeHand:
309 			o << "freeHand,";
310 			if (z.m_numPoints) o << "N=" << z.m_numPoints << ",";
311 			break;
312 		case Line:
313 			o << "line,";
314 			if (z.m_numPoints!=2) o << "N=" << z.m_numPoints << ",";
315 			break;
316 		case Picture:
317 			o << "picture,";
318 			break;
319 		case Polygon:
320 			o << "polygon,";
321 			if (z.m_numPoints) o << "N=" << z.m_numPoints << ",";
322 			break;
323 		case Rect:
324 			if (z.m_isRoundRect)
325 				o << "rect[round],";
326 			else
327 				o << "rect,";
328 			break;
329 		case Set:
330 			if (z.m_isGroup)
331 				o << "group,";
332 			else
333 				o << "set,";
334 			break;
335 		case TextBox:
336 			o << "textBox,";
337 			break;
338 		case Unknown:
339 		default:
340 			break;
341 		}
342 		o << "dim=" << z.m_box << ",";
343 		if (z.m_translate!=Vec2f(0,0))
344 			o << "translation=" << z.m_translate << ",";
345 		if (z.m_rotate<0||z.m_rotate>0)
346 			o << "rotation=" << z.m_rotate << ",";
347 		for (int i=0; i<2; ++i)
348 		{
349 			if (z.m_graphicId[i]<0) continue;
350 			o << (i==0 ? "style" : "shadow") << "=GS" << z.m_graphicId[i] << ",";
351 		}
352 		if (z.m_arrows&1)
353 			o << "arrows[beg],";
354 		if (z.m_arrows&2)
355 			o << "arrows[end],";
356 		o << z.m_extra << ",";
357 		return o;
358 	}
359 	//! the zone type
360 	Type m_type;
361 	//! true if the set is a group
362 	bool m_isGroup;
363 	//! the group last position
364 	size_t m_groupLastPosition;
365 	//! the number of points of a line or a polygon
366 	int m_numPoints;
367 	//! the list of points for a polygon
368 	std::vector<Vec2f> m_vertices;
369 	//! true if the rect is a round rect
370 	bool m_isRoundRect;
371 	//! the stream
372 	std::shared_ptr<WPSStream> m_stream;
373 	//! the bdbox
374 	WPSBox2f m_box;
375 	//! the translation
376 	Vec2f m_translate;
377 	//! the rotation
378 	float m_rotate;
379 	//! the graphic style id and the shadow style
380 	int m_graphicId[2];
381 	//! the line arrows
382 	int m_arrows;
383 	//! the text box entry
384 	WPSEntry m_textBoxEntry;
385 	//! the picture data
386 	librevenge::RVNGBinaryData m_pictureData;
387 	//! the number of read byte of the header
388 	int m_pictureHeaderRead;
389 	//! a flag to know if the zone has already be sent
390 	mutable bool m_isSent;
391 	//! extra data
392 	std::string m_extra;
393 };
394 
getGraphicShape(WPSGraphicShape & shape,WPSPosition & pos) const395 bool ZonePc::getGraphicShape(WPSGraphicShape &shape, WPSPosition &pos) const
396 {
397 	pos=WPSPosition(m_box[0],m_box.size(), librevenge::RVNG_POINT);
398 	pos.setRelativePosition(WPSPosition::Page);
399 	WPSBox2f box(Vec2f(0,0), m_box.size());
400 	switch (m_type)
401 	{
402 	case Line:
403 	{
404 		// we need to recompute the bdbox
405 		float bounds[4]= {m_box[0][0],m_box[0][1],m_box[1][0],m_box[1][1]};
406 		for (int i=0; i<2; ++i)
407 		{
408 			if (bounds[i]<=bounds[i+2]) continue;
409 			bounds[i]=bounds[i+2];
410 			bounds[i+2]=m_box[0][i];
411 		}
412 		WPSBox2f realBox(Vec2f(bounds[0],bounds[1]),Vec2f(bounds[2],bounds[3]));
413 		pos=WPSPosition(realBox[0],realBox.size(), librevenge::RVNG_POINT);
414 		pos.setRelativePosition(WPSPosition::Page);
415 		shape=WPSGraphicShape::line(m_box[0]-realBox[0], m_box[1]-realBox[0]);
416 		return true;
417 	}
418 	case Ellipse:
419 		shape=WPSGraphicShape::circle(box);
420 		return true;
421 	case Rect:
422 		if (m_isRoundRect)
423 			shape=WPSGraphicShape::rectangle(box, Vec2f(5,5));
424 		else
425 			shape=WPSGraphicShape::rectangle(box);
426 		return true;
427 	case Arc: // checkme only work if no flip
428 		shape=WPSGraphicShape::arc(box, WPSBox2f(Vec2f(-box[1][0],0),Vec2f(box[1][0],2*box[1][1])), Vec2f(0,90));
429 		return true;
430 	case FreeHand:
431 	case Polygon:
432 		if (m_vertices.empty())
433 		{
434 			WPS_DEBUG_MSG(("LotusGraphInternal::ZonePc::getGraphicShape: sorry, can not find the polygon vertices\n"));
435 			return false;
436 		}
437 		shape=WPSGraphicShape::polygon(box);
438 		shape.m_vertices=m_vertices;
439 		shape.m_vertices.push_back(m_vertices[0]);
440 		return true;
441 	case Chart:
442 	case Set:
443 	case TextBox:
444 		return true;
445 	case Picture:
446 	case Unknown:
447 	default:
448 		break;
449 	}
450 	static bool first=true;
451 	if (first)
452 	{
453 		first=false;
454 		WPS_DEBUG_MSG(("LotusGraphInternal::ZonePc::getGraphicShape: sorry, sending some graph types is not implemented\n"));
455 	}
456 	return false;
457 }
458 //! a list of ZonePc of a LotusGraph for 123 pc
459 struct ZonePcList
460 {
461 	//! constructor
ZonePcListLotusGraphInternal::ZonePcList462 	ZonePcList()
463 		: m_zones()
464 		, m_groupBeginStack()
465 	{
466 	}
467 	//! returns true if the number of graphic zone is empty
emptyLotusGraphInternal::ZonePcList468 	bool empty() const
469 	{
470 		for (auto const &zone : m_zones)
471 		{
472 			if (zone)
473 				return false;
474 		}
475 		return true;
476 	}
477 	//! the list of zones
478 	std::vector<std::shared_ptr<ZonePc> > m_zones;
479 	//! the stack indicating the beginning of each group
480 	std::stack<size_t> m_groupBeginStack;
481 };
482 
483 //! the state of LotusGraph
484 struct State
485 {
486 	//! constructor
StateLotusGraphInternal::State487 	State()
488 		:  m_version(-1)
489 		, m_actualSheetId(-1)
490 		, m_sheetIdZoneMacMap()
491 		, m_actualZoneMac()
492 		, m_sheetIdZoneWK4Map()
493 		, m_actualZoneWK4()
494 		, m_sheetIdZonePcListMap()
495 		, m_actualZonePc()
496 		, m_zIdToSheetIdMap()
497 		, m_nameToChartIdMap()
498 	{
499 	}
500 
501 	//! the file version
502 	int m_version;
503 	//! the actual sheet id
504 	int m_actualSheetId;
505 	//! a map sheetid to zone
506 	std::multimap<int, std::shared_ptr<ZoneMac> > m_sheetIdZoneMacMap;
507 	//! a pointer to the actual zone
508 	std::shared_ptr<ZoneMac> m_actualZoneMac;
509 	//! a map sheetid to zone
510 	std::multimap<int, std::shared_ptr<ZoneWK4> > m_sheetIdZoneWK4Map;
511 	//! a pointer to the actual zone
512 	std::shared_ptr<ZoneWK4> m_actualZoneWK4;
513 	//! a map sheetid to zone
514 	std::map<int, ZonePcList> m_sheetIdZonePcListMap;
515 	//! a pointer to the actual zone
516 	std::shared_ptr<ZonePc> m_actualZonePc;
517 	//! a map sheet zone id to final sheet id map
518 	std::map<int,int> m_zIdToSheetIdMap;
519 	//! a map chart name to id chart map
520 	std::map<std::string,int> m_nameToChartIdMap;
521 };
522 
523 //! Internal: the subdocument of a LotusGraphc
524 class SubDocument final : public WKSSubDocument
525 {
526 public:
527 	//! constructor for a text entry
SubDocument(std::shared_ptr<WPSStream> const & stream,LotusGraph & graphParser,WPSEntry & entry,int version)528 	SubDocument(std::shared_ptr<WPSStream> const &stream, LotusGraph &graphParser, WPSEntry &entry, int version)
529 		: WKSSubDocument(RVNGInputStreamPtr(), &graphParser.m_mainParser)
530 		, m_stream(stream)
531 		, m_graphParser(graphParser)
532 		, m_entry(entry)
533 		, m_version(version) {}
534 	//! destructor
~SubDocument()535 	~SubDocument() final {}
536 
537 	//! operator==
operator ==(std::shared_ptr<WPSSubDocument> const & doc) const538 	bool operator==(std::shared_ptr<WPSSubDocument> const &doc) const final
539 	{
540 		if (!doc || !WKSSubDocument::operator==(doc))
541 			return false;
542 		auto const *sDoc = dynamic_cast<SubDocument const *>(doc.get());
543 		if (!sDoc) return false;
544 		if (&m_graphParser != &sDoc->m_graphParser) return false;
545 		if (m_stream.get() != sDoc->m_stream.get()) return false;
546 		if (m_version != sDoc->m_version) return false;
547 		return m_entry == sDoc->m_entry;
548 	}
549 
550 	//! the parser function
551 	void parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType subDocumentType) final;
552 	//! the stream
553 	std::shared_ptr<WPSStream> m_stream;
554 	//! the graph parser
555 	LotusGraph &m_graphParser;
556 	//! a flag to known if we need to send the entry or the footer
557 	WPSEntry m_entry;
558 	//! the textbox version
559 	int m_version;
560 };
561 
parse(std::shared_ptr<WKSContentListener> & listener,libwps::SubDocumentType)562 void SubDocument::parse(std::shared_ptr<WKSContentListener> &listener, libwps::SubDocumentType)
563 {
564 	if (!listener.get())
565 	{
566 		WPS_DEBUG_MSG(("LotusGraphInternal::SubDocument::parse: no listener\n"));
567 		return;
568 	}
569 	if (!dynamic_cast<WKSContentListener *>(listener.get()))
570 	{
571 		WPS_DEBUG_MSG(("LotusGraphInternal::SubDocument::parse: bad listener\n"));
572 		return;
573 	}
574 	if (m_version==0)
575 		m_graphParser.sendTextBox(m_stream, m_entry);
576 	else if (m_version==1 || m_version==2)
577 		m_graphParser.sendTextBoxWK4(m_stream, m_entry, m_version==2);
578 	else
579 	{
580 		WPS_DEBUG_MSG(("LotusGraphInternal::SubDocument::parse: unknown version=%d\n", m_version));
581 		return;
582 	}
583 }
584 
585 }
586 
587 // constructor, destructor
LotusGraph(LotusParser & parser)588 LotusGraph::LotusGraph(LotusParser &parser)
589 	: m_listener()
590 	, m_mainParser(parser)
591 	, m_styleManager(parser.m_styleManager)
592 	, m_state(new LotusGraphInternal::State)
593 {
594 }
595 
~LotusGraph()596 LotusGraph::~LotusGraph()
597 {
598 }
599 
cleanState()600 void LotusGraph::cleanState()
601 {
602 	m_state.reset(new LotusGraphInternal::State);
603 }
604 
updateState(std::map<int,int> const & zIdToSheetIdMap,std::map<std::string,int> const & nameToChartIdMap)605 void LotusGraph::updateState(std::map<int,int> const &zIdToSheetIdMap,
606                              std::map<std::string,int> const &nameToChartIdMap)
607 {
608 	m_state->m_zIdToSheetIdMap=zIdToSheetIdMap;
609 	m_state->m_nameToChartIdMap=nameToChartIdMap;
610 }
611 
version() const612 int LotusGraph::version() const
613 {
614 	if (m_state->m_version<0)
615 		m_state->m_version=m_mainParser.version();
616 	return m_state->m_version;
617 }
618 
hasGraphics(int sheetId) const619 bool LotusGraph::hasGraphics(int sheetId) const
620 {
621 	if (m_state->m_sheetIdZoneMacMap.find(sheetId)!=m_state->m_sheetIdZoneMacMap.end() ||
622 	        m_state->m_sheetIdZoneWK4Map.find(sheetId)!=m_state->m_sheetIdZoneWK4Map.end())
623 		return true;
624 	if (m_state->m_zIdToSheetIdMap.find(sheetId)!=m_state->m_zIdToSheetIdMap.end())
625 	{
626 		int finalId=m_state->m_zIdToSheetIdMap.find(sheetId)->second;
627 		if (m_state->m_sheetIdZonePcListMap.find(finalId)!=m_state->m_sheetIdZonePcListMap.end()
628 		        && !m_state->m_sheetIdZonePcListMap.find(finalId)->second.empty())
629 			return true;
630 	}
631 	return false;
632 }
633 
setChartId(int chartId)634 bool LotusGraph::setChartId(int chartId)
635 {
636 	auto zone=m_state->m_actualZoneMac;
637 	if (!zone || zone->m_type != LotusGraphInternal::ZoneMac::Frame)
638 	{
639 		WPS_DEBUG_MSG(("LotusGraph::setChartId: Oops can not find the parent frame\n"));
640 		return false;
641 	}
642 	zone->m_chartId = chartId;
643 	m_state->m_actualZoneMac.reset();
644 	return true;
645 }
646 
647 ////////////////////////////////////////////////////////////
648 // low level
649 
650 ////////////////////////////////////////////////////////////
651 // zones
652 ////////////////////////////////////////////////////////////
readZoneBegin(std::shared_ptr<WPSStream> stream,long endPos)653 bool LotusGraph::readZoneBegin(std::shared_ptr<WPSStream> stream, long endPos)
654 {
655 	if (!stream) return false;
656 	RVNGInputStreamPtr &input = stream->m_input;
657 	libwps::DebugFile &ascFile=stream->m_ascii;
658 	libwps::DebugStream f;
659 	f << "Entries(GraphBegin):";
660 	long pos = input->tell();
661 	if (endPos-pos!=4)
662 	{
663 		WPS_DEBUG_MSG(("LotusParser::readZoneBegin: the zone seems bad\n"));
664 		f << "###";
665 		ascFile.addPos(pos-6);
666 		ascFile.addNote(f.str().c_str());
667 
668 		return true;
669 	}
670 	m_state->m_actualSheetId=int(libwps::readU8(input));
671 	f << "sheet[id]=" << m_state->m_actualSheetId << ",";
672 	for (int i=0; i<3; ++i)   // f0=1
673 	{
674 		auto val=int(libwps::readU8(input));
675 		if (val)
676 			f << "f" << i << "=" << val << ",";
677 	}
678 	m_state->m_actualZoneMac.reset();
679 	ascFile.addPos(pos-6);
680 	ascFile.addNote(f.str().c_str());
681 	return true;
682 
683 }
684 
readZoneData(std::shared_ptr<WPSStream> stream,long endPos,int type)685 bool LotusGraph::readZoneData(std::shared_ptr<WPSStream> stream, long endPos, int type)
686 {
687 	if (!stream) return false;
688 	RVNGInputStreamPtr &input = stream->m_input;
689 	libwps::DebugFile &ascFile=stream->m_ascii;
690 	libwps::DebugStream f;
691 	long pos = input->tell();
692 	long sz=endPos-pos;
693 
694 	std::shared_ptr<LotusGraphInternal::ZoneMac> zone(new LotusGraphInternal::ZoneMac(stream));
695 	m_state->m_actualZoneMac=zone;
696 
697 	switch (type)
698 	{
699 	case 0x2332:
700 		zone->m_type=LotusGraphInternal::ZoneMac::Line;
701 		break;
702 	case 0x2346:
703 		zone->m_type=LotusGraphInternal::ZoneMac::Rect;
704 		break;
705 	case 0x2350:
706 		zone->m_type=LotusGraphInternal::ZoneMac::Arc;
707 		break;
708 	case 0x2352:
709 		zone->m_type=LotusGraphInternal::ZoneMac::Frame;
710 		zone->m_hasShadow=true;
711 		break;
712 	case 0x23f0:
713 		zone->m_type=LotusGraphInternal::ZoneMac::Frame;
714 		break;
715 	default:
716 		WPS_DEBUG_MSG(("LotusGraph::readZoneData: find unexpected graph data\n"));
717 		f << "###";
718 	}
719 	if (sz<24)
720 	{
721 		WPS_DEBUG_MSG(("LotusGraph::readZoneData: the zone seems too short\n"));
722 		f << "Entries(GraphMac):" << *zone << "###";
723 		ascFile.addPos(pos-6);
724 		ascFile.addNote(f.str().c_str());
725 		return true;
726 	}
727 	zone->m_ordering=int(libwps::readU8(input));
728 	for (int i=0; i<4; ++i)   // always 0?
729 	{
730 		auto val=int(libwps::read8(input));
731 		if (val)
732 			f << "f" << i << "=" << val << ",";
733 	}
734 	int dim[4];
735 	for (int i=0; i<4; ++i)   // dim3[high]=0|100
736 	{
737 		dim[i]=int(libwps::read16(input));
738 		if (i==3)
739 			break;
740 		auto val=int(libwps::read16(input));
741 		if (val) f << "dim" << i << "[high]=" << std::hex << val << std::dec << ",";
742 	}
743 	zone->m_box=WPSBox2i(Vec2i(dim[1],dim[0]),Vec2i(dim[3],dim[2]));
744 	auto val=int(libwps::read8(input));
745 	if (val) // always 0
746 		f << "f4=" << val << ",";
747 	int fl;
748 	switch (zone->m_type)
749 	{
750 	case LotusGraphInternal::ZoneMac::Line:
751 		val=int(libwps::readU8(input));
752 		fl=int(libwps::readU8(input));
753 		if (val)
754 		{
755 			if (fl!=0x10)
756 				f << "#line[fl]=" << std::hex << fl << std::dec << ",";
757 			zone->m_lineId=val;
758 		}
759 		val=int(libwps::readU8(input)); // always 1?
760 		if (val!=1)
761 			f << "g0=" << val << ",";
762 		// the arrows &1 means end, &2 means begin
763 		zone->m_values[0]=int(libwps::readU8(input));
764 		if (sz<26)
765 		{
766 			WPS_DEBUG_MSG(("LotusGraph::readZoneData: the line zone seems too short\n"));
767 			f << "###sz,";
768 			break;
769 		}
770 		for (int i=0; i<2; ++i)   // always g1=0, g2=3 ?
771 		{
772 			val=int(libwps::readU8(input));
773 			if (val!=3*i)
774 				f << "g" << i+1 << "=" << val << ",";
775 		}
776 		break;
777 	case LotusGraphInternal::ZoneMac::Rect:
778 		val=int(libwps::readU8(input)); // always 1?
779 		if (val!=1)
780 			f << "g0=" << val << ",";
781 		zone->m_subType=int(libwps::readU8(input));
782 		if (sz<28)
783 		{
784 			WPS_DEBUG_MSG(("LotusGraph::readZoneData: the rect zone seems too short\n"));
785 			f << "###sz,";
786 			break;
787 		}
788 		for (int i=0; i<2; ++i)
789 		{
790 			val=int(libwps::readU8(input));
791 			fl=int(libwps::readU8(input));
792 			if (!val) continue;
793 			if (i==0)
794 			{
795 				if (fl!=0x10)
796 					f << "#line[fl]=" << std::hex << fl << std::dec << ",";
797 				zone->m_lineId=val;
798 				continue;
799 			}
800 			if (fl!=0x20)
801 				f << "#surface[fl]=" << std::hex << fl << std::dec << ",";
802 			zone->m_surfaceId=val;
803 		}
804 		val=int(libwps::read16(input)); // always 3?
805 		if (val!=3)
806 			f << "g1=" << val << ",";
807 		break;
808 	case LotusGraphInternal::ZoneMac::Frame:
809 		val=int(libwps::readU8(input)); // always 1?
810 		if (val!=1)
811 			f << "g0=" << val << ",";
812 		// small value 1-4
813 		zone->m_subType=int(libwps::readU8(input));
814 		val=int(libwps::readU8(input));
815 		fl=int(libwps::readU8(input));
816 		if (!val) break;;
817 		if (fl!=0x40)
818 			f << "#graphic[fl]=" << std::hex << fl << std::dec << ",";
819 		zone->m_graphicId=val;
820 		// can be followed by 000000000100 : some way to determine the content ?
821 		break;
822 	case LotusGraphInternal::ZoneMac::Arc:
823 		val=int(libwps::readU8(input)); // always 1?
824 		if (val!=1)
825 			f << "g0=" << val << ",";
826 		// always 3
827 		zone->m_subType=int(libwps::readU8(input));
828 		val=int(libwps::readU8(input));
829 		fl=int(libwps::readU8(input));
830 		if (val)
831 		{
832 			if (fl!=0x10)
833 				f << "#line[fl]=" << std::hex << fl << std::dec << ",";
834 			zone->m_lineId=val;
835 		}
836 		if (sz<26)
837 		{
838 			WPS_DEBUG_MSG(("LotusGraph::readZoneData: the arc zone seems too short\n"));
839 			f << "###sz,";
840 			break;
841 		}
842 		val=int(libwps::read16(input)); // always 0? maybe the angle
843 		if (val)
844 			f << "g1=" << val << ",";
845 		break;
846 	case LotusGraphInternal::ZoneMac::Unknown:
847 	default:
848 		break;
849 	}
850 
851 	if (m_state->m_actualSheetId<0)
852 	{
853 		WPS_DEBUG_MSG(("LotusGraph::readZoneData: oops no sheet zone is opened\n"));
854 		f << "###sheetId,";
855 	}
856 	else
857 		m_state->m_sheetIdZoneMacMap.insert(std::multimap<int, std::shared_ptr<LotusGraphInternal::ZoneMac> >::value_type
858 		                                    (m_state->m_actualSheetId, zone));
859 	zone->m_extra=f.str();
860 	f.str("");
861 	f << "Entries(GraphMac):" << *zone;
862 	ascFile.addPos(pos-6);
863 	ascFile.addNote(f.str().c_str());
864 	return true;
865 }
866 
readTextBoxData(std::shared_ptr<WPSStream> stream,long endPos)867 bool LotusGraph::readTextBoxData(std::shared_ptr<WPSStream> stream, long endPos)
868 {
869 	if (!stream) return false;
870 	RVNGInputStreamPtr &input = stream->m_input;
871 	libwps::DebugFile &ascFile=stream->m_ascii;
872 	libwps::DebugStream f;
873 	long pos = input->tell();
874 	long sz=endPos-pos;
875 	f << "Entries(GraphTextBox):";
876 	if (sz<1)
877 	{
878 		WPS_DEBUG_MSG(("LotusGraph::readTextBoxData: Oops the zone seems too short\n"));
879 		f << "###";
880 		ascFile.addPos(pos-6);
881 		ascFile.addNote(f.str().c_str());
882 		return true;
883 	}
884 
885 	if (!m_state->m_actualZoneMac || m_state->m_actualZoneMac->m_type != LotusGraphInternal::ZoneMac::Frame)
886 	{
887 		WPS_DEBUG_MSG(("LotusGraph::readTextBoxData: Oops can not find the parent frame\n"));
888 	}
889 	else
890 	{
891 		m_state->m_actualZoneMac->m_textBoxEntry.setBegin(input->tell());
892 		m_state->m_actualZoneMac->m_textBoxEntry.setEnd(endPos);
893 		m_state->m_actualZoneMac.reset();
894 	}
895 
896 	m_state->m_actualZoneMac.reset();
897 	ascFile.addPos(pos-6);
898 	ascFile.addNote(f.str().c_str());
899 	input->seek(endPos, librevenge::RVNG_SEEK_SET);
900 	return true;
901 }
902 
readPictureDefinition(std::shared_ptr<WPSStream> stream,long endPos)903 bool LotusGraph::readPictureDefinition(std::shared_ptr<WPSStream> stream, long endPos)
904 {
905 	if (!stream) return false;
906 	RVNGInputStreamPtr &input = stream->m_input;
907 	libwps::DebugFile &ascFile=stream->m_ascii;
908 	libwps::DebugStream f;
909 	long pos = input->tell();
910 	long sz=endPos-pos;
911 
912 	f << "Entries(PictDef):";
913 	if (sz!=13)
914 	{
915 		WPS_DEBUG_MSG(("LotusGraph::readPictureDefinition: the picture def seems bad\n"));
916 		f << "###";
917 		ascFile.addPos(pos-6);
918 		ascFile.addNote(f.str().c_str());
919 		return true;
920 	}
921 	if (!m_state->m_actualZoneMac || m_state->m_actualZoneMac->m_type != LotusGraphInternal::ZoneMac::Frame)
922 	{
923 		WPS_DEBUG_MSG(("LotusGraph::readPictureDefinition: Oops can not find the parent frame\n"));
924 	}
925 	auto val=int(libwps::readU8(input)); // always 0?
926 	if (val)
927 		f << "f0=" << val << ",";
928 	int dim[2];
929 	dim[0]=int(libwps::readU16(input));
930 	for (int i=0; i<2; ++i)
931 	{
932 		val=int(libwps::readU8(input));
933 		if (val)
934 			f << "f" << i+1 << "=" << val << ",";
935 	}
936 	dim[1]=int(libwps::readU16(input));
937 	f << "dim=" << Vec2i(dim[0], dim[1]) << ",";
938 	val=int(libwps::readU8(input));
939 	if (val)
940 		f << "f3=" << val << ",";
941 	auto pictSz=int(libwps::readU16(input)); // maybe 32bits
942 	f << "pict[sz]=" << std::hex << pictSz << std::dec << ",";
943 	for (int i=0; i<3; ++i)   // always 0,0,1
944 	{
945 		val=int(libwps::readU8(input));
946 		if (val)
947 			f << "g" << i << "=" << val << ",";
948 	}
949 	ascFile.addPos(pos-6);
950 	ascFile.addNote(f.str().c_str());
951 	return true;
952 }
953 
readPictureData(std::shared_ptr<WPSStream> stream,long endPos)954 bool LotusGraph::readPictureData(std::shared_ptr<WPSStream> stream, long endPos)
955 {
956 	if (!stream) return false;
957 	RVNGInputStreamPtr &input = stream->m_input;
958 	libwps::DebugFile &ascFile=stream->m_ascii;
959 	libwps::DebugStream f;
960 	long pos = input->tell();
961 	long sz=endPos-pos;
962 
963 	f << "Entries(PictData):";
964 	if (sz<=1)
965 	{
966 		WPS_DEBUG_MSG(("LotusGraph::readPictureData: the picture def seems bad\n"));
967 		f << "###";
968 		ascFile.addPos(pos-6);
969 		ascFile.addNote(f.str().c_str());
970 		return true;
971 	}
972 	auto val=int(libwps::readU8(input)); // always 1?
973 	if (val!=1)
974 		f << "type?=" << val << ",";
975 	if (!m_state->m_actualZoneMac || m_state->m_actualZoneMac->m_type != LotusGraphInternal::ZoneMac::Frame)
976 	{
977 		WPS_DEBUG_MSG(("LotusGraph::readPictureData: Oops can not find the parent frame\n"));
978 	}
979 	else
980 	{
981 		m_state->m_actualZoneMac->m_pictureEntry.setBegin(input->tell());
982 		m_state->m_actualZoneMac->m_pictureEntry.setEnd(endPos);
983 		m_state->m_actualZoneMac.reset();
984 	}
985 #ifdef DEBUG_WITH_FILES
986 	ascFile.skipZone(input->tell(), endPos-1);
987 	librevenge::RVNGBinaryData data;
988 	if (!libwps::readData(input, static_cast<unsigned long>(endPos-input->tell()), data))
989 		f << "###";
990 	else
991 	{
992 		std::stringstream s;
993 		static int fileId=0;
994 		s << "Pict" << ++fileId << ".pct";
995 		libwps::Debug::dumpFile(data, s.str().c_str());
996 	}
997 #endif
998 
999 	ascFile.addPos(pos-6);
1000 	ascFile.addNote(f.str().c_str());
1001 	return true;
1002 }
1003 
1004 ////////////////////////////////////////////////////////////
1005 // send data
1006 ////////////////////////////////////////////////////////////
sendPicture(LotusGraphInternal::ZoneMac const & zone)1007 void LotusGraph::sendPicture(LotusGraphInternal::ZoneMac const &zone)
1008 {
1009 	if (!m_listener || !zone.m_stream || !zone.m_stream->m_input || !zone.m_pictureEntry.valid())
1010 	{
1011 		WPS_DEBUG_MSG(("LotusGraph::sendPicture: I can not find the listener/picture entry\n"));
1012 		return;
1013 	}
1014 	RVNGInputStreamPtr input=zone.m_stream->m_input;
1015 	librevenge::RVNGBinaryData data;
1016 	input->seek(zone.m_pictureEntry.begin(), librevenge::RVNG_SEEK_SET);
1017 	if (!libwps::readData(input, static_cast<unsigned long>(zone.m_pictureEntry.length()), data))
1018 	{
1019 		WPS_DEBUG_MSG(("LotusGraph::sendPicture: I can not find the picture\n"));
1020 		return;
1021 	}
1022 	WPSGraphicShape shape;
1023 	WPSPosition pos;
1024 	if (!zone.getGraphicShape(shape, pos))
1025 		return;
1026 	WPSGraphicStyle style;
1027 	if (zone.m_graphicId)
1028 		m_styleManager->updateGraphicStyle(zone.m_graphicId, style);
1029 	m_listener->insertPicture(pos, data, "image/pict", style);
1030 }
1031 
sendTextBox(std::shared_ptr<WPSStream> stream,WPSEntry const & entry)1032 void LotusGraph::sendTextBox(std::shared_ptr<WPSStream> stream, WPSEntry const &entry)
1033 {
1034 	if (!stream || !m_listener || entry.length()<1)
1035 	{
1036 		WPS_DEBUG_MSG(("LotusGraph::sendTextBox: I can not find the listener/textbox entry\n"));
1037 		return;
1038 	}
1039 	RVNGInputStreamPtr input=stream->m_input;
1040 	libwps::DebugFile &ascFile=stream->m_ascii;
1041 	libwps::DebugStream f;
1042 	long pos = entry.begin();
1043 	long sz=entry.length();
1044 	f << "GraphTextBox[data]:";
1045 	input->seek(pos, librevenge::RVNG_SEEK_SET);
1046 	auto val=int(libwps::readU8(input)); // always 1?
1047 	if (val!=1) f << "f0=" << val << ",";
1048 	auto fontType = m_mainParser.getDefaultFontType();
1049 	WPSFont font=WPSFont::getDefault();
1050 	m_listener->setFont(font);
1051 	bool actualFlags[7]= {false, false, false, false, false, false, false };
1052 	std::string text;
1053 	for (long i=1; i<=sz; ++i)
1054 	{
1055 		auto c=i+1==sz ? '\0' : char(libwps::readU8(input));
1056 		if ((c==0 || c==0xe || c==0xf) && !text.empty())
1057 		{
1058 			m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
1059 			text.clear();
1060 		}
1061 		if (c==0)
1062 		{
1063 			if (int(i+2)<int(sz)) // g++ v7 warms if we test i+2<sz :-~
1064 			{
1065 				WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find a 0 char\n"));
1066 				f << "[###0]";
1067 			}
1068 			continue;
1069 		}
1070 		if (c!=0xe && c!=0xf)
1071 		{
1072 			f << c;
1073 			text.push_back(c);
1074 			continue;
1075 		}
1076 		if (i+1>=sz)
1077 		{
1078 			WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find modifier in last pos\n"));
1079 			f << "[###" << int(c) << "]";
1080 		}
1081 		auto mod=int(libwps::readU8(input));
1082 		++i;
1083 		if (c==0xf)
1084 		{
1085 			if (mod==45)
1086 			{
1087 				f << "[break]";
1088 				m_listener->insertEOL();
1089 			}
1090 			else
1091 			{
1092 				WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find unknown modifier f\n"));
1093 				f << "[###f:" << mod << "]";
1094 			}
1095 			continue;
1096 		}
1097 		int szParam=(mod==0x80) ? 4 : (mod>=0x40 && mod<=0x44) ? 2 : 0;
1098 		if (i+1+2*szParam>=sz)
1099 		{
1100 			WPS_DEBUG_MSG(("LotusGraph::sendTextBox: the param size seems bad\n"));
1101 			f << "[##e:" << std::hex << mod << std::dec << "]";
1102 			continue;
1103 		}
1104 		int param=0;
1105 		long actPos=input->tell();
1106 		bool ok=true;
1107 		for (int d=0; d<szParam; ++d)
1108 		{
1109 			auto mod1=int(libwps::readU8(input));
1110 			val=int(libwps::readU8(input));
1111 			static int const decal[]= {1,0,3,2};
1112 			if (mod1==0xe && (val>='0'&&val<='9'))
1113 			{
1114 				param += (val-'0')<<(4*decal[d]);
1115 				continue;
1116 			}
1117 			else if (mod1==0xe && (val>='A'&&val<='F'))
1118 			{
1119 				param += (val-'A'+10)<<(4*decal[d]);
1120 				continue;
1121 			}
1122 			WPS_DEBUG_MSG(("LotusGraph::sendTextBox: something when bad when reading param\n"));
1123 			f << "[##e:" << std::hex << mod << ":" << param << std::dec << "]";
1124 			ok=false;
1125 		}
1126 		if (!ok)
1127 		{
1128 			input->seek(actPos, librevenge::RVNG_SEEK_SET);
1129 			continue;
1130 		}
1131 		i+=2*szParam;
1132 		switch (mod)
1133 		{
1134 		case 1:
1135 		case 2:
1136 		case 3:
1137 		case 4:
1138 		case 5:
1139 		case 6:
1140 		case 7:
1141 		{
1142 			bool newFlag=actualFlags[mod-1]=!actualFlags[mod-1];
1143 			static char const *wh[]= {"b", "it", "outline", "underline", "shadow", "condensed", "extended"};
1144 			f << "[";
1145 			if (!newFlag) f << "/";
1146 			f << wh[mod-1] << "]";
1147 			if (mod<=5)
1148 			{
1149 				static uint32_t const attrib[]= { WPS_BOLD_BIT, WPS_ITALICS_BIT, WPS_UNDERLINE_BIT, WPS_OUTLINE_BIT, WPS_SHADOW_BIT };
1150 				if (newFlag)
1151 					font.m_attributes |= attrib[mod-1];
1152 				else
1153 					font.m_attributes &= ~attrib[mod-1];
1154 			}
1155 			else
1156 			{
1157 				font.m_spacing=0;
1158 				if (actualFlags[5]) font.m_spacing-=2;
1159 				if (actualFlags[6]) font.m_spacing+=2;
1160 			}
1161 			m_listener->setFont(font);
1162 			break;
1163 		}
1164 		case 0x40:
1165 		{
1166 			WPSFont newFont;
1167 			f << "[FN" << param<< "]";
1168 			if (m_mainParser.getFont(param, newFont, fontType))
1169 			{
1170 				font.m_name=newFont.m_name;
1171 				m_listener->setFont(font);
1172 			}
1173 			else
1174 				f << "###";
1175 			break;
1176 		}
1177 		case 0x41:
1178 		{
1179 			f << "[color=" << param << "]";
1180 			WPSColor color;
1181 			if (m_styleManager->getColor256(param, color))
1182 			{
1183 				font.m_color=color;
1184 				m_listener->setFont(font);
1185 			}
1186 			else
1187 				f << "###";
1188 			break;
1189 		}
1190 		case 0x44:
1191 		{
1192 			WPSParagraph para;
1193 			switch (param)
1194 			{
1195 			case 1:
1196 				f << "align[left]";
1197 				para.m_justify=libwps::JustificationLeft;
1198 				break;
1199 			case 2:
1200 				f << "align[right]";
1201 				para.m_justify=libwps::JustificationRight;
1202 				break;
1203 			case 3:
1204 				f << "align[center]";
1205 				para.m_justify=libwps::JustificationCenter;
1206 				break;
1207 			default:
1208 				f << "#align=" << param << ",";
1209 				break;
1210 			};
1211 			m_listener->setParagraph(para);
1212 			break;
1213 		}
1214 		case 0x80:
1215 		{
1216 			f << "[fSz=" << param/32. << "]";
1217 			font.m_size=param/32.;
1218 			m_listener->setFont(font);
1219 			break;
1220 		}
1221 		default:
1222 			WPS_DEBUG_MSG(("LotusGraph::sendTextBox: Oops find unknown modifier e\n"));
1223 			f << "[##e:" << std::hex << mod << "=" << param << std::dec << "]";
1224 			break;
1225 		}
1226 	}
1227 	ascFile.addPos(pos);
1228 	ascFile.addNote(f.str().c_str());
1229 }
1230 
sendGraphics(int sheetId)1231 void LotusGraph::sendGraphics(int sheetId)
1232 {
1233 	if (!m_listener)
1234 	{
1235 		WPS_DEBUG_MSG(("LotusGraph::sendGraphics: I can not find the listener\n"));
1236 		return;
1237 	}
1238 	auto it=m_state->m_sheetIdZoneMacMap.lower_bound(sheetId);
1239 	while (it!=m_state->m_sheetIdZoneMacMap.end() && it->first==sheetId)
1240 	{
1241 		auto zone=(it++)->second;
1242 		if (!zone) continue;
1243 		if (zone->m_pictureEntry.valid())
1244 		{
1245 			sendPicture(*zone);
1246 			continue;
1247 		}
1248 		WPSGraphicShape shape;
1249 		WPSPosition pos;
1250 		if (!zone->getGraphicShape(shape, pos))
1251 			continue;
1252 		WPSGraphicStyle style;
1253 		if (zone->m_lineId)
1254 			m_styleManager->updateLineStyle(zone->m_lineId, style);
1255 		if (zone->m_surfaceId)
1256 			m_styleManager->updateSurfaceStyle(zone->m_surfaceId, style);
1257 		if (zone->m_graphicId)
1258 			m_styleManager->updateGraphicStyle(zone->m_graphicId, style);
1259 		if (zone->m_textBoxEntry.valid())
1260 		{
1261 			std::shared_ptr<LotusGraphInternal::SubDocument> doc
1262 			(new LotusGraphInternal::SubDocument(zone->m_stream, *this, zone->m_textBoxEntry, 0));
1263 			m_listener->insertTextBox(pos, doc, style);
1264 			continue;
1265 		}
1266 		if (zone->m_chartId)
1267 		{
1268 			m_mainParser.sendChart(zone->m_chartId, pos, style);
1269 			continue;
1270 		}
1271 		if (zone->m_type==zone->Line)
1272 		{
1273 			if (zone->m_values[0]&1)
1274 				style.m_arrows[1]=true;
1275 			if (zone->m_values[0]&2)
1276 				style.m_arrows[0]=true;
1277 		}
1278 		m_listener->insertPicture(pos, shape, style);
1279 	}
1280 	auto it4=m_state->m_sheetIdZoneWK4Map.lower_bound(sheetId);
1281 	while (it4!=m_state->m_sheetIdZoneWK4Map.end() && it4->first==sheetId)
1282 	{
1283 		auto zone=it4++->second;
1284 		if (!zone) continue;
1285 		Vec2f decal;
1286 		if (!m_mainParser.getLeftTopPosition(zone->m_cell, sheetId, decal))
1287 			decal=Vec2f(float(72*zone->m_cell[0]),float(16*zone->m_cell[1]));
1288 		Vec2f dimension(zone->m_frameSize);
1289 		if (zone->m_type==zone->Shape)
1290 			dimension=zone->m_shape.getBdBox().size();
1291 		else if (zone->m_type==zone->Picture)
1292 			dimension=Vec2f(zone->m_pictureDim.size());
1293 		WPSPosition pos(decal+zone->m_cellPosition, dimension, librevenge::RVNG_POINT);
1294 		pos.setRelativePosition(WPSPosition::Page);
1295 		if (zone->m_type==zone->Shape)
1296 			m_listener->insertPicture(pos, zone->m_shape, zone->m_graphicStyle);
1297 		else if (zone->m_type==zone->TextBox)
1298 		{
1299 			std::shared_ptr<LotusGraphInternal::SubDocument> doc
1300 			(new LotusGraphInternal::SubDocument(zone->m_stream, *this, zone->m_textEntry, zone->m_subType==0xd ? 2 : 1));
1301 			m_listener->insertTextBox(pos, doc, zone->m_graphicStyle);
1302 		}
1303 		else if (zone->m_type==zone->Chart)
1304 		{
1305 			if (zone->m_pictureName.empty())
1306 			{
1307 				WPS_DEBUG_MSG(("LotusGraph::sendGraphics: sorry, can not find the chart name\n"));
1308 			}
1309 			else
1310 			{
1311 				auto nIt=m_state->m_nameToChartIdMap.find(zone->m_pictureName);
1312 				if (nIt==m_state->m_nameToChartIdMap.end())
1313 				{
1314 					WPS_DEBUG_MSG(("LotusGraph::sendGraphics: sorry, can not find the chart id for %s\n", zone->m_pictureName.c_str()));
1315 				}
1316 				else
1317 					m_mainParser.sendChart(nIt->second, pos, zone->m_graphicStyle);
1318 			}
1319 		}
1320 		else if (zone->m_type==zone->Picture)
1321 		{
1322 			WPSEmbeddedObject object;
1323 			if (m_mainParser.updateEmbeddedObject(zone->m_id, object) && !object.isEmpty())
1324 				m_listener->insertObject(pos, object);
1325 		}
1326 		else
1327 		{
1328 			static bool first=true;
1329 			if (first)
1330 			{
1331 				first=false;
1332 				WPS_DEBUG_MSG(("LotusGraph::sendGraphics: sorry, sending some graph types is not implemented\n"));
1333 			}
1334 		}
1335 	}
1336 	if (m_state->m_zIdToSheetIdMap.find(sheetId)!=m_state->m_zIdToSheetIdMap.end())
1337 	{
1338 		int finalId=m_state->m_zIdToSheetIdMap.find(sheetId)->second;
1339 		if (m_state->m_sheetIdZonePcListMap.find(finalId)!=m_state->m_sheetIdZonePcListMap.end())
1340 		{
1341 			auto const &zoneList=m_state->m_sheetIdZonePcListMap.find(finalId)->second;
1342 			WPSTransformation transform;
1343 			for (size_t i=0; i<zoneList.m_zones.size(); ++i)
1344 				sendZone(zoneList, i, transform);
1345 		}
1346 	}
1347 }
1348 
readZoneBeginC9(std::shared_ptr<WPSStream> stream)1349 bool LotusGraph::readZoneBeginC9(std::shared_ptr<WPSStream> stream)
1350 {
1351 	if (!stream) return false;
1352 	RVNGInputStreamPtr &input=stream->m_input;
1353 	libwps::DebugFile &ascFile=stream->m_ascii;
1354 	libwps::DebugStream f;
1355 
1356 	long pos = input->tell();
1357 	auto type = long(libwps::read16(input));
1358 	if (type!=0xc9)
1359 	{
1360 		WPS_DEBUG_MSG(("LotusGraph::readZoneBeginC9: not a sheet header\n"));
1361 		return false;
1362 	}
1363 	auto sz = long(libwps::readU16(input));
1364 	f << "Entries(FMTGraphBegin):";
1365 	if (sz != 1)
1366 	{
1367 		WPS_DEBUG_MSG(("LotusGraph::readZoneBeginC9: the zone size seems bad\n"));
1368 		f << "###";
1369 		ascFile.addPos(pos);
1370 		ascFile.addNote(f.str().c_str());
1371 		return true;
1372 	}
1373 	m_state->m_actualSheetId=int(libwps::readU8(input));
1374 	f << "sheet[id]=" << m_state->m_actualSheetId << ",";
1375 	ascFile.addPos(pos);
1376 	ascFile.addNote(f.str().c_str());
1377 	return true;
1378 }
1379 
readFMTPictName(std::shared_ptr<WPSStream> stream)1380 bool LotusGraph::readFMTPictName(std::shared_ptr<WPSStream> stream)
1381 {
1382 	RVNGInputStreamPtr &input = stream->m_input;
1383 	libwps::DebugFile &ascFile=stream->m_ascii;
1384 	libwps::DebugStream f;
1385 
1386 	long pos = input->tell();
1387 	auto type = int(libwps::read16(input));
1388 	if (type!=0xb7)
1389 	{
1390 		WPS_DEBUG_MSG(("LotusGraph::readFMTPictName: not a font name definition\n"));
1391 		return false;
1392 	}
1393 	auto sz = long(libwps::readU16(input));
1394 	if (sz!=0x68)
1395 	{
1396 		WPS_DEBUG_MSG(("LotusGraph::readFMTPictName: the zone size seems bad\n"));
1397 		ascFile.addPos(pos);
1398 		ascFile.addNote("Entries(FMTPictName):###");
1399 		return true;
1400 	}
1401 	f << "Entries(FMTPictName):";
1402 	std::string name;
1403 	for (int i=0; i<16; ++i)
1404 	{
1405 		auto c=char(libwps::readU8(input));
1406 		if (c==0) break;
1407 		name+= c;
1408 	}
1409 	f << "name=" << name << ",";
1410 	// <METAFILE> means data in Pictbf?
1411 	if (version()==3)
1412 	{
1413 		if (!m_state->m_actualZoneWK4)
1414 		{
1415 			// rare, find also this zone isolated in the header zone...
1416 			WPS_DEBUG_MSG(("LotusGraph::readFMTPictName: can not find the current chart\n"));
1417 		}
1418 		else
1419 			m_state->m_actualZoneWK4->m_pictureName=name;
1420 	}
1421 	input->seek(pos+4+16, librevenge::RVNG_SEEK_SET);
1422 	for (int i=0; i<2; ++i)
1423 	{
1424 		// seems ok in wk3 files but not in wk4 files
1425 		auto col=int(libwps::readU8(input));
1426 		auto table=int(libwps::readU8(input));
1427 		auto row=int(libwps::readU16(input));
1428 		f << "C" << col << "-" << row;
1429 		if (table) f << "[" << table << "]";
1430 		if (i==0)
1431 			f << "<->";
1432 		else
1433 			f << ",";
1434 	}
1435 	for (int i=0; i<5; ++i)   // f1=0|1|3
1436 	{
1437 		auto val=int(libwps::readU16(input));
1438 		if (!val) continue;
1439 		f << "f" << i << "=" << val << ",";
1440 	}
1441 	int dim[2];
1442 	for (auto &d : dim) d=int(libwps::readU16(input));
1443 	if (dim[0]||dim[1]) f << "dim=" << Vec2i(dim[0],dim[1]) << ",";
1444 	ascFile.addPos(pos);
1445 	ascFile.addNote(f.str().c_str());
1446 
1447 	pos=input->tell();
1448 	f.str("");
1449 	f << "FMTPictName-A:" << ",";
1450 	name.clear();
1451 	for (int i=0; i<16; ++i)
1452 	{
1453 		auto c=char(libwps::readU8(input));
1454 		if (c==0) break;
1455 		name+= c;
1456 	}
1457 	if (!name.empty())
1458 		// if file name is not empty, we will not retrieve the chart, ...
1459 		f << "fileName=" << name << ",";
1460 	input->seek(pos+16, librevenge::RVNG_SEEK_SET);
1461 	for (int i=0; i<25; ++i)
1462 	{
1463 		auto val=int(libwps::readU16(input));
1464 		if (!val) continue;
1465 		f << "f" << i << "=" << val << ",";
1466 	}
1467 	ascFile.addPos(pos);
1468 	ascFile.addNote(f.str().c_str());
1469 	return true;
1470 }
1471 
readGraphic(std::shared_ptr<WPSStream> stream)1472 bool LotusGraph::readGraphic(std::shared_ptr<WPSStream> stream)
1473 {
1474 	if (!stream) return false;
1475 	RVNGInputStreamPtr &input=stream->m_input;
1476 	libwps::DebugFile &ascFile=stream->m_ascii;
1477 	libwps::DebugStream f;
1478 
1479 	long pos = input->tell();
1480 	auto type = long(libwps::read16(input));
1481 	if (type!=0xca)
1482 	{
1483 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: not a sheet header\n"));
1484 		return false;
1485 	}
1486 	auto sz = long(libwps::readU16(input));
1487 	f << "Entries(FMTGraphic):";
1488 	if (sz < 0x23)
1489 	{
1490 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: the zone size seems bad\n"));
1491 		f << "###";
1492 		ascFile.addPos(pos);
1493 		ascFile.addNote(f.str().c_str());
1494 		return true;
1495 	}
1496 	auto mType=int(libwps::readU8(input));
1497 	if (mType==2) f << "graph,";
1498 	else if (mType==4) f << "group,";
1499 	else if (mType==5) f << "chart,";
1500 	else if (mType==0xa) f << "textbox,";
1501 	else if (mType==0xb) f << "cell[border],";
1502 	else if (mType==0xc) f << "graph,";
1503 	else f << "type[main]=" << mType << ",";
1504 
1505 	std::shared_ptr<LotusGraphInternal::ZoneWK4> zone(new LotusGraphInternal::ZoneWK4(stream));
1506 	zone->m_subType=int(libwps::readU8(input));
1507 	switch (zone->m_subType)
1508 	{
1509 	case 1:
1510 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1511 		f << "line,";
1512 		break;
1513 	case 2:
1514 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1515 		f << "poly,";
1516 		break;
1517 	case 4:
1518 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1519 		f << "arc,";
1520 		break;
1521 	case 5:
1522 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1523 		f << "spline,";
1524 		break;
1525 	case 6:
1526 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1527 		f << "rect,";
1528 		break;
1529 	case 7:
1530 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1531 		f << "rect[round],";
1532 		break;
1533 	case 8:
1534 		zone->m_type=LotusGraphInternal::ZoneWK4::Shape;
1535 		f << "oval,";
1536 		break;
1537 	case 9:
1538 		zone->m_type=LotusGraphInternal::ZoneWK4::Chart;
1539 		f << "chart,";
1540 		break;
1541 	case 0xa:
1542 		zone->m_type=LotusGraphInternal::ZoneWK4::Group;
1543 		f << "group,";
1544 		break;
1545 	case 0xd:
1546 		zone->m_type=LotusGraphInternal::ZoneWK4::TextBox;
1547 		f << "button,";
1548 		break;
1549 	case 0xe:
1550 		zone->m_type=LotusGraphInternal::ZoneWK4::TextBox;
1551 		f << "textbox,";
1552 		break;
1553 	case 0x10:
1554 		zone->m_type=LotusGraphInternal::ZoneWK4::Border;
1555 		f << "cell[border],";
1556 		break;
1557 	case 0x11:
1558 		zone->m_type=LotusGraphInternal::ZoneWK4::Picture;
1559 		f << "picture,";
1560 		break;
1561 	default:
1562 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: find unknown graphic type=%d\n", zone->m_subType));
1563 		f << "##type[local]=" << zone->m_subType << ",";
1564 		break;
1565 	}
1566 	auto val=int(libwps::readU8(input)); // 0|80
1567 	if (val) f << "fl0=" << std::hex << val << std::dec << ",";
1568 	zone->m_id=int(libwps::readU16(input));
1569 	f << "id=" << zone->m_id << ",";
1570 
1571 	f << "line=[";
1572 	val=int(libwps::readU8(input));
1573 	WPSGraphicStyle &style=zone->m_graphicStyle;
1574 	if (!m_styleManager->getColor256(val, style.m_lineColor))
1575 	{
1576 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: can not read a color\n"));
1577 		f << "###colId=" << val << ",";
1578 	}
1579 	else if (!style.m_lineColor.isBlack()) f << style.m_lineColor << ",";
1580 	val=int(libwps::readU8(input));
1581 	bool noLine=false;
1582 	if (val<8)
1583 	{
1584 		switch (val)
1585 		{
1586 		case 0:
1587 			f << "none,";
1588 			noLine=true;
1589 			break;
1590 		case 2:
1591 			style.m_lineDashWidth.push_back(7);
1592 			style.m_lineDashWidth.push_back(3);
1593 			f << "dash7x3";
1594 			break;
1595 		case 3:
1596 			style.m_lineDashWidth.push_back(4);
1597 			style.m_lineDashWidth.push_back(4);
1598 			f << "dot4x4";
1599 			break;
1600 		case 4:
1601 			style.m_lineDashWidth.push_back(6);
1602 			style.m_lineDashWidth.push_back(2);
1603 			style.m_lineDashWidth.push_back(4);
1604 			style.m_lineDashWidth.push_back(2);
1605 			f << "dash6x2:4x2";
1606 			break;
1607 		case 5:
1608 			style.m_lineDashWidth.push_back(4);
1609 			style.m_lineDashWidth.push_back(2);
1610 			style.m_lineDashWidth.push_back(2);
1611 			style.m_lineDashWidth.push_back(2);
1612 			style.m_lineDashWidth.push_back(2);
1613 			style.m_lineDashWidth.push_back(2);
1614 			f << "dash4x2:2x2:2x2";
1615 			break;
1616 		case 6:
1617 			style.m_lineDashWidth.push_back(2);
1618 			style.m_lineDashWidth.push_back(2);
1619 			f << "dot2x2";
1620 			break;
1621 		case 7:
1622 			style.m_lineDashWidth.push_back(1);
1623 			style.m_lineDashWidth.push_back(1);
1624 			f << "dot1x1";
1625 			break;
1626 		default:
1627 			break;
1628 		}
1629 	}
1630 	else
1631 	{
1632 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: can not read the line's style\n"));
1633 		f << "###style=" << val << ",";
1634 	}
1635 	val=int(libwps::readU8(input));
1636 	if (val<8)
1637 	{
1638 		style.m_lineWidth = noLine ? 0.f : float(val+1);
1639 		if (val) f << "w=" << val+1 << ",";
1640 	}
1641 	else
1642 	{
1643 		style.m_lineWidth= noLine ? 0 : 1;
1644 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: can not read the line's width\n"));
1645 		f << "###width=" << val << ",";
1646 	}
1647 	f << "],";
1648 	f << "surf=[";
1649 	int colId[2];
1650 	for (int i=0; i<2; ++i)
1651 	{
1652 		colId[1-i]=int(libwps::readU8(input));
1653 		f << colId[1-i] << ",";
1654 	}
1655 	auto patternId=int(libwps::readU8(input));
1656 	f << patternId << ",";
1657 	if (!m_styleManager->updateSurfaceStyle(colId[0], colId[1], patternId, style))
1658 		f << "###";
1659 	f << "],";
1660 	f << "shadow=["; // border design
1661 	val=int(libwps::readU8(input));
1662 	WPSColor color;
1663 	if (!m_styleManager->getColor256(val, color))
1664 	{
1665 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: can not read a color\n"));
1666 		f << "###colId=" << val << ",";
1667 	}
1668 	else if (!color.isBlack()) f << color << ",";
1669 	val=int(libwps::readU8(input)); // 0=none
1670 	if (val)
1671 	{
1672 		zone->m_hasShadow=true;
1673 		f << "type=" << val << ",";
1674 	}
1675 	f << "],";
1676 	float matrix[6];
1677 	for (float &i : matrix) i=float(libwps::read32(input))/65536.f;
1678 	WPSTransformation transform(WPSVec3f(matrix[0],matrix[1],matrix[4]), WPSVec3f(matrix[2],matrix[3],matrix[5]));
1679 	if (!transform.isIdentity())
1680 		f << "mat=[" << matrix[0] << "," << matrix[1] << "," << matrix[4]
1681 		  << " ," << matrix[2] << "," << matrix[3] << "," << matrix[5] << "],";
1682 	unsigned long lVal=libwps::readU32(input); // unsure maybe related to some angle
1683 	if (lVal) f << "unkn=" << std::hex << lVal << std::dec << ",";
1684 	for (int i=0; i<2; ++i)   // f0=0|1|2, f1=0|5|6
1685 	{
1686 		val=int(libwps::readU8(input));
1687 		if (val) f << "f" << i << "=" << val << ",";
1688 	}
1689 	switch (zone->m_subType)
1690 	{
1691 	case 1:   // line
1692 	{
1693 		if (sz!=0x37)
1694 			break;
1695 		val=int(libwps::readU16(input));
1696 		if (val!=2) f << "g0=" << val << ",";
1697 		val=int(libwps::readU16(input));
1698 		if (val&1)
1699 		{
1700 			style.m_arrows[0]=true;
1701 			f << "arrow[beg],";
1702 		}
1703 		if (val&2)
1704 		{
1705 			style.m_arrows[1]=true;
1706 			f << "arrow[end],";
1707 		}
1708 		val&=0xFFFC;
1709 		if (val) f << "g1=" << std::hex << val << std::dec << ",";
1710 		int pts[4];
1711 		for (int &pt : pts) pt=int(libwps::readU16(input));
1712 		f << "pts=" << Vec2i(pts[0],pts[1]) << "<->" << Vec2i(pts[2],pts[3]) << ",";
1713 		zone->m_shape=WPSGraphicShape::line(Vec2f(float(pts[0]),float(pts[1])), Vec2f(float(pts[2]),float(pts[3])));
1714 		break;
1715 	}
1716 	case 4:   // arc
1717 	{
1718 		if (sz!=0x3b)
1719 			break;
1720 		val=int(libwps::readU16(input));
1721 		if (val!=3) f << "g0=" << val << ",";
1722 		val=int(libwps::readU16(input)); // 0208
1723 		if (val) f << "g1=" << std::hex << val << std::dec << ",";
1724 		f << "pts=[";
1725 		std::vector<Vec2f> vertices;
1726 		vertices.resize(3);
1727 		WPSBox2f box;
1728 		for (size_t i=0; i<3; ++i)
1729 		{
1730 			int pts[2];
1731 			for (int &pt : pts) pt=int(libwps::readU16(input));
1732 			vertices[i]=Vec2f(float(pts[0]),float(pts[1]));
1733 			if (i==0) box=WPSBox2f(vertices[i],vertices[i]);
1734 			else box=box.getUnion(WPSBox2f(vertices[i],vertices[i]));
1735 			f << vertices[i] << ",";
1736 		}
1737 		f << "],";
1738 		// not frequent, approximate it by a Bezier's curve
1739 		zone->m_shape=WPSGraphicShape::path(box);
1740 		zone->m_shape.m_path.push_back(WPSGraphicShape::PathData('M', vertices[0]));
1741 		zone->m_shape.m_path.push_back(WPSGraphicShape::PathData('Q', vertices[2], vertices[1]));
1742 		break;
1743 	}
1744 	case 2:
1745 	case 5:
1746 	{
1747 		auto N=int(libwps::readU16(input));
1748 		if (sz!=4*N+0x2f) break;
1749 		val=int(libwps::readU16(input)); // 0
1750 		if (val) f << "g0=" << val << ",";
1751 		std::vector<Vec2f> vertices;
1752 		vertices.resize(size_t(N));
1753 		f << "pts=[";
1754 		WPSBox2f box;
1755 		for (size_t i=0; i<size_t(N); ++i)
1756 		{
1757 			int pts[2];
1758 			for (int &pt : pts) pt=int(libwps::readU16(input));
1759 			vertices[i]=Vec2f(float(pts[0]),float(pts[1]));
1760 			if (i==0) box=WPSBox2f(vertices[i],vertices[i]);
1761 			else box=box.getUnion(WPSBox2f(vertices[i],vertices[i]));
1762 			f << vertices[i] << ",";
1763 		}
1764 		f << "],";
1765 		if (zone->m_subType==2 || vertices.size()<=1)
1766 		{
1767 			zone->m_shape=WPSGraphicShape::polygon(box);
1768 			zone->m_shape.m_vertices=vertices;
1769 		}
1770 		else
1771 		{
1772 			zone->m_shape=WPSGraphicShape::path(box);
1773 			zone->m_shape.m_path.push_back(WPSGraphicShape::PathData('M', vertices[0]));
1774 			for (size_t i=0; i+1<vertices.size(); ++i)
1775 				zone->m_shape.m_path.push_back(WPSGraphicShape::PathData('Q', 0.5f*(vertices[i]+vertices[i+1]),
1776 				                                                         vertices[i]));
1777 			zone->m_shape.m_path.push_back(WPSGraphicShape::PathData('T', vertices.back()));
1778 		}
1779 		break;
1780 	}
1781 	case 6: // rect
1782 	case 7: // rectround
1783 	case 8:   // oval
1784 	{
1785 		if (sz!=0x3f)
1786 			break;
1787 		val=int(libwps::readU16(input));
1788 		if (val!=4) f << "g0=" << val << ",";
1789 		for (int i=0; i<2; ++i)   // g1=4, g2=0|1
1790 		{
1791 			val=int(libwps::readU8(input));
1792 			if (i==1)
1793 			{
1794 				if (val&1)
1795 					f << "round,";
1796 				else if (val&2)
1797 					f << "oval,";
1798 				val &= 0xFC;
1799 			}
1800 			if (val)
1801 				f << "g" << i+1 << "=" << val << ",";
1802 		}
1803 		WPSBox2f box;
1804 		f << "pts=[";
1805 		for (int i=0; i<4; ++i)
1806 		{
1807 			int pts[2];
1808 			for (int &pt : pts) pt=int(libwps::readU16(input));
1809 			Vec2f pt=Vec2f(float(pts[0]), float(pts[1]));
1810 			f << pt << ",";
1811 			if (i==0)
1812 				box=WPSBox2f(pt,pt);
1813 			else
1814 				box=box.getUnion(WPSBox2f(pt,pt));
1815 		}
1816 		f << "],";
1817 		if (zone->m_subType==8)
1818 			zone->m_shape=WPSGraphicShape::circle(box);
1819 		else
1820 			zone->m_shape=WPSGraphicShape::rectangle(box, zone->m_subType==6 ? Vec2f(0,0) : Vec2f(5,5));
1821 		break;
1822 	}
1823 	case 9: // chart, readme
1824 		if (sz!=0x33)
1825 			break;
1826 		f << "dim=";
1827 		for (int i=0; i<2; ++i)
1828 		{
1829 			int pts[2];
1830 			for (int &pt : pts) pt=int(libwps::readU16(input));
1831 			f << Vec2i(pts[0],pts[1]) << (i==0 ? "<->" : ",");
1832 		}
1833 		break;
1834 	case 10: // group, useme
1835 		if (sz!=0x35)
1836 			break;
1837 		f << "pts=[";
1838 		for (int i=0; i<2; ++i)
1839 		{
1840 			int pts[2];
1841 			for (int &pt : pts) pt=int(libwps::readU16(input));
1842 			f << Vec2i(pts[0],pts[1]) << ",";
1843 		}
1844 		f << "],";
1845 		val=int(libwps::readU16(input));
1846 		if (val!=1) f << "g0=" << val << ",";
1847 		break;
1848 	case 0xd:
1849 	case 0xe:
1850 		if (sz!=0x35) break;
1851 		f << "pts=";
1852 		for (int i=0; i<4; ++i)
1853 		{
1854 			f << int(libwps::readU16(input));
1855 			if (i==1) f << "<->";
1856 			else if (i==3) f << ",";
1857 			else f << "x";
1858 		}
1859 		val=int(libwps::readU16(input)); // small number
1860 		f << "g0=" << val << ",";
1861 		break;
1862 	case 0x10:
1863 		if (sz!=0x34) break;
1864 		for (int i=0; i<2; ++i)   // 0
1865 		{
1866 			val=int(libwps::readU16(input));
1867 			if (val) f << "g" << i << "=" << val << ",";
1868 		}
1869 		for (int i=0; i<3; ++i)   // g2=[0-f][0|8], g3=0-2 g4=??
1870 		{
1871 			val=int(libwps::readU8(input));
1872 			if (val) f << "g" << i+2 << "=" << std::hex << val << std::dec << ",";
1873 		}
1874 		val=int(libwps::readU16(input)); // 0-2
1875 		if (val) f << "g5=" << val << ",";
1876 		break;
1877 	case 0x11:
1878 	{
1879 		if (sz!=0x43) break;
1880 		Vec2i dim[2];
1881 		for (auto &i : dim)
1882 		{
1883 			int pts[2];
1884 			for (int &pt : pts) pt=int(libwps::readU16(input));
1885 			i=Vec2i(pts[0],pts[1]);
1886 		}
1887 		zone->m_pictureDim=WPSBox2i(dim[0], dim[1]);
1888 		f << "dim=" << zone->m_pictureDim << ",";
1889 		val=int(libwps::readU16(input)); // big number 2590..3262
1890 		if (val) f << "g0=" << std::hex << val << std::dec << ",";
1891 		val=int(libwps::readU16(input));
1892 		if (val!=0x3cf7) f << "g1=" << std::hex << val << std::dec << ",";
1893 		for (int i=0; i<6; ++i)   // 0
1894 		{
1895 			val=int(libwps::readU16(input));
1896 			if (val) f << "g" << i+2 << "=" << val << ",";
1897 		}
1898 		break;
1899 	}
1900 	default:
1901 		break;
1902 	}
1903 	if (zone->m_shape.m_type!=WPSGraphicShape::ShapeUnknown && !transform.isIdentity())
1904 		zone->m_shape=zone->m_shape.transform(transform);
1905 	if (m_state->m_actualZoneWK4)
1906 	{
1907 		WPS_DEBUG_MSG(("LotusGraph::readGraphic: oops an zone is already defined\n"));
1908 	}
1909 	m_state->m_actualZoneWK4=zone;
1910 	if (input->tell() != pos+4+sz)
1911 		ascFile.addDelimiter(input->tell(), '|');
1912 	ascFile.addPos(pos);
1913 	ascFile.addNote(f.str().c_str());
1914 	return true;
1915 }
1916 
readFrame(std::shared_ptr<WPSStream> stream)1917 bool LotusGraph::readFrame(std::shared_ptr<WPSStream> stream)
1918 {
1919 	if (!stream) return false;
1920 	RVNGInputStreamPtr &input=stream->m_input;
1921 	libwps::DebugFile &ascFile=stream->m_ascii;
1922 	libwps::DebugStream f;
1923 
1924 	long pos = input->tell();
1925 	auto type = long(libwps::read16(input));
1926 	if (type!=0xcc)
1927 	{
1928 		WPS_DEBUG_MSG(("LotusGraph::readFrame: not a frame header\n"));
1929 		return false;
1930 	}
1931 	auto sz = long(libwps::readU16(input));
1932 	f << "Entries(FMTFrame):";
1933 	if (sz != 0x13)
1934 	{
1935 		WPS_DEBUG_MSG(("LotusGraph::readFrame: the zone size seems bad\n"));
1936 		f << "###";
1937 		ascFile.addPos(pos);
1938 		ascFile.addNote(f.str().c_str());
1939 		return true;
1940 	}
1941 	auto zone=m_state->m_actualZoneWK4;
1942 	if (!zone)
1943 	{
1944 		WPS_DEBUG_MSG(("LotusGraph::readFrame: can not find the original shape\n"));
1945 		f << "##noShape,";
1946 	}
1947 	/* the positions are relative to a cell ; the first cell stores the LT position,
1948 	   while the second stores the RB position.
1949 
1950 	   fixme: if we want precise position, we must add graphic with anchor-type=cell
1951 	     instead of using the first cell to find the page's LT position...
1952 	 */
1953 	for (int c=0; c<2; ++c)
1954 	{
1955 		auto row=int(libwps::readU16(input));
1956 		auto col=int(libwps::readU8(input));
1957 		int pts[2]; // first in 100.*char, second in point
1958 		for (int &pt : pts) pt=int(libwps::readU16(input));
1959 		Vec2f decal(8.f*float(pts[0])/100.f, float(pts[1]));
1960 		Vec2i cell(col, row);
1961 		if (c==0 && zone)
1962 		{
1963 			zone->m_cell=cell;
1964 			zone->m_cellPosition=decal;
1965 		}
1966 		f << "C" << cell << "[" << decal << "]";
1967 		if (c==0) f << "<->";
1968 		else f << ",";
1969 	}
1970 	// this is the initial dimension, which may become invalid when a column/row shrinks/grows...
1971 	int dim[2];
1972 	for (int &i : dim) i=int(libwps::readU16(input));
1973 	f << "dim=" << Vec2i(dim[0], dim[1]) << ",";
1974 	if (zone) zone->m_frameSize=Vec2i(dim[0], dim[1]);
1975 	auto val=int(libwps::readU8(input)); // 1|2
1976 	if (val&0x80) f << "in[group],";
1977 	val &= 0x7F;
1978 	if (val!=1) f << "fl0=" << val << ",";
1979 	ascFile.addPos(pos);
1980 	ascFile.addNote(f.str().c_str());
1981 
1982 	if (zone)
1983 	{
1984 		if (m_state->m_actualSheetId<0)
1985 		{
1986 			WPS_DEBUG_MSG(("LotusGraph::readFrame: oops no sheet zone is opened\n"));
1987 			f << "###sheetId,";
1988 		}
1989 		else
1990 			m_state->m_sheetIdZoneWK4Map.insert(std::multimap<int, std::shared_ptr<LotusGraphInternal::ZoneWK4> >::value_type
1991 			                                    (m_state->m_actualSheetId, zone));
1992 
1993 	}
1994 	m_state->m_actualZoneWK4.reset();
1995 
1996 	return true;
1997 }
1998 
readTextBoxDataD1(std::shared_ptr<WPSStream> stream)1999 bool LotusGraph::readTextBoxDataD1(std::shared_ptr<WPSStream> stream)
2000 {
2001 	if (!stream) return false;
2002 	RVNGInputStreamPtr &input=stream->m_input;
2003 	libwps::DebugFile &ascFile=stream->m_ascii;
2004 	libwps::DebugStream f;
2005 
2006 	long pos = input->tell();
2007 	auto type = long(libwps::read16(input));
2008 	if (type!=0xd1)
2009 	{
2010 		WPS_DEBUG_MSG(("LotusGraph::readTextBoxDataD1: not a textbox header\n"));
2011 		return false;
2012 	}
2013 	auto sz = long(libwps::readU16(input));
2014 	f << "Entries(FMTTextBox):";
2015 	if (!m_state->m_actualZoneWK4 || m_state->m_actualZoneWK4->m_type!=LotusGraphInternal::ZoneWK4::TextBox)
2016 	{
2017 		WPS_DEBUG_MSG(("LotusGraph::readTextBoxDataD1: find unexpected textbox data\n"));
2018 		f << "###";
2019 	}
2020 	else
2021 	{
2022 		m_state->m_actualZoneWK4->m_textEntry.setBegin(input->tell());
2023 		m_state->m_actualZoneWK4->m_textEntry.setLength(sz);
2024 		input->seek(sz, librevenge::RVNG_SEEK_CUR);
2025 	}
2026 	ascFile.addPos(pos);
2027 	ascFile.addNote(f.str().c_str());
2028 	return true;
2029 }
2030 
sendTextBoxWK4(std::shared_ptr<WPSStream> stream,WPSEntry const & entry,bool isButton)2031 void LotusGraph::sendTextBoxWK4(std::shared_ptr<WPSStream> stream, WPSEntry const &entry, bool isButton)
2032 {
2033 	if (!stream || !m_listener || (entry.length() && entry.length()<3))
2034 	{
2035 		WPS_DEBUG_MSG(("LotusGraph::sendTextBoxWK4: I can not find the listener/textbox entry\n"));
2036 		return;
2037 	}
2038 	RVNGInputStreamPtr input=stream->m_input;
2039 	libwps::DebugFile &ascFile=stream->m_ascii;
2040 	libwps::DebugStream f;
2041 	long endPos=entry.end();
2042 	input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2043 
2044 	auto fontType = m_mainParser.getDefaultFontType();
2045 	WPSFont font=WPSFont::getDefault();
2046 	m_listener->setFont(font);
2047 	while (!input->isEnd())
2048 	{
2049 		long pos = input->tell();
2050 		if (pos+3>endPos) break;
2051 		f.str("");
2052 		f << "FMTTextBox[data]:";
2053 		auto dSz=int(libwps::readU16(input));
2054 		if (pos+2+dSz>endPos)
2055 		{
2056 			input->seek(pos, librevenge::RVNG_SEEK_SET);
2057 			break;
2058 		}
2059 		bool send=!isButton || pos==entry.begin();
2060 		std::string text;
2061 		for (int i=0; i<dSz; ++i)
2062 		{
2063 			auto c=i+1==dSz ? '\0' : char(libwps::readU8(input));
2064 			if ((c==0 || c==1) && !text.empty())
2065 			{
2066 				if (send)
2067 					m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, fontType));
2068 				text.clear();
2069 			}
2070 			if (c==0)
2071 			{
2072 				if (i+2<dSz)
2073 				{
2074 					WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find a 0 char\n"));
2075 					f << "[###0]";
2076 				}
2077 				continue;
2078 			}
2079 			if (c==1)
2080 			{
2081 				if (i+2>=dSz)
2082 				{
2083 					WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find unexpected 1 char\n"));
2084 					f << "[###1]";
2085 					continue;
2086 				}
2087 				++i;
2088 				c=char(libwps::readU8(input));
2089 				f << "[1-" << int(c) << "]";
2090 				// find 010d010d010a
2091 				if (send)
2092 				{
2093 					if (c==0xd) m_listener->insertEOL(false);
2094 					else if (c==0xa) m_listener->insertEOL();
2095 					else
2096 					{
2097 						WPS_DEBUG_MSG(("LotusGraph::sendTextBox: find unexpected 1 char\n"));
2098 						f << "###";
2099 					}
2100 				}
2101 				continue;
2102 			}
2103 			f << c;
2104 			text.push_back(c);
2105 		}
2106 		f << ",unk=" << int(libwps::readU8(input));
2107 		if (input->tell()<endPos)
2108 			m_listener->insertEOL();
2109 		ascFile.addPos(pos);
2110 		ascFile.addNote(f.str().c_str());
2111 	}
2112 	if (isButton && input->tell()+1==endPos)
2113 	{
2114 		f.str("");
2115 		f << "FMTTextBox[data]:button=" << int(libwps::readU8(input)) << ",";
2116 		ascFile.addPos(endPos-1);
2117 		ascFile.addNote(f.str().c_str());
2118 	}
2119 	if (input->tell()!=endPos)
2120 	{
2121 		WPS_DEBUG_MSG(("LotusGraph::sendTextBoxWK4: find extra data\n"));
2122 		ascFile.addPos(input->tell());
2123 		ascFile.addNote("FMTTextBox[data]:###extra");
2124 	}
2125 }
2126 
sendZone(LotusGraphInternal::ZonePcList const & zoneList,size_t id,WPSTransformation & transf)2127 void LotusGraph::sendZone(LotusGraphInternal::ZonePcList const &zoneList, size_t id, WPSTransformation &transf)
2128 {
2129 	if (!m_listener)
2130 	{
2131 		WPS_DEBUG_MSG(("LotusGraph::sendZone: can not find the listener\n"));
2132 		return;
2133 	}
2134 	std::vector<std::shared_ptr<LotusGraphInternal::ZonePc> > const &zones=zoneList.m_zones;
2135 	if (id>=zones.size())
2136 	{
2137 		WPS_DEBUG_MSG(("LotusGraph::sendZone: can not find the sub zone %d\n", int(id)));
2138 		return;
2139 	}
2140 	auto zone=zones[id];
2141 	if (!zone || zone->m_isSent) return;
2142 	zone->m_isSent=true;
2143 	WPSTransformation finalTrans=transf*zone->getTransformation();
2144 	if (zone->m_type==zone->Set)
2145 	{
2146 		if (!zone->m_isGroup || zone->m_groupLastPosition+1<=id) return;
2147 		WPSPosition pos(zone->m_box[0],zone->m_box.size(), librevenge::RVNG_POINT);
2148 		pos.setRelativePosition(WPSPosition::Page);
2149 		if (!m_listener->openGroup(pos)) return;
2150 		for (size_t i=id+1; i<zone->m_groupLastPosition; ++i)
2151 			sendZone(zoneList, i, finalTrans);
2152 		m_listener->closeGroup();
2153 		return;
2154 	}
2155 	if (zone->m_type==zone->Picture)
2156 	{
2157 		if (!zone->m_pictureData.empty())
2158 		{
2159 			WPSPosition pos(zone->m_box[0],zone->m_box.size(), librevenge::RVNG_POINT);
2160 			pos.setRelativePosition(WPSPosition::Page);
2161 			m_listener->insertPicture(pos, zone->m_pictureData);
2162 #ifdef DEBUG_WITH_FILES
2163 			std::stringstream s;
2164 			static int fileId=0;
2165 			s << "Pict" << ++fileId << ".emf";
2166 			libwps::Debug::dumpFile(zone->m_pictureData, s.str().c_str());
2167 #endif
2168 		}
2169 		return;
2170 	}
2171 
2172 	WPSGraphicShape shape;
2173 	WPSPosition pos;
2174 	if (!zone->getGraphicShape(shape, pos))
2175 		return;
2176 	WPSGraphicStyle style;
2177 	if (zone->m_graphicId[0]>=0)
2178 		m_styleManager->updateGraphicStyle(zone->m_graphicId[0], style);
2179 	if (zone->m_type==zone->TextBox)
2180 	{
2181 		std::shared_ptr<LotusGraphInternal::SubDocument> doc
2182 		(new LotusGraphInternal::SubDocument(zone->m_stream, *this, zone->m_textBoxEntry, 1));
2183 		m_listener->insertTextBox(pos, doc, style);
2184 		return;
2185 	}
2186 	if (zone->m_type==zone->Line)
2187 	{
2188 		if (zone->m_arrows&1)
2189 			style.m_arrows[1]=true;
2190 		if (zone->m_arrows&2)
2191 			style.m_arrows[0]=true;
2192 	}
2193 	if (finalTrans.isIdentity())
2194 		m_listener->insertPicture(pos, shape, style);
2195 	else
2196 	{
2197 		// checkme: ok for translation but not for rotation...
2198 		WPSGraphicShape finalShape=shape.transform(finalTrans);
2199 		pos.setOrigin(finalTrans*pos.origin());
2200 		pos.setSize(finalShape.getBdBox().size());
2201 		m_listener->insertPicture(pos, finalShape, style);
2202 	}
2203 }
2204 
readGraphZone(std::shared_ptr<WPSStream> stream,int zId)2205 bool LotusGraph::readGraphZone(std::shared_ptr<WPSStream> stream, int zId)
2206 {
2207 	if (!stream) return false;
2208 	RVNGInputStreamPtr &input = stream->m_input;
2209 	libwps::DebugFile &ascFile=stream->m_ascii;
2210 	libwps::DebugStream f;
2211 	float const unit=version()>=5 ? 1.f/16.f : 1.f/256.f;
2212 	long pos = input->tell();
2213 	auto id = int(libwps::readU8(input));
2214 	if (libwps::readU8(input) != 3)
2215 	{
2216 		input->seek(pos, librevenge::RVNG_SEEK_SET);
2217 		return false;
2218 	}
2219 	auto sz = long(libwps::readU16(input));
2220 	long endPos=pos+4+sz;
2221 	if (sz<0 || !stream->checkFilePosition(endPos))
2222 	{
2223 		input->seek(pos, librevenge::RVNG_SEEK_SET);
2224 		return false;
2225 	}
2226 	if (zId)
2227 		f << "Entries(GraphZone)[Z" << zId << "]:";
2228 	else
2229 		f << "Entries(GraphZone)[_]:";
2230 	if (id<0x80)
2231 		m_state->m_actualZonePc.reset();
2232 	int val;
2233 	switch (id)
2234 	{
2235 	case 0: // rare, when it exists, present in sheet zone
2236 		f << "zoneA0,";
2237 		if (sz!=16)
2238 		{
2239 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone 0\n"));
2240 			f << "###";
2241 			break;
2242 		}
2243 		for (int i=0; i<8; ++i)   // always f0=1, other 0
2244 		{
2245 			val=int(libwps::readU16(input));
2246 			if (val) f << "f" << i << "=" << val << ",";
2247 		}
2248 		break;
2249 	case 4: // seems link to the ref zone, unsure
2250 		f << "ref,";
2251 		if (sz!=20)
2252 		{
2253 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone 4\n"));
2254 			f << "###";
2255 			break;
2256 		}
2257 		for (int i=0; i<10; ++i)   // always f0=1, other 0
2258 		{
2259 			val=int(libwps::readU16(input));
2260 			if (val) f << "f" << i << "=" << val << ",";
2261 		}
2262 		break;
2263 
2264 	case 0x83: // relative to graphic
2265 	case 0x84: // rare, when it exists before chartName
2266 	case 0x86: // rare, when it exists, end of a set. TODO useme
2267 		f << (id==0x83 ? "GraphBeg" : id==0x84 ? "chartBeg" : "endSet") << ",";
2268 		if (id==0x86)
2269 		{
2270 			if (zId<0 || m_state->m_sheetIdZonePcListMap.find(zId)==m_state->m_sheetIdZonePcListMap.end() ||
2271 			        m_state->m_sheetIdZonePcListMap.find(zId)->second.m_groupBeginStack.empty())
2272 			{
2273 				WPS_DEBUG_MSG(("LotusGraph::readGraphZone: oops can not find the begin of the group\n"));
2274 				f << "###group,";
2275 			}
2276 			else
2277 			{
2278 				auto &current=m_state->m_sheetIdZonePcListMap.find(zId)->second;
2279 				size_t begPos=current.m_groupBeginStack.top();
2280 				if (begPos<current.m_zones.size() && current.m_zones[begPos])
2281 					current.m_zones[begPos]->m_groupLastPosition=current.m_zones.size();
2282 				current.m_groupBeginStack.pop();
2283 			}
2284 		}
2285 		if (sz!=0)
2286 		{
2287 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone %d\n", id));
2288 			f << "###";
2289 			break;
2290 		}
2291 		break;
2292 	case 0x81: // rare, when it exists, in sheetA4 zones (followed by sheetB5, sheetB6)
2293 		f << "zoneB1,";
2294 		if (sz!=6)
2295 		{
2296 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone 81\n"));
2297 			f << "###";
2298 			break;
2299 		}
2300 		for (int i=0; i<3; ++i)   // always f0=1, other 0
2301 		{
2302 			val=int(libwps::read16(input));
2303 			if (val) f << "f" << i << "=" << val << ",";
2304 		}
2305 		break;
2306 	case 0x95: // relative to graphic
2307 		f << "zoneB15,";
2308 		if ((sz%4)!=0)
2309 		{
2310 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone 815\n"));
2311 			f << "###";
2312 			break;
2313 		}
2314 		if (sz==0) break;
2315 		val=int(libwps::read16(input));
2316 		if (m_state->m_actualZonePc)
2317 			m_state->m_actualZonePc->m_arrows=int(val&3);
2318 		switch (val&3)
2319 		{
2320 		case 1:
2321 			f << "arrow[beg],";
2322 			break;
2323 		case 2:
2324 			f << "arrow[end],";
2325 			break;
2326 		case 3:
2327 			f << "arrow[beg,end],";
2328 			break;
2329 		case 0:
2330 		default:
2331 			break;
2332 		}
2333 		val &=0xFFFC;
2334 		if (val) f << "f0=" << val << ",";
2335 		for (long i=1; i<sz/2; ++i)   // always f0=1, other 0
2336 		{
2337 			val=int(libwps::read16(input));
2338 			if (val) f << "f" << i << "=" << val << ",";
2339 		}
2340 		break;
2341 	case 0x85: // group or ???, after sheetB6.
2342 	case 0x88: // line after sheetB4
2343 	case 0x89: // polygon
2344 	case 0x8a: // freehand
2345 	case 0x8b: // rect
2346 	case 0x8c: // ellipse
2347 	case 0x8d: // arc
2348 	case 0x8e: // picture
2349 	case 0x90: // textbox
2350 	case 0x9a:   // chart, after sheetB4
2351 	{
2352 		std::shared_ptr<LotusGraphInternal::ZonePc> zone(new LotusGraphInternal::ZonePc(stream));
2353 		if (zId<0)
2354 		{
2355 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: oops can not find the sheet zone id\n"));
2356 			f << "###sheetId,";
2357 		}
2358 		else
2359 		{
2360 			// CHECKME: sometimes this does not work, ie. zId is not the spreadssheet data id
2361 			if (m_state->m_sheetIdZonePcListMap.find(zId)==m_state->m_sheetIdZonePcListMap.end())
2362 				m_state->m_sheetIdZonePcListMap.insert(std::map<int, LotusGraphInternal::ZonePcList>::value_type(zId, LotusGraphInternal::ZonePcList()));
2363 			auto &current=m_state->m_sheetIdZonePcListMap.find(zId)->second;
2364 			if (id==0x85) current.m_groupBeginStack.push(current.m_zones.size());
2365 			current.m_zones.push_back(zone);
2366 		}
2367 
2368 		m_state->m_actualZonePc=zone;
2369 		int expectedSz=0;
2370 		switch (id)
2371 		{
2372 		case 0x85:
2373 			zone->m_type=zone->Set;
2374 			expectedSz=80;
2375 			break;
2376 		case 0x88:
2377 			zone->m_type=zone->Line;
2378 			expectedSz=70;
2379 			break;
2380 		case 0x89:
2381 			zone->m_type=zone->Polygon;
2382 			expectedSz=70;
2383 			break;
2384 		case 0x8a:
2385 			zone->m_type=zone->FreeHand;
2386 			expectedSz=70;
2387 			break;
2388 		case 0x8b:
2389 			zone->m_type=zone->Rect;
2390 			expectedSz=110;
2391 			break;
2392 		case 0x8c:
2393 			zone->m_type=zone->Ellipse;
2394 			expectedSz=80;
2395 			break;
2396 		case 0x8d:
2397 			zone->m_type=zone->Arc;
2398 			expectedSz=84;
2399 			break;
2400 		case 0x8e:
2401 			zone->m_type=zone->Picture;
2402 			expectedSz=116;
2403 			break;
2404 		case 0x90:
2405 			zone->m_type=zone->TextBox;
2406 			expectedSz=112;
2407 			break;
2408 		case 0x9a:
2409 			zone->m_type=zone->Chart;
2410 			expectedSz=126;
2411 			break;
2412 		default:
2413 			break;
2414 		}
2415 
2416 		if (sz<expectedSz)
2417 		{
2418 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone %d\n", id));
2419 			f << *zone << "###";
2420 			break;
2421 		}
2422 		libwps::DebugStream f2;
2423 		f2 << "id=" << libwps::read32(input) << ","; // small number or -1
2424 		for (int i=0; i<5; ++i)   // f2=-1|4
2425 		{
2426 			val=int(libwps::readU16(input));
2427 			if (val==0xFFFF) continue;
2428 			switch (i)
2429 			{
2430 			case 0:
2431 				if ((val&0x10)==0x10) zone->m_isGroup=true;
2432 				if ((val&0x40)==0) f2 << "locked,";
2433 				if ((val&0x200)==0) zone->m_isRoundRect=true;
2434 				if ((val&0x2000)==0) f2 << "hidden,";
2435 				val &=0xDDAF;
2436 				if (val!=0x4d01)
2437 					f2 << "f0=" << std::hex << val << std::dec << ",";
2438 				break;
2439 			case 1:
2440 				if (val!=0x94)
2441 					f2 << "f1=" << std::hex << val << std::dec << ",";
2442 				break;
2443 			case 2:
2444 				if ((val>>8)==0x40)
2445 					zone->m_graphicId[0]=(val&0xFF);
2446 				else
2447 				{
2448 					WPS_DEBUG_MSG(("LotusGraph::readGraphZone: find unexpected graphic style id\n"));
2449 					f2 << "###GS" << std::hex << val << std::dec << ",";
2450 				}
2451 				break;
2452 			case 3:
2453 				switch (val)
2454 				{
2455 				case 0: // TL and BR
2456 					break;
2457 				case 1:
2458 					f2 << "fasten[TL],";
2459 					break;
2460 				case 2:
2461 					f2 << "no[fasten],";
2462 					break;
2463 				default:
2464 					f2 << "f3=" << val << ",";
2465 				}
2466 				break;
2467 			default:
2468 			{
2469 				if (val)
2470 					f2 << "f" << i << "=" << val << ",";
2471 				break;
2472 			}
2473 			}
2474 		}
2475 		auto sSz=int(libwps::readU16(input));
2476 		if ((id!=0x8e && expectedSz+sSz!=sz) || (id==0x8e && expectedSz+sSz>sz))
2477 		{
2478 			WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone %d\n", id));
2479 			f << *zone << "###" << f2.str();
2480 			break;
2481 		}
2482 		std::string name;
2483 		for (int i=0; i<sSz; ++i)
2484 		{
2485 			auto c=char(libwps::readU8(input));
2486 			if (c) name+=c;
2487 			else if (i+1!=sSz)
2488 			{
2489 				WPS_DEBUG_MSG(("LotusGraph::readGraphZone: find odd char in zone %d\n", id));
2490 				f2 << "###";
2491 			}
2492 		}
2493 		if (!name.empty()) f2 << name << ",";
2494 		f2 << "unkn=[";
2495 		for (int i=0; i<4; ++i) // often _,3ff00000,_,3ff00000
2496 		{
2497 			unsigned long lVal=libwps::readU32(input);
2498 			if (lVal)
2499 				f2 << std::hex << lVal << std::dec << ",";
2500 			else
2501 				f2 << "_,";
2502 		}
2503 		f2 << "],";
2504 		bool hasFlip=false;
2505 		for (int i=0; i<10; ++i)
2506 		{
2507 			val=int(libwps::readU16(input));
2508 			if (!val) continue;
2509 			if (i==0)
2510 				zone->m_rotate=float(val)/10.f;
2511 			else if (i==5 && val==1)
2512 			{
2513 				hasFlip=true;
2514 				f2 << "has[flip],";
2515 			}
2516 			else
2517 				f2 << "g" << i << "=" << std::hex << val << std::dec << ",";
2518 		}
2519 		float translate[2];
2520 		for (float &i : translate) i=unit*float(libwps::read32(input));
2521 		zone->m_translate=Vec2f(translate[0],translate[1]);
2522 		for (int i=0; i<2; ++i)
2523 		{
2524 			val=int(libwps::readU16(input));
2525 			if (!val) continue;
2526 			if (i==0 && hasFlip && val<3)   // useme
2527 			{
2528 				if (val&1) f2 << "flipX,";
2529 				if (val&2) f2 << "flipY,";
2530 				continue;
2531 			}
2532 			f2 << "g" << i+10 << "=" << std::hex << val << std::dec << ",";
2533 		}
2534 		if (id>=0x88 && id<=0x8a)
2535 		{
2536 			val=int(libwps::read16(input)); // 0
2537 			if (val) f2 << "h0=" << val << ",";
2538 			zone->m_numPoints=int(libwps::readU16(input));
2539 			val=int(libwps::read16(input)); // 0
2540 			if (val) f2 << "h1=" << val << ",";
2541 		}
2542 		else
2543 		{
2544 			float dim[4];
2545 			for (float &i : dim) i=unit*float(libwps::read32(input));
2546 			zone->m_box=WPSBox2f(Vec2f(dim[0],dim[1]),Vec2f(dim[2],dim[3]));
2547 			if (id==0x8b || id==0x8e || id==0x9a)
2548 			{
2549 				for (int i=0; i<2; ++i) // h0 small number
2550 				{
2551 					val=int(libwps::read16(input));
2552 					if (val)
2553 						f2 << "h" << i << "=" << val << ",";
2554 				}
2555 				val=int(libwps::readU16(input));
2556 				if ((val>>8)==0x40)
2557 					zone->m_graphicId[1]=(val&0xFF);
2558 				else if (val)
2559 				{
2560 					WPS_DEBUG_MSG(("LotusGraph::readGraphZone: find unexpected graphic style id\n"));
2561 					f2 << "###GS" << std::hex << val << std::dec << ",";
2562 				}
2563 			}
2564 			if (id==0x8d)
2565 			{
2566 				for (int i=0; i<2; ++i) // 0
2567 				{
2568 					val=int(libwps::read16(input));
2569 					if (val) f2 << "h" << i+3 << "=" << val << ",";
2570 				}
2571 			}
2572 			else if (id==0x8e || id==0x9a)
2573 			{
2574 				for (int i=0; i<11; ++i)   // h3=h4=h6=0|1, h8=h10=0|2d, h13=-1
2575 				{
2576 					val=int(libwps::read16(input));
2577 					if (val) f2 << "h" << i+3 << "=" << val << ",";
2578 				}
2579 				if (id==0x8e)
2580 				{
2581 					auto dSz=int(libwps::readU16(input));
2582 					if (expectedSz+sSz+dSz!=sz)
2583 					{
2584 						WPS_DEBUG_MSG(("LotusGraph::readGraphZone: the size seems bad for zone %d\n", id));
2585 						f << *zone << "###" << f2.str();
2586 						break;
2587 					}
2588 					std::string dir;
2589 					for (int i=0; i<dSz; ++i)
2590 					{
2591 						auto c=char(libwps::readU8(input));
2592 						if (c) dir+=c;
2593 						else if (i+1!=dSz)
2594 						{
2595 							WPS_DEBUG_MSG(("LotusGraph::readGraphZone: find odd char in zone %d\n", id));
2596 							f2 << "###";
2597 						}
2598 					}
2599 					if (!dir.empty()) f2 << dir << ",";
2600 					for (int i=0; i<2; ++i)   // l0=19, l1=24: maybe some zoneId
2601 					{
2602 						val=int(libwps::read16(input));
2603 						if (val) f2 << "l" << i << "=" << val << ",";
2604 					}
2605 				}
2606 				else
2607 				{
2608 					f2 << "prev[id]=Z" << int(libwps::read32(input)) << ",";
2609 					for (int i=0; i<5; ++i)
2610 					{
2611 						val=int(libwps::read16(input));
2612 						int const expected[]= {0,0,1,1,0};
2613 						if (val!=expected[i]) f2 << "l" << i << "=" << val << ",";
2614 					}
2615 					f2 << "act[id]=Z" << int(libwps::read32(input)) << ",";
2616 				}
2617 			}
2618 		}
2619 		zone->m_extra=f2.str();
2620 		f << *zone;
2621 		break;
2622 	}
2623 	default:
2624 		f << "type=" << std::hex << id << std::dec << ",";
2625 		break;
2626 	}
2627 	ascFile.addPos(pos);
2628 	ascFile.addNote(f.str().c_str());
2629 	if (input->tell()!=endPos && input->tell()!=pos)
2630 		ascFile.addDelimiter(input->tell(),'|');
2631 	input->seek(endPos, librevenge::RVNG_SEEK_SET);
2632 	return true;
2633 }
2634 
readGraphDataZone(std::shared_ptr<WPSStream> stream,long endPos)2635 bool LotusGraph::readGraphDataZone(std::shared_ptr<WPSStream> stream, long endPos)
2636 {
2637 	if (!stream) return false;
2638 	RVNGInputStreamPtr &input = stream->m_input;
2639 	libwps::DebugFile &ascFile=stream->m_ascii;
2640 	libwps::DebugStream f;
2641 	float const unit=version()>=5 ? 1.f/16.f : 1.f/256.f;
2642 	f << "Entries(GraphZone)[data]:";
2643 	long pos = input->tell();
2644 	auto sz=int(endPos-pos);
2645 	if (m_state->m_actualZonePc && m_state->m_actualZonePc->m_type==LotusGraphInternal::ZonePc::Line && sz==16)
2646 	{
2647 		f << "line,";
2648 		float dim[4];
2649 		for (float &i : dim) i=unit*float(libwps::read32(input));
2650 		m_state->m_actualZonePc->m_box= WPSBox2f(Vec2f(dim[0],dim[1]),Vec2f(dim[2],dim[3]));
2651 		f << "dim=" << m_state->m_actualZonePc->m_box << ",";
2652 	}
2653 	else if (m_state->m_actualZonePc && sz==8*m_state->m_actualZonePc->m_numPoints &&
2654 	         (m_state->m_actualZonePc->m_type==LotusGraphInternal::ZonePc::FreeHand || m_state->m_actualZonePc->m_type==LotusGraphInternal::ZonePc::Polygon))
2655 	{
2656 		f << "poly,pts=[";
2657 		float dim[2];
2658 		for (int n=0; n<m_state->m_actualZonePc->m_numPoints; ++n)
2659 		{
2660 			for (float &i : dim) i=unit*float(libwps::read32(input));
2661 			m_state->m_actualZonePc->m_vertices.push_back(Vec2f(dim[0],dim[1]));
2662 			f << m_state->m_actualZonePc->m_vertices.back() << ",";
2663 		}
2664 		f << "],";
2665 	}
2666 	else if (m_state->m_actualZonePc && m_state->m_actualZonePc->m_type==LotusGraphInternal::ZonePc::TextBox)
2667 	{
2668 		m_state->m_actualZonePc->m_textBoxEntry.setBegin(pos-2);
2669 		m_state->m_actualZonePc->m_textBoxEntry.setEnd(endPos);
2670 		f << "texbox,";
2671 		std::string text;
2672 		for (int i=0; i<sz; ++i)
2673 		{
2674 			auto c=char(libwps::readU8(input));
2675 			if (c)
2676 				text+=c;
2677 			else if (i+1!=sz)
2678 			{
2679 				WPS_DEBUG_MSG(("LotusGraph::readGraphDataZone: find unexpected 0 char\n"));
2680 				f << "###";
2681 			}
2682 		}
2683 		f << text;
2684 	}
2685 	else if (m_state->m_actualZonePc && m_state->m_actualZonePc->m_type==LotusGraphInternal::ZonePc::Picture)
2686 	{
2687 		/* checkme: the picture is stored in a list on consecutif data zone
2688 		   and seems preceded by 0100000008000000da0a0000da0a0000de380000
2689 		 */
2690 		if (endPos!=pos)
2691 		{
2692 			const unsigned char *readData;
2693 			unsigned long sizeRead, expectedSize=static_cast<unsigned long>(endPos-pos);
2694 			if ((readData=input->read(expectedSize, sizeRead)) == nullptr || sizeRead!=expectedSize)
2695 			{
2696 				WPS_DEBUG_MSG(("LotusGraph::readGraphDataZone: can not read the data\n"));
2697 				f << "###";
2698 			}
2699 			else
2700 			{
2701 				f << "picture,";
2702 				if (m_state->m_actualZonePc->m_pictureHeaderRead<20)   // skip the header? zone
2703 				{
2704 					int headerRead=m_state->m_actualZonePc->m_pictureHeaderRead+int(expectedSize) > 20 ?
2705 					               20-m_state->m_actualZonePc->m_pictureHeaderRead : int(expectedSize);
2706 					m_state->m_actualZonePc->m_pictureHeaderRead+=headerRead;
2707 					if (headerRead<int(expectedSize))
2708 					{
2709 						m_state->m_actualZonePc->m_pictureData.append(readData+headerRead, static_cast<unsigned long>(endPos-pos-headerRead));
2710 						ascFile.skipZone(pos+headerRead, endPos-1);
2711 					}
2712 				}
2713 				else
2714 				{
2715 					m_state->m_actualZonePc->m_pictureData.append(readData, sizeRead);
2716 					ascFile.skipZone(pos, endPos-1);
2717 				}
2718 			}
2719 		}
2720 	}
2721 	else
2722 	{
2723 		WPS_DEBUG_MSG(("LotusGraph::readGraphDataZone: find unknown data zone\n"));
2724 		f << "###";
2725 	}
2726 	ascFile.addPos(pos-4);
2727 	ascFile.addNote(f.str().c_str());
2728 	if (input->tell()!=endPos)
2729 		ascFile.addDelimiter(input->tell(),'|');
2730 	input->seek(endPos, librevenge::RVNG_SEEK_SET);
2731 	return true;
2732 }
2733 
2734 /* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
2735