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 ¤t=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 ¤t=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