1 /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
2 
3 /* libmwaw
4 * Version: MPL 2.0 / LGPLv2+
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 2.0 (the "License"); you may not use this file except in compliance with
8 * the License or as specified alternatively below. You may obtain a copy of
9 * the License at http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Major Contributor(s):
17 * Copyright (C) 2002 William Lachance (wrlach@gmail.com)
18 * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
19 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
20 * Copyright (C) 2006, 2007 Andrew Ziem
21 * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
22 *
23 *
24 * All Rights Reserved.
25 *
26 * For minor contributions see the git repository.
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
30 * in which case the provisions of the LGPLv2+ are applicable
31 * instead of those above.
32 */
33 
34 #include <cmath>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <map>
39 #include <set>
40 #include <sstream>
41 
42 #include <librevenge/librevenge.h>
43 
44 #include "MWAWListener.hxx"
45 #include "MWAWFont.hxx"
46 #include "MWAWFontConverter.hxx"
47 #include "MWAWGraphicEncoder.hxx"
48 #include "MWAWGraphicListener.hxx"
49 #include "MWAWGraphicShape.hxx"
50 #include "MWAWGraphicStyle.hxx"
51 #include "MWAWParagraph.hxx"
52 #include "MWAWPictBitmap.hxx"
53 #include "MWAWPictMac.hxx"
54 #include "MWAWPosition.hxx"
55 #include "MWAWSubDocument.hxx"
56 
57 #include "MsWksTable.hxx"
58 #include "MsWksDocument.hxx"
59 
60 #include "MsWksGraph.hxx"
61 
62 /** Internal: the structures of a MsWksGraph */
63 namespace MsWksGraphInternal
64 {
65 ////////////////////////////////////////
66 //! Internal: a list of zones ( for v4)
67 struct RBZone {
RBZoneMsWksGraphInternal::RBZone68   RBZone()
69     : m_isMain(true)
70     , m_id(-2)
71     , m_idList()
72     , m_frame("")
73   {
74   }
75   //! returns a unique id
getIdMsWksGraphInternal::RBZone76   int getId() const
77   {
78     return m_isMain ? -1 : m_id;
79   }
80   //! the zone type: rbdr(true) or rbil
81   bool m_isMain;
82   //! the zone id
83   int m_id;
84   //! the list of rb
85   std::vector<int> m_idList;
86   //! the frame name ( if it exist )
87   std::string m_frame;
88 };
89 
90 ////////////////////////////////////////
91 //! Internal: the generic pict
92 struct Zone {
93   enum Type { Unknown, Shape, ChartZone, Group, Pict, Text, Textv4, Bitmap, TableZone, OLE};
94   //! constructor
ZoneMsWksGraphInternal::Zone95   Zone()
96     : m_subType(-1)
97     , m_zoneId(-1)
98     , m_pos()
99     , m_dataPos(-1)
100     , m_fileId(-1)
101     , m_page(-1)
102     , m_decal()
103     , m_finalDecal()
104     , m_box()
105     , m_line(-1)
106     , m_style()
107     , m_order(0)
108     , m_extra("")
109     , m_doNotSend(false)
110     , m_isSent(false)
111   {
112     for (auto &id : m_ids) id = 0;
113   }
114   Zone(Zone const &)=default;
115   Zone &operator=(Zone const &)=default;
116   Zone &operator=(Zone &&)=default;
117   //! destructor
~ZoneMsWksGraphInternal::Zone118   virtual ~Zone() {}
119 
120   //! operator<<
operator <<(std::ostream & o,Zone const & pict)121   friend std::ostream &operator<<(std::ostream &o, Zone const &pict)
122   {
123     pict.print(o);
124     return o;
125   }
126   //! return the type
typeMsWksGraphInternal::Zone127   virtual Type type() const
128   {
129     return Unknown;
130   }
131 
132   //! return a binary data (if known)
getBinaryDataMsWksGraphInternal::Zone133   virtual bool getBinaryData(MWAWInputStreamPtr, MWAWEmbeddedObject &picture) const
134   {
135     picture=MWAWEmbeddedObject();
136     return false;
137   }
138 
139   //! return the extra border space ( if needed)
needExtraBorderWidthMsWksGraphInternal::Zone140   virtual float needExtraBorderWidth() const
141   {
142     return 0.0;
143   }
144 
145   //! add frame parameters to propList (if needed )
fillFrameMsWksGraphInternal::Zone146   virtual void fillFrame(MWAWGraphicStyle &) const { }
147 
148   //! return the box
getLocalBoxMsWksGraphInternal::Zone149   MWAWBox2f getLocalBox(bool extendWithBord=true) const
150   {
151     float x = m_box.size().x(), y=m_box.size().y();
152     MWAWVec2f min = m_box.min();
153     if (x < 0) {
154       min+=MWAWVec2f(x,0);
155       x *= -1.0f;
156     }
157     if (y < 0) {
158       min+=MWAWVec2f(0,y);
159       y *= -1.0f;
160     }
161     MWAWBox2f res(min, min+MWAWVec2f(x,y));
162     if (!extendWithBord) return res;
163     float bExtra = needExtraBorderWidth();
164     if (bExtra > 0) res.extend(2.0f*bExtra);
165     return res;
166   }
167 
getPositionMsWksGraphInternal::Zone168   MWAWPosition getPosition(MWAWPosition::AnchorTo rel) const
169   {
170     MWAWPosition res;
171     MWAWBox2f box = getLocalBox();
172     if (rel==MWAWPosition::Paragraph || rel==MWAWPosition::Frame) {
173       res = MWAWPosition(box.min()+m_finalDecal, box.size(), librevenge::RVNG_POINT);
174       res.setRelativePosition(rel);
175       if (rel==MWAWPosition::Paragraph)
176         res.m_wrapping = MWAWPosition::WBackground;
177     }
178     else if (rel!=MWAWPosition::Page) {
179       res = MWAWPosition(MWAWVec2f(0,0), box.size(), librevenge::RVNG_POINT);
180       res.setRelativePosition(MWAWPosition::Char,
181                               MWAWPosition::XLeft, MWAWPosition::YTop);
182     }
183     else {
184       res = MWAWPosition(box.min()+m_finalDecal, box.size(), librevenge::RVNG_POINT);
185       res.setRelativePosition(MWAWPosition::Page);
186       if (m_page >=0)
187         res.setPage(m_page+1);
188       res.m_wrapping =  MWAWPosition::WBackground;
189     }
190     if (m_order > 0) res.setOrder(m_order);
191     return res;
192   }
193 
194   //! the virtual print function
195   virtual void print(std::ostream &o) const;
196 
197   //! the type
198   int m_subType;
199   //! the zone id
200   int m_zoneId;
201   //! the file position
202   MWAWEntry m_pos;
203   //! the data begin position
204   long m_dataPos;
205   //! the file id
206   int m_fileId;
207   //! the zones id (main, previous, next)
208   long m_ids[3];
209   //! the page
210   int m_page;
211   //! the local position
212   MWAWBox2f m_decal;
213   //! the final local position
214   MWAWVec2f m_finalDecal;
215   //! local bdbox
216   MWAWBox2f m_box;
217   //! the line position(v1)
218   int m_line;
219   //! the style
220   MsWksGraph::Style m_style;
221   //! the picture order
222   int m_order;
223   //! extra data
224   std::string m_extra;
225   //! a flag used to know if we need to send the data ( or if this is the part of a sub group)
226   bool m_doNotSend;
227   //! true if the zone is send
228   bool m_isSent;
229 };
230 
print(std::ostream & o) const231 void Zone::print(std::ostream &o) const
232 {
233   if (m_fileId >= 0) {
234     o << "P" << m_fileId;
235     if (m_zoneId >= 0) o << "[" << m_zoneId << "],";
236     else o << ",";
237   }
238   for (int i = 0; i < 3; i++) {
239     if (m_ids[i] <= 0) continue;
240     switch (i) {
241     case 0:
242       o << "id=";
243       break;
244     case 1:
245       o << "pId=";
246       break;
247     default:
248       o << "nId=";
249       break;
250     }
251     o << std::hex << m_ids[i] << std::dec << ",";
252   }
253   switch (m_subType) {
254   case 0:
255     o << "line,";
256     break;
257   case 1:
258     o << "rect,";
259     break;
260   case 2:
261     o << "rectOval,";
262     break;
263   case 3:
264     o << "circle,";
265     break;
266   case 4:
267     o << "arc,";
268     break;
269   case 5:
270     o << "poly,";
271     break;
272   case 7:
273     o << "pict,";
274     break;
275   case 8:
276     o << "group,";
277     break;
278   case 9:
279     o << "textbox,";
280     break;
281   case 0xa:
282     o << "chart,";
283     break;
284   case 0xc:
285     o << "equation/graph,";
286     break;
287   case 0xd:
288     o << "bitmap,";
289     break;
290   case 0xe:
291     o << "ssheet,";
292     break;
293   case 0xf:
294     o << "textbox2,";
295     break;
296   case 0x10:
297     o << "table,";
298     break;
299   case 0x100:
300     o << "pict,";
301     break; // V1 pict
302   default:
303     o << "#type=" << m_subType << ",";
304   }
305   if (m_page>=0) o << "page=" << m_page << ",";
306   if (m_decal!=MWAWBox2f())
307     o << "pos=" << m_decal << ",";
308   o << "bdbox=" << m_box << ",";
309   o << "style=[" << m_style << "],";
310   if (m_line >= 0) o << "line=" << m_line << ",";
311   if (m_extra.length()) o << m_extra;
312 }
313 ////////////////////////////////////////
314 //! Internal: the group of a MsWksGraph
315 struct GroupZone final : public Zone {
316   //! constructor
GroupZoneMsWksGraphInternal::GroupZone317   explicit GroupZone(Zone const &z)
318     : Zone(z)
319     , m_childs()
320   {
321   }
322   //! destructor
323   ~GroupZone() final;
324   //! return the type
typeMsWksGraphInternal::GroupZone325   Type type() const final
326   {
327     return Group;
328   }
329   //! operator<<
printMsWksGraphInternal::GroupZone330   void print(std::ostream &o) const final
331   {
332     Zone::print(o);
333     o << "childs=[";
334     for (auto id : m_childs)
335       o << "P" << id << ",";
336     o << "],";
337   }
338   //! list of child id
339   std::vector<int> m_childs;
340 };
341 
~GroupZone()342 GroupZone::~GroupZone()
343 {
344 }
345 
346 ////////////////////////////////////////
347 //! Internal: the simple form of a MsWksGraph ( line, rect, ...)
348 struct BasicShape final : public Zone {
349   //! constructor
BasicShapeMsWksGraphInternal::BasicShape350   explicit BasicShape(Zone const &z)
351     : Zone(z)
352     , m_shape()
353   {
354   }
355   //! destructor
356   ~BasicShape() final;
357   //! return the type
typeMsWksGraphInternal::BasicShape358   Type type() const final
359   {
360     return Shape;
361   }
362   //! operator<<
printMsWksGraphInternal::BasicShape363   void print(std::ostream &o) const final
364   {
365     Zone::print(o);
366     o << m_shape << ",";
367   }
368   //! return the extra border size
needExtraBorderWidthMsWksGraphInternal::BasicShape369   float needExtraBorderWidth() const final
370   {
371     float res=m_style.m_lineWidth;
372     if (m_shape.m_type==MWAWGraphicShape::Line) {
373       for (const auto &arrow : m_style.m_arrows) {
374         if (!arrow.isEmpty()) res+=4;
375       }
376     }
377     return 0.5f*res;
378   }
379   //! return the shape type
getStyleMsWksGraphInternal::BasicShape380   MWAWGraphicStyle getStyle() const
381   {
382     MWAWGraphicStyle style(m_style);
383     if (m_subType!=0)
384       style.m_arrows[0] = style.m_arrows[1]=MWAWGraphicStyle::Arrow();
385     return style;
386   }
387 
388   //! the basic shape
389   MWAWGraphicShape m_shape;
390 private:
391   BasicShape(BasicShape const &) = delete;
392   BasicShape &operator=(BasicShape const &) = delete;
393 };
394 
~BasicShape()395 BasicShape::~BasicShape()
396 {
397 }
398 
399 ////////////////////////////////////////
400 //! Internal: the table of a MsWksGraph
401 struct Chart final : public Zone {
402   //! constructor
ChartMsWksGraphInternal::Chart403   explicit Chart(Zone const &z)
404     : Zone(z)
405     , m_chartId(0)
406   {
407   }
408   //! empty constructor
ChartMsWksGraphInternal::Chart409   Chart()
410     : Zone()
411     , m_chartId(0)
412   {
413   }
414   //! destructor
415   ~Chart() final;
416 
417   //! return the type
typeMsWksGraphInternal::Chart418   Type type() const final
419   {
420     return ChartZone;
421   }
422   //! the chart id
423   int m_chartId;
424 };
425 
~Chart()426 Chart::~Chart()
427 {
428 }
429 
430 ////////////////////////////////////////
431 //! Internal: the picture of a MsWksGraph
432 struct DataPict final : public Zone {
433   //! constructor
DataPictMsWksGraphInternal::DataPict434   explicit DataPict(Zone const &z)
435     : Zone(z)
436     , m_dataEndPos(-1)
437     , m_naturalBox()
438   {
439   }
440   //! empty constructor
DataPictMsWksGraphInternal::DataPict441   DataPict()
442     : Zone(),
443       m_dataEndPos(-1)
444     , m_naturalBox()
445   {
446   }
447 
448   //! return the type
typeMsWksGraphInternal::DataPict449   Type type() const final
450   {
451     return Pict;
452   }
453   //! return a binary data (if known)
454   bool getBinaryData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture) const final;
455 
456   //! operator<<
printMsWksGraphInternal::DataPict457   void print(std::ostream &o) const final
458   {
459     Zone::print(o);
460   }
461   //! the end of data (only defined when different to m_pos.end())
462   long m_dataEndPos;
463   //! the pict box (if known )
464   mutable MWAWBox2f m_naturalBox;
465 };
466 
getBinaryData(MWAWInputStreamPtr ip,MWAWEmbeddedObject & picture) const467 bool DataPict::getBinaryData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture) const
468 {
469   picture=MWAWEmbeddedObject();
470   long endPos = m_dataEndPos<=0 ? m_pos.end() : m_dataEndPos;
471   long pictSize = endPos-m_dataPos;
472   if (pictSize < 0) {
473     MWAW_DEBUG_MSG(("MsWksGraphInternal::DataPict::getBinaryData: picture size is bad\n"));
474     return false;
475   }
476 
477 #ifdef DEBUG_WITH_FILES
478   if (1) {
479     librevenge::RVNGBinaryData file;
480     ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
481     ip->readDataBlock(pictSize, file);
482     static int volatile pictName = 0;
483     libmwaw::DebugStream f;
484     f << "Pict-" << ++pictName << ".pct";
485     libmwaw::Debug::dumpFile(file, f.str().c_str());
486   }
487 #endif
488 
489   ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
490   auto res = MWAWPictData::check(ip, static_cast<int>(pictSize), m_naturalBox);
491   if (res == MWAWPict::MWAW_R_BAD) {
492     MWAW_DEBUG_MSG(("MsWksGraphInternal::DataPict::getBinaryData: can not find the picture\n"));
493     return false;
494   }
495 
496   ip->seek(m_dataPos, librevenge::RVNG_SEEK_SET);
497   std::shared_ptr<MWAWPict> pict(MWAWPictData::get(ip, static_cast<int>(pictSize)));
498 
499   return pict && pict->getBinary(picture);
500 }
501 
502 ////////////////////////////////////////
503 //! Internal: the bitmap of a MsWksGraph
504 struct DataBitmap final : public Zone {
505   //! constructor
DataBitmapMsWksGraphInternal::DataBitmap506   explicit DataBitmap(Zone const &z)
507     : Zone(z)
508     , m_numRows(0)
509     , m_numCols(0)
510     , m_dataSize(0)
511     , m_naturalBox()
512   {
513   }
514   //! empty constructor
DataBitmapMsWksGraphInternal::DataBitmap515   DataBitmap()
516     : Zone()
517     , m_numRows(0)
518     , m_numCols(0)
519     , m_dataSize(0)
520     , m_naturalBox()
521   {
522   }
523   //! destructor
524   ~DataBitmap() final;
525 
526   //! return the type
typeMsWksGraphInternal::DataBitmap527   Type type() const final
528   {
529     return Bitmap;
530   }
531   //! return a binary data (if known)
532   bool getPictureData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture, std::vector<MWAWColor> const &palette) const;
533 
534   //! operator<<
printMsWksGraphInternal::DataBitmap535   void print(std::ostream &o) const final
536   {
537     o << "nRows=" << m_numRows << ",";
538     o << "nCols=" << m_numCols << ",";
539     if (m_dataSize > 0)
540       o << "dSize=" << std::hex << m_dataSize << std::dec << ",";
541     Zone::print(o);
542   }
543 
544   int m_numRows /** the number of rows*/, m_numCols/** the number of columns*/;
545   long m_dataSize /** the bitmap data size */;
546   //! the pict box (if known )
547   mutable MWAWBox2f m_naturalBox;
548 };
549 
getPictureData(MWAWInputStreamPtr ip,MWAWEmbeddedObject & picture,std::vector<MWAWColor> const & palette) const550 bool DataBitmap::getPictureData(MWAWInputStreamPtr ip, MWAWEmbeddedObject &picture, std::vector<MWAWColor> const &palette) const
551 {
552   picture=MWAWEmbeddedObject();
553   if (m_dataSize <= 0 || m_dataSize < m_numRows*m_numCols) {
554     MWAW_DEBUG_MSG(("MsWksGraphInternal::DataBitmap::getPictureData: dataSize size is bad\n"));
555     return false;
556   }
557   auto szCol = int(m_dataSize/m_numRows);
558   long pos = m_dataPos;
559 
560   MWAWPictBitmapIndexed *btmap = new MWAWPictBitmapIndexed(MWAWVec2i(m_numCols, m_numRows));
561   if (!btmap) return false;
562   btmap->setColors(palette);
563   std::shared_ptr<MWAWPict> pict(btmap);
564   for (int i = 0; i < m_numRows; i++) {
565     ip->seek(pos, librevenge::RVNG_SEEK_SET);
566 
567     unsigned long numRead;
568     uint8_t const *value = ip->read(size_t(m_numCols), numRead);
569     if (!value || int(numRead) != m_numCols) return false;
570     btmap->setRow(i, value);
571 
572     pos += szCol;
573   }
574 
575   return pict->getBinary(picture);
576 }
577 
~DataBitmap()578 DataBitmap::~DataBitmap()
579 {
580 }
581 
582 ////////////////////////////////////////
583 //! Internal: the table of a MsWksGraph
584 struct Table final : public Zone {
585   //! constructor
TableMsWksGraphInternal::Table586   explicit Table(Zone const &z)
587     : Zone(z)
588     , m_tableId(0)
589   {
590   }
591   //! empty constructor
TableMsWksGraphInternal::Table592   Table()
593     : Zone()
594     , m_tableId(0)
595   {
596   }
597   //! destructor
598   ~Table() final;
599 
600   //! return the type
typeMsWksGraphInternal::Table601   Type type() const final
602   {
603     return TableZone;
604   }
605   //! the table id
606   int m_tableId;
607 };
608 
~Table()609 Table::~Table()
610 {
611 }
612 
613 ////////////////////////////////////////
614 //! Internal: the textbox of a MsWksGraph ( v2-v3)
615 struct TextBox final : public Zone {
616   //! constructor
TextBoxMsWksGraphInternal::TextBox617   explicit TextBox(Zone const &z)
618     : Zone(z)
619     , m_numPositions(-1)
620     , m_fontsList()
621     , m_positions()
622     , m_formats()
623     , m_text("")
624     , m_justify(MWAWParagraph::JustificationLeft)
625   { }
626   //! destructor
627   ~TextBox() final;
628   //! return the type
typeMsWksGraphInternal::TextBox629   Type type() const final
630   {
631     return Text;
632   }
633   //! operator<<
printMsWksGraphInternal::TextBox634   void print(std::ostream &o) const final
635   {
636     Zone::print(o);
637     switch (m_justify) {
638     case MWAWParagraph::JustificationLeft:
639       break;
640     case MWAWParagraph::JustificationCenter:
641       o << ",centered";
642       break;
643     case MWAWParagraph::JustificationRight:
644       o << ",right";
645       break;
646     case MWAWParagraph::JustificationFull:
647       o << ",full";
648       break;
649     case MWAWParagraph::JustificationFullAllLines:
650       o << ",fullAllLines";
651       break;
652 #if !defined(__clang__)
653     default:
654       o << ",#just=" << m_justify;
655       break;
656 #endif
657     }
658   }
659 
660   //! add frame parameters to propList (if needed )
fillFrameMsWksGraphInternal::TextBox661   void fillFrame(MWAWGraphicStyle &style) const final
662   {
663     if (!m_style.m_baseSurfaceColor.isWhite())
664       style.setBackgroundColor(m_style.m_baseSurfaceColor);
665   }
666 
667   //! the number of positions
668   int m_numPositions;
669   //! the list of fonts
670   std::vector<MWAWFont> m_fontsList;
671   //! the list of positions
672   std::vector<int> m_positions;
673   //! the list of format
674   std::vector<int> m_formats;
675   //! the text
676   std::string m_text;
677   //! the paragraph alignment
678   MWAWParagraph::Justification m_justify;
679 private:
680   TextBox(TextBox const &) = delete;
681   TextBox &operator=(TextBox const &) = delete;
682 };
683 
~TextBox()684 TextBox::~TextBox()
685 {
686 }
687 
688 ////////////////////////////////////////
689 //! Internal: the ole zone of a MsWksGraph ( v4)
690 struct OLEZone final : public Zone {
691   //! constructor
OLEZoneMsWksGraphInternal::OLEZone692   explicit OLEZone(Zone const &z)
693     : Zone(z)
694     , m_oleId(-1)
695     , m_dim()
696   { }
697   //! destructor
698   ~OLEZone() final;
699   //! return the type
typeMsWksGraphInternal::OLEZone700   Type type() const final
701   {
702     return OLE;
703   }
704   //! operator<<
printMsWksGraphInternal::OLEZone705   void print(std::ostream &o) const final
706   {
707     if (m_oleId >= 0) o << "ole" << m_oleId << ",";
708     if (m_dim[0] > 0 && m_dim[1] > 0) o << "dim=" << m_dim << ",";
709     Zone::print(o);
710   }
711 
712   //! the ole id
713   int m_oleId;
714   //! the dimension
715   MWAWVec2i m_dim;
716 };
717 
~OLEZone()718 OLEZone::~OLEZone()
719 {
720 }
721 
722 ////////////////////////////////////////
723 //! Internal: the textbox of a MsWksGraph ( v4)
724 struct TextBoxv4 final : public Zone {
725   //! constructor
TextBoxv4MsWksGraphInternal::TextBoxv4726   explicit TextBoxv4(Zone const &z)
727     : Zone(z)
728     , m_text()
729     , m_frame("")
730   { }
731   //! destructor
732   ~TextBoxv4() final;
733   //! return the type
typeMsWksGraphInternal::TextBoxv4734   Type type() const final
735   {
736     return Textv4;
737   }
738   //! operator<<
printMsWksGraphInternal::TextBoxv4739   void print(std::ostream &o) const final
740   {
741     Zone::print(o);
742     if (m_text.valid()) o << ", textPos=[" << m_text.begin() << "-" << m_text.end() << "]";
743   }
744 
745   //! add frame parameters to propList (if needed )
fillFrameMsWksGraphInternal::TextBoxv4746   void fillFrame(MWAWGraphicStyle &style) const final
747   {
748     if (!m_style.m_baseSurfaceColor.isWhite())
749       style.setBackgroundColor(m_style.m_baseSurfaceColor);
750   }
751 
752   //! the text of positions (0-0: means no text)
753   MWAWEntry m_text;
754   //! the frame name
755   std::string m_frame;
756 };
757 
758 
~TextBoxv4()759 TextBoxv4::~TextBoxv4()
760 {
761 }
762 
763 //! Internal the pattern ressource of a MsWksGraph
764 struct Pattern {
765   //! constructor ( 4 int by patterns )
PatternMsWksGraphInternal::Pattern766   Pattern(int num, uint16_t const *data)
767     : m_num(num)
768     , m_valuesList()
769     , m_percentList()
770   {
771     if (m_num<=0) return;
772     m_valuesList.resize(size_t(m_num)*8);
773     for (size_t i=0; i < size_t(m_num)*4; ++i) {
774       uint16_t val=data[i];
775       m_valuesList[2*i]=static_cast<unsigned char>(val>>8);
776       m_valuesList[2*i+1]=static_cast<unsigned char>(val&0xFF);
777     }
778     size_t pat=0;
779     for (size_t i=0; i < size_t(num); ++i) {
780       int numOnes=0;
781       for (int j=0; j < 8; ++j) {
782         uint8_t val=m_valuesList[pat++];
783         for (int b=0; b < 8; b++) {
784           if (val&1) ++numOnes;
785           val = uint8_t(val>>1);
786         }
787       }
788       m_percentList.push_back(float(numOnes)/64.f);
789     }
790   }
791   //! return the pattern corresponding to an id
getMsWksGraphInternal::Pattern792   bool get(int id, MWAWGraphicStyle::Pattern &pat) const
793   {
794     if (id < 0 || id >= m_num) {
795       MWAW_DEBUG_MSG(("MsWksGraphInternal::Pattern::get: can not find pattern %d\n", id));
796       return false;
797     }
798     pat.m_dim=MWAWVec2i(8,8);
799     unsigned char const *ptr=&m_valuesList[8*size_t(id)];
800     pat.m_data.resize(8);
801     for (auto &data : pat.m_data)
802       data=*(ptr++);
803     return true;
804   }
805   //! return the percentage corresponding to a pattern
getPercentMsWksGraphInternal::Pattern806   float getPercent(int id) const
807   {
808     if (id < 0 || id >= m_num) {
809       MWAW_DEBUG_MSG(("MsWksGraphInternal::Pattern::getPatternPercent: can not find pattern %d\n", id));
810       return 1.0;
811     }
812     return m_percentList[size_t(id)];
813   }
814 
815   //! the number of patterns
816   int m_num;
817   //! the pattern values (8 data by pattern)
818   std::vector<unsigned char> m_valuesList;
819   //! the pattern percent values
820   std::vector<float> m_percentList;
821 };
822 
823 ////////////////////////////////////////
824 //! Internal: the state of a MsWksGraph
825 struct State {
826   //! constructor
StateMsWksGraphInternal::State827   State()
828     : m_version(-1)
829     , m_leftTopPos(0,0)
830     , m_zonesList()
831     , m_RBsMap()
832     , m_font(20,12)
833     , m_chartId(0)
834     , m_tableId(0)
835     , m_numPages(0)
836     , m_rsrcPatternMap()
837   {
838   }
839   //! return the pattern corresponding to an id
840   bool getPattern(MWAWGraphicStyle::Pattern &pat, int id, long rsid=-1);
841   //! return the percentage corresponding to a pattern
842   float getPatternPercent(int id, long rsid=-1);
843   //! init the pattern value
844   void initPattern(int vers);
845   //! the version
846   int m_version;
847   //! the page left top position in points
848   MWAWVec2f m_leftTopPos;
849   //! the list of zone
850   std::vector<std::shared_ptr<Zone> > m_zonesList;
851   //! the RBIL zone id->list id
852   std::map<int, RBZone> m_RBsMap;
853   //! the actual font
854   MWAWFont m_font;
855   //! an index used to store chart
856   int m_chartId;
857   //! an index used to store table
858   int m_tableId;
859   //! the number of pages
860   int m_numPages;
861   //! a map ressource id -> patterns
862   std::map<long, Pattern> m_rsrcPatternMap;
863 };
864 
initPattern(int vers)865 void State::initPattern(int vers)
866 {
867   if (!m_rsrcPatternMap.empty()) return;
868   if (vers <= 2) {
869     static uint16_t const valuesV2[] = {
870       0xffff, 0xffff, 0xffff, 0xffff,  0xddff, 0x77ff, 0xddff, 0x77ff,  0xdd77, 0xdd77, 0xdd77, 0xdd77,  0xaa55, 0xaa55, 0xaa55, 0xaa55,
871       0x55ff, 0x55ff, 0x55ff, 0x55ff,  0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,  0xeedd, 0xbb77, 0xeedd, 0xbb77,  0x8888, 0x8888, 0x8888, 0x8888,
872       0xb130, 0x031b, 0xd8c0, 0x0c8d,  0x8010, 0x0220, 0x0108, 0x4004,  0xff88, 0x8888, 0xff88, 0x8888,  0xff80, 0x8080, 0xff08, 0x0808,
873       0x0000, 0x0002, 0x0000, 0x0002,  0x8040, 0x2000, 0x0204, 0x0800,  0x8244, 0x3944, 0x8201, 0x0101,  0xf874, 0x2247, 0x8f17, 0x2271,
874       0x55a0, 0x4040, 0x550a, 0x0404,  0x2050, 0x8888, 0x8888, 0x0502,  0xbf00, 0xbfbf, 0xb0b0, 0xb0b0,  0x0000, 0x0000, 0x0000, 0x0000,
875       0x8000, 0x0800, 0x8000, 0x0800,  0x8800, 0x2200, 0x8800, 0x2200,  0x8822, 0x8822, 0x8822, 0x8822,  0xaa00, 0xaa00, 0xaa00, 0xaa00,
876       0x00ff, 0x00ff, 0x00ff, 0x00ff,  0x1122, 0x4488, 0x1122, 0x4488,  0x8040, 0x2000, 0x0204, 0x0800,  0x0102, 0x0408, 0x1020, 0x4080,
877       0xaa00, 0x8000, 0x8800, 0x8000,  0xff80, 0x8080, 0x8080, 0x8080,  0x0814, 0x2241, 0x8001, 0x0204,  0x8814, 0x2241, 0x8800, 0xaa00,
878       0x40a0, 0x0000, 0x040a, 0x0000,  0x0384, 0x4830, 0x0c02, 0x0101,  0x8080, 0x413e, 0x0808, 0x14e3,  0x1020, 0x54aa, 0xff02, 0x0408,
879       0x7789, 0x8f8f, 0x7798, 0xf8f8,  0x0008, 0x142a, 0x552a, 0x1408,  0x0000, 0x0000, 0x0000, 0x0000,
880     };
881     m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(-1,Pattern(39, valuesV2)));
882   }
883   static uint16_t const values4002[] = {
884     0xffff, 0xffff, 0xffff, 0xffff,  0x7fff, 0xffff, 0xf7ff, 0xffff,  0x7fff, 0xf7ff, 0x7fff, 0xf7ff,  0x77ff, 0xddff, 0x77ff, 0xddff,
885     0x55ff, 0xddff, 0x55ff, 0xddff,  0x55ff, 0xeeff, 0x55ff, 0xbbff,  0x55ff, 0x55ff, 0x55ff, 0x55ff,  0x77dd, 0x77dd, 0x77dd, 0x77dd,
886     0x55bf, 0x55ff, 0x55fb, 0x55ff,  0x55bb, 0x55ff, 0x55bb, 0x55ff,  0x55bf, 0x55ee, 0x55fb, 0x55ee,  0x55bb, 0x55ee, 0x55bb, 0x55ee,
887     0x55bb, 0x55ea, 0x55bb, 0x55ae,  0x55ba, 0x55ab, 0x55ba, 0x55ab,  0x55ea, 0x55aa, 0x55ae, 0x55aa,  0xaa55, 0xaa55, 0xaa55, 0xaa55,
888     0xaa15, 0xaa55, 0xaa51, 0xaa55,  0xaa45, 0xaa54, 0xaa45, 0xaa54,  0xaa44, 0xaa15, 0xaa44, 0xaa51,  0xaa44, 0xaa11, 0xaa44, 0xaa11,
889     0xaa40, 0xaa11, 0xaa04, 0xaa11,  0xaa44, 0xaa00, 0xaa44, 0xaa00,  0xaa40, 0xaa00, 0xaa04, 0xaa00,  0x8822, 0x8822, 0x8822, 0x8822,
890     0xaa00, 0xaa00, 0xaa00, 0xaa00,  0xaa00, 0x1100, 0xaa00, 0x4400,  0xaa00, 0x2200, 0xaa00, 0x2200,  0x8800, 0x2200, 0x8800, 0x2200,
891     0x8800, 0x2000, 0x8800, 0x0200,  0x8000, 0x0800, 0x8000, 0x0800,  0x8000, 0x0000, 0x0800, 0x0000,  0x0000, 0x0000, 0x0000, 0x0000
892   };
893   m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4002,Pattern(32, values4002)));
894   static uint16_t const values4003[] = {
895     0x0000, 0x0000, 0x0000, 0x0000,  0x8000, 0x0000, 0x0800, 0x0000,  0x8000, 0x0800, 0x8000, 0x0800,  0x8800, 0x2000, 0x8800, 0x0200,
896     0x8800, 0x2200, 0x8800, 0x2200,  0xaa00, 0x2200, 0xaa00, 0x2200,  0xaa00, 0x1100, 0xaa00, 0x4400,  0xaa00, 0xaa00, 0xaa00, 0xaa00,
897     0x8822, 0x8822, 0x8822, 0x8822,  0xaa44, 0xaa11, 0xaa44, 0xaa11,  0xaa45, 0xaa54, 0xaa45, 0xaa54,  0xaa55, 0xaa55, 0xaa55, 0xaa55,
898     0x55ea, 0x55aa, 0x55ae, 0x55aa,  0x55ba, 0x55ab, 0x55ba, 0x55ab,  0x55bb, 0x55ee, 0x55bb, 0x55ee,  0x77dd, 0x77dd, 0x77dd, 0x77dd,
899     0x55ff, 0x55ff, 0x55ff, 0x55ff,  0x55ff, 0xeeff, 0x55ff, 0xbbff,  0x77ff, 0xddff, 0x77ff, 0xddff,  0x7fff, 0xf7ff, 0x7fff, 0xf7ff,
900     0x7fff, 0xffff, 0xf7ff, 0xffff,  0xffff, 0xffff, 0xffff, 0xffff
901   };
902   m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4003,Pattern(22, values4003)));
903   static uint16_t const values4004[] = {
904     0xf0f0, 0xf0f0, 0x0f0f, 0x0f0f,  0xcccc, 0x3333, 0xcccc, 0x3333,  0x3333, 0xcccc, 0x3333, 0xcccc
905   };
906   m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(4004,Pattern(3, values4004)));
907   static uint16_t const values7000[] = {
908     0x0101, 0x1010, 0x0101, 0x1010,  0xcc00, 0x0000, 0x3300, 0x0000,  0x1122, 0x4400, 0x1122, 0x4400,  0x4422, 0x0088, 0x4422, 0x0088,
909     0xf0f0, 0xf0f0, 0x0f0f, 0x0f0f,  0x9966, 0x6699, 0x9966, 0x6699,  0x0008, 0x1c3e, 0x7f3e, 0x1c08,  0x0008, 0x142a, 0x552a, 0x1408,
910     0xb130, 0x031b, 0xd8c0, 0x0c8d,  0x8010, 0x0220, 0x0108, 0x4004,  0x0814, 0x2241, 0x8001, 0x0204,  0x80c0, 0x2112, 0x0c04, 0x0201,
911     0xff80, 0x8080, 0xff08, 0x0808,  0x007f, 0x7f7f, 0x00f7, 0xf7f7,  0x8040, 0x2000, 0x0204, 0x0800,  0x8244, 0x3944, 0x8201, 0x0101,
912     0xf078, 0x2442, 0x870f, 0x1221,  0x1020, 0x54aa, 0xff02, 0x0408,  0xf874, 0x2247, 0x8f17, 0x2271,  0xbfa0, 0xbfbd, 0xbdfd, 0x05fd,
913     0x2050, 0x8888, 0x8888, 0x0502,  0x55a0, 0x4040, 0x550a, 0x0404,  0x8844, 0x2211, 0x1122, 0x4488,  0x8142, 0x2418, 0x8142, 0x2418,
914     0xaa00, 0x8000, 0x8800, 0x8000,  0x0384, 0x4830, 0x0c02, 0x0101,  0x8080, 0x413e, 0x0808, 0x14e3,  0xaf5f, 0xaf5f, 0x0d0b, 0x0d0b,
915     0x7789, 0x8f8f, 0x7798, 0xf8f8,  0x8814, 0x2241, 0x8800, 0xaa00,  0x40a0, 0x0000, 0x040a, 0x0000,  0xbf00, 0xbfbf, 0xb0b0, 0xb0b0
916   };
917   m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(7000,Pattern(32, values7000)));
918   static uint16_t const values14001[] = {
919     0x8844, 0x2211, 0x8844, 0x2211,  0x77bb, 0xddee, 0x77bb, 0xddee,  0x1122, 0x4488, 0x1122, 0x4488,  0xeedd, 0xbb77, 0xeedd, 0xbb77,
920     0x8040, 0x2010, 0x0804, 0x0201,  0x7fbf, 0xdfef, 0xf7fb, 0xfdfe,  0x0102, 0x0408, 0x1020, 0x4080,  0xfefd, 0xfbf7, 0xefdf, 0xbf7f,
921     0xe070, 0x381c, 0x0e07, 0x83c1,  0x99cc, 0x6633, 0x99cc, 0x6633,  0x8307, 0x0e1c, 0x3870, 0xe0c1,  0x3366, 0xcc99, 0x3366, 0xcc99,
922     0x8142, 0x2418, 0x1824, 0x4281,  0x7ebd, 0xdbe7, 0xe7db, 0xbd7e,  0x8244, 0x2810, 0x2844, 0x8201,  0x7dbb, 0xd7ef, 0xd7bb, 0x7dfe,
923     0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa,  0x00ff, 0x00ff, 0x00ff, 0x00ff,  0x8888, 0x8888, 0x8888, 0x8888,  0x7777, 0x7777, 0x7777, 0x7777,
924     0xff00, 0x0000, 0xff00, 0x0000,  0x00ff, 0xffff, 0x00ff, 0xffff,  0x8080, 0x8080, 0x8080, 0x8080,  0x7f7f, 0x7f7f, 0x7f7f, 0x7f7f,
925     0xff00, 0x0000, 0x0000, 0x0000,  0x00ff, 0xffff, 0xffff, 0xffff,  0xcccc, 0xcccc, 0xcccc, 0xcccc,  0xffff, 0x0000, 0xffff, 0x0000,
926     0xff88, 0x8888, 0xff88, 0x8888,  0x0077, 0x7777, 0x0077, 0x7777,  0xff80, 0x8080, 0x8080, 0x8080,  0x007f, 0x7f7f, 0x7f7f, 0x7f7f
927   };
928   m_rsrcPatternMap.insert(std::map<long, Pattern>::value_type(14001,Pattern(32, values14001)));
929 }
930 
getPatternPercent(int id,long rsid)931 float State::getPatternPercent(int id, long rsid)
932 {
933   if (m_rsrcPatternMap.empty())
934     initPattern(m_version);
935   if (m_rsrcPatternMap.find(rsid)==m_rsrcPatternMap.end()) {
936     MWAW_DEBUG_MSG(("MsWksGraphInternal::State::getPatternPercent unknown map for rsdid=%ld\n",rsid));
937     return 1.0;
938   }
939   return m_rsrcPatternMap.find(rsid)->second.getPercent(id);
940 }
941 
getPattern(MWAWGraphicStyle::Pattern & pat,int id,long rsid)942 bool State::getPattern(MWAWGraphicStyle::Pattern &pat, int id, long rsid)
943 {
944   if (m_rsrcPatternMap.empty())
945     initPattern(m_version);
946   if (m_rsrcPatternMap.find(rsid)==m_rsrcPatternMap.end()) {
947     MWAW_DEBUG_MSG(("MsWksGraphInternal::State::getPattern unknown map for rsdid=%ld\n",rsid));
948     return false;
949   }
950   return m_rsrcPatternMap.find(rsid)->second.get(id, pat);
951 }
952 
953 ////////////////////////////////////////
954 //! Internal: the subdocument of a MsWksGraph
955 class SubDocument final : public MWAWSubDocument
956 {
957 public:
958   enum Type { RBILZone, Chart, Empty, Group, Table, TextBox, TextBoxv4 };
SubDocument(MsWksGraph & pars,MWAWInputStreamPtr const & input,Type type,int zoneId)959   SubDocument(MsWksGraph &pars, MWAWInputStreamPtr const &input, Type type, int zoneId)
960     : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
961     , m_graphParser(&pars)
962     , m_type(type)
963     , m_id(zoneId)
964     , m_frame("")
965   {
966   }
SubDocument(MsWksGraph & pars,MWAWInputStreamPtr const & input,Type type,MWAWEntry const & entry,std::string const & frame=std::string (""))967   SubDocument(MsWksGraph &pars, MWAWInputStreamPtr const &input, Type type,
968               MWAWEntry const &entry, std::string const &frame=std::string(""))
969     : MWAWSubDocument(pars.m_mainParser, input, entry)
970     , m_graphParser(&pars)
971     , m_type(type)
972     , m_id(-1)
973     , m_frame(frame)
974   {
975   }
976 
977   //! destructor
~SubDocument()978   ~SubDocument() final {}
979 
980   //! operator!=
981   bool operator!=(MWAWSubDocument const &doc) const final;
982 
983   //! the parser function
984   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
985 private:
986   SubDocument(SubDocument const &orig) = delete;
987   SubDocument &operator=(SubDocument const &orig) = delete;
988 
989 protected:
990   /** the graph parser */
991   MsWksGraph *m_graphParser;
992   /** the type */
993   Type m_type;
994   /** the subdocument id*/
995   int m_id;
996   /** the frame name: for textv4 */
997   std::string m_frame;
998 };
999 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)1000 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
1001 {
1002   if (!listener.get()) {
1003     MWAW_DEBUG_MSG(("MsWksGraphInternal::SubDocument::parse: no listener\n"));
1004     return;
1005   }
1006   if (!m_graphParser) {
1007     MWAW_DEBUG_MSG(("MsWksGraphInternal::SubDocument::parse: no parser\n"));
1008     return;
1009   }
1010 
1011   long pos = m_input->tell();
1012   switch (m_type) {
1013   case Empty:
1014     break;
1015   case Chart:
1016     m_graphParser->sendChart(m_id);
1017     break;
1018   case Group: {
1019     MWAWPosition gPos;
1020     gPos.setRelativePosition(MWAWPosition::Frame,
1021                              MWAWPosition::XLeft, MWAWPosition::YTop);
1022     m_graphParser->sendGroupChild(m_id, gPos);
1023     break;
1024   }
1025   case Table:
1026     m_graphParser->sendTable(m_id);
1027     break;
1028   case TextBox:
1029     m_graphParser->sendTextBox(m_id, listener);
1030     break;
1031   case TextBoxv4:
1032     m_graphParser->sendFrameText(m_zone, m_frame);
1033     break;
1034   case RBILZone: {
1035     MsWksGraph::SendData sendData;
1036     sendData.m_type = MsWksGraph::SendData::RBIL;
1037     sendData.m_id = m_id;
1038     sendData.m_anchor =  MWAWPosition::Frame;
1039     m_graphParser->sendObjects(sendData);
1040     break;
1041   }
1042 #if !defined(__clang__)
1043   default:
1044     MWAW_DEBUG_MSG(("MsWksGraph::SubDocument::parse: unexpected zone type\n"));
1045     break;
1046 #endif
1047   }
1048   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
1049 }
1050 
operator !=(MWAWSubDocument const & doc) const1051 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
1052 {
1053   if (MWAWSubDocument::operator!=(doc)) return true;
1054   auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
1055   if (!sDoc) return true;
1056   if (m_graphParser != sDoc->m_graphParser) return true;
1057   if (m_id != sDoc->m_id) return true;
1058   if (m_type != sDoc->m_type) return true;
1059   if (m_frame != sDoc->m_frame) return true;
1060   return false;
1061 }
1062 
1063 }
1064 
1065 ////////////////////////////////////////////////////////////
1066 // constructor/destructor, ...
1067 ////////////////////////////////////////////////////////////
MsWksGraph(MsWksDocument & document)1068 MsWksGraph::MsWksGraph(MsWksDocument &document)
1069   : m_parserState()
1070   , m_state(new MsWksGraphInternal::State)
1071   , m_mainParser(&document.getMainParser())
1072   , m_document(document)
1073   , m_tableParser()
1074 {
1075   m_parserState=m_mainParser->getParserState();
1076   m_tableParser.reset(new MsWksTable(*m_mainParser, m_document, *this));
1077 }
1078 
~MsWksGraph()1079 MsWksGraph::~MsWksGraph()
1080 {
1081 }
1082 
setPageLeftTop(MWAWVec2f const & leftTop)1083 void MsWksGraph::setPageLeftTop(MWAWVec2f const &leftTop)
1084 {
1085   m_state->m_leftTopPos=leftTop;
1086 }
1087 
version() const1088 int MsWksGraph::version() const
1089 {
1090   if (m_state->m_version < 0)
1091     m_state->m_version = m_parserState->m_version;
1092   return m_state->m_version;
1093 }
1094 
numPages(int zoneId) const1095 int MsWksGraph::numPages(int zoneId) const
1096 {
1097   if (m_state->m_numPages > 0)
1098     return m_state->m_numPages;
1099 
1100   int maxPage = 0;
1101   for (auto zone : m_state->m_zonesList) {
1102     if (zoneId >= 0 && zone->m_zoneId!=zoneId) continue;
1103     if (zone->m_page > maxPage)
1104       maxPage = zone->m_page;
1105   }
1106   m_state->m_numPages = maxPage+1;
1107   return m_state->m_numPages;
1108 }
1109 
sendFrameText(MWAWEntry const & entry,std::string const & frame)1110 void MsWksGraph::sendFrameText(MWAWEntry const &entry, std::string const &frame)
1111 {
1112   m_document.sendTextbox(entry, frame);
1113 }
1114 
sendChart(int zoneId)1115 void MsWksGraph::sendChart(int zoneId)
1116 {
1117   m_tableParser->sendChart(zoneId);
1118 }
1119 
sendTable(int zoneId)1120 void MsWksGraph::sendTable(int zoneId)
1121 {
1122   m_tableParser->sendTable(zoneId);
1123 }
1124 
1125 ////////////////////////////////////////////////////////////
1126 //
1127 // Intermediate level
1128 //
1129 ////////////////////////////////////////////////////////////
getZoneGraphicStyle(int id,MWAWGraphicStyle & style) const1130 bool MsWksGraph::getZoneGraphicStyle(int id, MWAWGraphicStyle &style) const
1131 {
1132   if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)]) {
1133     MWAW_DEBUG_MSG(("MsWksGraph::getZoneGraphicStyle: unknown zone %d\n", id));
1134     return false;
1135   }
1136   style = m_state->m_zonesList[size_t(id)]->m_style;
1137   return true;
1138 }
1139 
getZonePosition(int id,MWAWPosition::AnchorTo anchor,MWAWPosition & pos) const1140 bool MsWksGraph::getZonePosition(int id, MWAWPosition::AnchorTo anchor, MWAWPosition &pos) const
1141 {
1142   if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)]) {
1143     MWAW_DEBUG_MSG(("MsWksGraph::getZoneGraphicStyle: unknown zone %d\n", id));
1144     return false;
1145   }
1146   pos = m_state->m_zonesList[size_t(id)]->getPosition(anchor);
1147   return true;
1148 }
1149 
readPictHeader(MsWksGraphInternal::Zone & pict)1150 bool MsWksGraph::readPictHeader(MsWksGraphInternal::Zone &pict)
1151 {
1152   MWAWInputStreamPtr input=m_document.getInput();
1153   long pos=input->tell();
1154   int vers = version();
1155   if (!input->checkPosition(pos+(vers>=3 ? 54 : 38)) || input->readULong(1) != 0) return false;
1156   pict = MsWksGraphInternal::Zone();
1157   pict.m_subType = static_cast<int>(input->readULong(1));
1158   if (pict.m_subType > 0x10 || pict.m_subType == 6 || pict.m_subType == 0xb)
1159     return false;
1160   if (pict.m_subType>9) {
1161     if (vers<=2)
1162       return false;
1163     // we can find chart in v3 spreadsheet file
1164     else if (vers==3 && pict.m_subType != 0xa && m_parserState->m_type!=MWAWParserState::Spreadsheet)
1165       return false;
1166   }
1167   libmwaw::DebugStream f;
1168   int val;
1169   if (vers >= 3) {
1170     val = static_cast<int>(input->readLong(2));
1171     if (vers == 4 || m_parserState->m_type==MWAWParserState::Graphic)
1172       pict.m_page = val==0 ? -2 : val-1;
1173     else if (val)
1174       f << "f0=" << val << ",";
1175   }
1176   // color
1177   Style &style=pict.m_style;
1178   for (int i = 0; i < 2; i++) {
1179     auto rId = static_cast<int>(input->readLong(2));
1180     int cId = (vers <= 2) ? rId+1 : rId;
1181     MWAWColor col;
1182     if (m_document.getColor(cId,col,vers <= 3 ? vers : 3)) {
1183       if (i) style.m_baseSurfaceColor = col;
1184       else style.m_baseLineColor = col;
1185     }
1186     else
1187       f << "#col" << i << "=" << rId << ",";
1188   }
1189   bool hasSurfPatFunction=false;
1190   if (vers <= 2) {
1191     for (int i = 0; i < 2; i++) {
1192       auto pId = static_cast<int>(input->readLong(2));
1193       if (pId==38) { // empty
1194         if (i==0)
1195           style.m_lineWidth=0;
1196         continue;
1197       }
1198       float percent = m_state->getPatternPercent(pId);
1199       MWAWGraphicStyle::Pattern pattern;
1200       if (i==0)
1201         style.m_lineColor=MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor);
1202       else if (m_state->getPattern(pattern, pId)) {
1203         pattern.m_colors[0] = style.m_baseSurfaceColor;
1204         pattern.m_colors[1] = style.m_baseLineColor;
1205         style.setPattern(pattern);
1206       }
1207       else
1208         style.setSurfaceColor(MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor));
1209     }
1210     auto lineType=static_cast<int>(input->readLong(2));
1211     if (style.m_lineWidth>0) {
1212       switch (lineType) {
1213       case 0:
1214         style.m_lineWidth=0.;
1215         break;
1216       case 1:
1217         style.m_lineWidth=0.5;
1218         break;
1219       case 2: // lineW=1
1220         break;
1221       case 3:
1222         style.m_lineWidth=2;
1223         break;
1224       case 4:
1225         style.m_lineWidth=4;
1226         break;
1227       default:
1228         f << "#lineType=" << lineType << ",";
1229         break;
1230       }
1231     }
1232   }
1233   else {
1234     style.m_lineColor=style.m_baseLineColor;
1235     style.m_surfaceColor=style.m_baseSurfaceColor;
1236     for (int i = 0; i < 2; i++) {
1237       if (i) f << "surface";
1238       else f << "line";
1239       f << "Pattern=[";
1240       long rsid= input->readLong(2);
1241       if (rsid==0) f << "noColor,";
1242       else if (rsid==-1) f << "grad,";
1243       else f << "rsid=" << rsid << ",";
1244       auto patId = static_cast<int>(input->readULong(2));
1245       if (patId) f << "pat=" << patId << ",";
1246       else f << "_";
1247       if (vers==4 && rsid==-1 && patId==0xFFFF)
1248         hasSurfPatFunction=true;
1249       val = static_cast<int>(input->readLong(1));
1250       if (val) f << "unkn=" << val << ",";
1251       auto per = static_cast<int>(input->readULong(1));
1252       f << per << "%,";
1253       if (rsid<=0) {
1254         if (i==0 && rsid==0)
1255           style.m_lineWidth=0.;
1256       }
1257       else {
1258         float percent=1.0;
1259         bool done=false;
1260         MWAWGraphicStyle::Pattern pattern;
1261         if (per >= 0 && per < 100)
1262           percent = float(per)/100.f;
1263         else if (m_state->getPattern(pattern, patId, rsid)) {
1264           percent = m_state->getPatternPercent(patId, rsid);
1265           if (i) {
1266             pattern.m_colors[0] = style.m_baseSurfaceColor;
1267             pattern.m_colors[1] = style.m_baseLineColor;
1268             style.setPattern(pattern);
1269             done = true;
1270           }
1271         }
1272         else {
1273           MWAW_DEBUG_MSG(("MsWksGraph::readPictHeader:find odd pattern\n"));
1274           f << "##";
1275         }
1276         if (done) {
1277         }
1278         else if (i==0)
1279           style.m_lineColor=MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor);
1280         else
1281           style.setSurfaceColor(MWAWColor::barycenter(percent, style.m_baseLineColor, 1.f-percent, style.m_baseSurfaceColor));
1282       }
1283       f << "],";
1284     }
1285     int penSize[2];
1286     for (auto &pSize : penSize) pSize = static_cast<int>(input->readLong(2));
1287     if (style.m_lineWidth<=0)
1288       f << "pen=" << penSize[0] << "x" << penSize[1] << ",";
1289     else if (penSize[0]==penSize[1])
1290       style.m_lineWidth=float(penSize[0]);
1291     else {
1292       f << "pen=" << penSize[0] << "x" << penSize[1] << ",";
1293       style.m_lineWidth=0.5f*float(penSize[0]+penSize[1]);
1294     }
1295     if (style.m_lineWidth < 0 || style.m_lineWidth > 10) {
1296       f << "##penSize=" << style.m_lineWidth << ",";
1297       style.m_lineWidth = 1;
1298     }
1299     val = static_cast<int>(input->readLong(2));
1300     if (val)
1301       f << "f1=" << val << ",";
1302   }
1303 
1304   float offset[4];
1305   for (auto &off : offset) off = float(input->readLong(2));
1306   pict.m_decal = MWAWBox2f(MWAWVec2f(offset[0],offset[1]), MWAWVec2f(offset[3],offset[2]));
1307   pict.m_finalDecal = MWAWVec2f(float(offset[0]+offset[3]), float(offset[1]+offset[2]));
1308 
1309   // the two point which allows to create the form ( in general the bdbox)
1310   float dim[4];
1311   for (auto &d : dim) d = float(input->readLong(4))/65536.f;
1312   pict.m_box=MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
1313 
1314   auto flags = static_cast<int>(input->readLong(1));
1315   // 2: rotations, 1:lock ?, 0: nothing, other ?
1316   if (vers >= 4 && (flags&1)) {
1317     f << "locked,";
1318     flags &= 0xFE;
1319   }
1320   if (vers >= 4 && (flags&2)) {
1321     f << "Rot=[";
1322     for (int i = 0; i < 32; i++)
1323       f << input->readLong(2) << ",";
1324     f << "],";
1325     flags &= 0xFC;
1326   }
1327   if (flags) f << "fl0=" << flags << ",";
1328   auto lineFlags = static_cast<int>(input->readULong(1));
1329   switch (lineFlags&3) {
1330   case 2:
1331     style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
1332     MWAW_FALLTHROUGH;
1333   case 1:
1334     style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
1335     break;
1336   default:
1337     f << "#arrow=3,";
1338     break;
1339   case 0:
1340     break;
1341   }
1342   if (lineFlags&0xFC) f << "#lineFlags=" << std::hex << (lineFlags&0xFC) << std::dec << ",";
1343   if (vers >= 3) pict.m_ids[0] = long(input->readULong(4));
1344   if (vers >= 4 && hasSurfPatFunction) {
1345     pos = input->tell();
1346     if (!readGradient(style)) {
1347       f << "##gradient,";
1348       input->seek(pos, librevenge::RVNG_SEEK_SET);
1349     }
1350   }
1351   pict.m_extra = f.str();
1352   pict.m_dataPos = input->tell();
1353   return true;
1354 }
1355 
readGradient(MsWksGraph::Style & style)1356 bool MsWksGraph::readGradient(MsWksGraph::Style &style)
1357 {
1358   MWAWInputStreamPtr input=m_document.getInput();
1359   long pos = input->tell();
1360 
1361   if (!input->checkPosition(pos+22))
1362     return false;
1363 
1364   libmwaw::DebugStream f;
1365   f << "gradient[unknown]=[";
1366   auto type=static_cast<int>(input->readLong(2));
1367   auto val=static_cast<int>(input->readLong(2)); // always 0?
1368   if (val) f << "f0=" << val << ",";
1369   val=static_cast<int>(input->readLong(1)); // always 8?
1370   if (val!=8) f << "f1=" << val << ",";
1371   val=static_cast<int>(input->readLong(2)); // find 1 in square
1372   if (val) f << "f2=" << val << ",";
1373   val=static_cast<int>(input->readULong(2)); // always 0 ?
1374   if (val) f << "f3=" << std::hex << val << std::dec << ",";
1375   auto angle =static_cast<int>(input->readLong(2));
1376   val=static_cast<int>(input->readLong(2)); // 89[square]|156[square:linearbi]|255
1377   if (val!=0xff) f << "f4=" << val << ",";
1378   val=static_cast<int>(input->readLong(2)); // 54[square]|0
1379   if (val) f << "f5=" << val << ",";
1380   val=static_cast<int>(input->readLong(2)); // 18
1381   if (val!=0x18) f << "f6=" << val << ",";
1382   val=static_cast<int>(input->readULong(2));
1383   int subType = (val&0xf);
1384   val = (val>>4);
1385   if (val!=0xFF)
1386     f << "sType[high]=" << std::hex << val << std::dec << ",";
1387   val=static_cast<int>(input->readLong(2)); // 0
1388   if (val) f << "f7=" << val << ",";
1389   val=static_cast<int>(input->readLong(1)); // 0
1390   if (val) f << "f8=" << val << ",";
1391   f << "],";
1392   auto &finalGrad=style.m_gradient;
1393   switch (type) {
1394   case 1:
1395     finalGrad.m_stopList.resize(2);
1396     finalGrad.m_stopList[0]=MWAWGraphicStyle::Gradient::Stop(0.0, style.m_baseSurfaceColor);
1397     finalGrad.m_stopList[1]=MWAWGraphicStyle::Gradient::Stop(1.0, style.m_baseLineColor);
1398     finalGrad.m_angle = float(90+angle);
1399     finalGrad.m_type = MWAWGraphicStyle::Gradient::G_Linear;
1400     angle=type=0;
1401     break;
1402   case 2:
1403     finalGrad.m_stopList.resize(2);
1404     finalGrad.m_stopList[0]=MWAWGraphicStyle::Gradient::Stop(0.0, style.m_baseSurfaceColor);
1405     finalGrad.m_stopList[1]=MWAWGraphicStyle::Gradient::Stop(1.0, style.m_baseLineColor);
1406     finalGrad.m_angle = float(90+angle);
1407     finalGrad.m_type = MWAWGraphicStyle::Gradient::G_Axial;
1408     angle=type=0;
1409     break;
1410   case 3:
1411     finalGrad.m_stopList.resize(2);
1412     finalGrad.m_stopList[0]=MWAWGraphicStyle::Gradient::Stop(0.0, style.m_baseSurfaceColor);
1413     finalGrad.m_stopList[1]=MWAWGraphicStyle::Gradient::Stop(1.0, style.m_baseLineColor);
1414     switch (subType) {
1415     case 9:
1416       finalGrad.m_percentCenter=MWAWVec2f(0.25f,0.25f);
1417       break;
1418     case 10:
1419       finalGrad.m_percentCenter=MWAWVec2f(0.25f,0.75f);
1420       break;
1421     case 11:
1422       finalGrad.m_percentCenter=MWAWVec2f(0.75f,0.75f);
1423       break;
1424     case 12:
1425       finalGrad.m_percentCenter=MWAWVec2f(1.f,1.f);
1426       break;
1427     case 13:
1428       finalGrad.m_percentCenter=MWAWVec2f(0.f,0.f);
1429       break;
1430     default:
1431       f << "#subType=" << subType << ",";
1432       break;
1433     case 8: // centered
1434       break;
1435     }
1436     finalGrad.m_type = MWAWGraphicStyle::Gradient::G_Rectangular;
1437     angle=type=0;
1438     break;
1439   case 7:
1440     finalGrad.m_stopList.resize(2);
1441     finalGrad.m_stopList[0]=MWAWGraphicStyle::Gradient::Stop(0.0, style.m_baseSurfaceColor);
1442     finalGrad.m_stopList[1]=MWAWGraphicStyle::Gradient::Stop(1.0, style.m_baseLineColor);
1443     finalGrad.m_type = MWAWGraphicStyle::Gradient::G_Radial;
1444     type = 0;
1445     break;
1446   default:
1447     break;
1448   }
1449   if (type) f << "#type=" << type << ",";
1450   if (angle) f << "#angle=" << angle << ",";
1451   f << "subType=" << subType << ",";
1452   f << "],";
1453   style.m_extra = f.str();
1454   return true;
1455 }
1456 
getEntryPicture(int zoneId,MWAWEntry & zone,bool autoSend,int order)1457 int MsWksGraph::getEntryPicture(int zoneId, MWAWEntry &zone, bool autoSend, int order)
1458 {
1459   MsWksGraphInternal::Zone pict;
1460   MWAWInputStreamPtr input=m_document.getInput();
1461   long pos = input->tell();
1462 
1463   if (!readPictHeader(pict))
1464     return -1;
1465   pict.m_zoneId = zoneId;
1466   pict.m_pos.setBegin(pos);
1467   libmwaw::DebugFile &ascFile = m_document.ascii();
1468   libmwaw::DebugStream f;
1469   int vers = version();
1470   long debData = input->tell();
1471   long dataSize = 0;
1472   int versSize = 0;
1473   switch (pict.m_subType) {
1474   case 0:
1475   case 1:
1476   case 2:
1477   case 3:
1478     dataSize = 1;
1479     break;
1480   case 4: // arc
1481     dataSize = 0xd;
1482     break;
1483   case 5: { // poly
1484     input->seek(3, librevenge::RVNG_SEEK_CUR);
1485     auto N = static_cast<int>(input->readULong(2));
1486     dataSize = 9+N*8;
1487     break;
1488   }
1489   case 7: { // picture
1490     if (vers >= 3) versSize = 0x14;
1491     dataSize = 5;
1492     input->seek(debData+5+versSize-2, librevenge::RVNG_SEEK_SET);
1493     dataSize += static_cast<int>(input->readULong(2));
1494     break;
1495   }
1496   case 8: // group
1497     if (vers >= 3) versSize = 4;
1498     dataSize = 0x1b;
1499     break;
1500   case 9: // textbox v<=3
1501     dataSize = 0x21;
1502     if (vers >= 3) dataSize += 0x10;
1503     break;
1504   case 0xa: // chart v4
1505     dataSize = 50;
1506     break;
1507   case 0xc: // equation v4
1508     dataSize = 0x11;
1509     break;
1510   case 0xd: { // bitmap v4
1511     input->seek(debData+0x29, librevenge::RVNG_SEEK_SET);
1512     auto sz = long(input->readULong(4));
1513     dataSize = 0x29+4+sz;
1514     break;
1515   }
1516   case 0xe: { // spreadsheet v4
1517     input->seek(debData+0xa7, librevenge::RVNG_SEEK_SET);
1518     auto pSize = static_cast<int>(input->readULong(2));
1519     if (pSize == 0) return -1;
1520     dataSize = 0xa9+pSize;
1521     if (!input->checkPosition(debData+dataSize))
1522       return -1;
1523 
1524     input->seek(debData+dataSize, librevenge::RVNG_SEEK_SET);
1525     for (int i = 0; i < 2; i++) {
1526       auto sz = long(input->readULong(4));
1527       if (sz<0 || (sz>>28)) return -1;
1528       dataSize += 4 + sz;
1529       input->seek(sz, librevenge::RVNG_SEEK_CUR);
1530     }
1531     break;
1532   }
1533   case 0xf: { // textbox v4
1534     input->seek(debData+0x39, librevenge::RVNG_SEEK_SET);
1535     dataSize = 0x3b+ long(input->readULong(2));
1536     break;
1537   }
1538   case 0x10: { // table v4
1539     input->seek(debData+0x57, librevenge::RVNG_SEEK_SET);
1540     dataSize = 0x59+ long(input->readULong(2));
1541     input->seek(debData+dataSize, librevenge::RVNG_SEEK_SET);
1542 
1543     for (int i = 0; i < 3; i++) {
1544       auto sz = long(input->readULong(4));
1545       if (sz<0 || ((sz>>28))) return -1;
1546       dataSize += 4 + sz;
1547       input->seek(sz, librevenge::RVNG_SEEK_CUR);
1548     }
1549 
1550     break;
1551   }
1552   default:
1553     MWAW_DEBUG_MSG(("MsWksGraph::getEntryPicture: type %d is not umplemented\n", pict.m_subType));
1554     return -1;
1555   }
1556 
1557   pict.m_pos.setEnd(debData+dataSize+versSize);
1558   if (!input->checkPosition(pict.m_pos.end()))
1559     return -1;
1560 
1561   input->seek(debData, librevenge::RVNG_SEEK_SET);
1562   if (versSize) {
1563     switch (pict.m_subType) {
1564     case 7: {
1565       auto ptr = long(input->readULong(4));
1566       f << std::hex << "ptr2=" << ptr << std::dec << ",";
1567       f << "depth?=" << input->readLong(1) << ",";
1568       float dim[4];
1569       for (auto &d : dim) d = float(input->readLong(4))/65536.f;
1570       MWAWBox2f box(MWAWVec2f(dim[1], dim[0]), MWAWVec2f(dim[3], dim[2]));
1571       f << "bdbox2=" << box << ",";
1572       break;
1573     }
1574     default:
1575       break;
1576     }
1577   }
1578   auto val = static_cast<int>(input->readLong(1)); // 0 and sometimes -1
1579   if (val) f << "g0=" << val << ",";
1580   pict.m_dataPos++;
1581 
1582   if (pict.m_subType > 0xd) {
1583     f << ", " << std::hex << input->readULong(4) << std::dec << ", BdMWAWBox2=(";
1584     for (int i = 0; i < 4; i++)
1585       f << float(input->readLong(4))/65536.f << ", ";
1586     f << ")";
1587   }
1588 
1589   std::shared_ptr<MsWksGraphInternal::Zone> res;
1590   switch (pict.m_subType) {
1591   case 0: { // line
1592     auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
1593     res = form;
1594     form->m_shape = MWAWGraphicShape::line(pict.m_box.min(), pict.m_box.max());
1595     break;
1596   }
1597   case 1: // rect
1598   case 2: // rectoval
1599   case 3: { // circle
1600     MWAWBox2f bdbox = pict.m_box;
1601     auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
1602     res = form;
1603     form->m_shape.m_bdBox = form->m_shape.m_formBox = bdbox;
1604     form->m_shape.m_type = (pict.m_subType==3) ? MWAWGraphicShape::Circle :
1605                            MWAWGraphicShape::Rectangle;
1606     if (pict.m_subType==2) {
1607       float sz=10;
1608       if (bdbox.size().x() > 0 && bdbox.size().x() < 2*sz)
1609         sz = bdbox.size().x()/2.f;
1610       if (bdbox.size().y() > 0 && bdbox.size().y() < 2*sz)
1611         sz = bdbox.size().y()/2.f;
1612       form->m_shape.m_cornerWidth=MWAWVec2f(sz,sz);
1613     }
1614     break;
1615   }
1616   case 4: {
1617     auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
1618     res = form;
1619     auto angle = float(input->readLong(2));
1620     auto deltaAngle = float(input->readLong(2));
1621     float angl2 = angle+((deltaAngle>0) ? deltaAngle : -deltaAngle);
1622     float dim[4]; // real Bdbox
1623     for (auto &d : dim) d = float(input->readLong(2));
1624     MWAWBox2f realBox(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
1625     form->m_shape=MWAWGraphicShape::arc(realBox,pict.m_box,MWAWVec2f(450.f-angl2,450.f-angle));
1626     form->m_box = realBox;
1627     break;
1628   }
1629   case 5: {
1630     auto form = std::make_shared<MsWksGraphInternal::BasicShape>(pict);
1631     res = form;
1632     val = static_cast<int>(input->readULong(2));
1633     bool smooth=false;
1634     if (val==1)
1635       smooth=true;
1636     else if (val) f << "#smooth=" << val << ",";
1637     auto numPt = static_cast<int>(input->readLong(2));
1638     auto ptr = long(input->readULong(4));
1639     if (!input->checkPosition(input->tell()+8*numPt))
1640       return -1;
1641     f << std::hex << "ptr2=" << ptr << std::dec << ",";
1642     std::vector<MWAWVec2f> vertices;
1643     for (int i = 0; i < numPt; i++) {
1644       float x = float(input->readLong(4))/65336.f;
1645       float y = float(input->readLong(4))/65336.f;
1646       vertices.push_back(MWAWVec2f(x,y));
1647     }
1648     if (!smooth || numPt <= 2) {
1649       form->m_shape=MWAWGraphicShape::polygon(pict.m_box);
1650       form->m_shape.m_vertices = vertices;
1651       break;
1652     }
1653     form->m_shape=MWAWGraphicShape::path(pict.m_box);
1654     form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('M', vertices[0]));
1655 
1656     MWAWVec2f middle=0.5f*(vertices[1]+vertices[0]);
1657     form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('L', middle));
1658     for (size_t pt=1; pt+1 < size_t(numPt); ++pt) {
1659       middle=0.5f*(vertices[pt+1]+vertices[pt]);
1660       form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('Q', middle, vertices[pt]));
1661     }
1662     form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('L',vertices[size_t(numPt-1)]));
1663     if (vertices[0]==vertices[size_t(numPt)-1])
1664       form->m_shape.m_path.push_back(MWAWGraphicShape::PathData('Z'));
1665     break;
1666   }
1667   case 7: {
1668     val = static_cast<int>(input->readULong(vers >= 3 ? 1 : 2));
1669     if (val) f << "g1=" << val << ",";
1670     // skip size (already read)
1671     pict.m_dataPos = input->tell()+2;
1672     auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
1673     res = pct;
1674     ascFile.skipZone(pct->m_dataPos, pct->m_pos.end()-1);
1675     break;
1676   }
1677   case 8:
1678     res = readGroup(pict);
1679     if (!res)
1680       return -1;
1681     break;
1682   case 9: { // textbox normal
1683     auto justify = MWAWParagraph::JustificationLeft;
1684     val = static_cast<int>(input->readLong(2));
1685     switch (val) {
1686     case 0:
1687       break;
1688     case 1:
1689       justify = MWAWParagraph::JustificationCenter;
1690       break;
1691     case 2:
1692       justify = MWAWParagraph::JustificationFull;
1693       break;
1694     case -1:
1695       justify = MWAWParagraph::JustificationRight;
1696       break;
1697     default:
1698       f << "##align=" << val << ",";
1699       break;
1700     }
1701     if (vers >= 3) {
1702       f << "h=" << input->readLong(4) << ",";
1703       for (int i = 0; i < 6; i++) {
1704         val = static_cast<int>(input->readLong(2));
1705         if (val) f << "g" << i+2 << "=" << val << ",";
1706       }
1707       pict.m_dataPos += 0x10;
1708     }
1709     f << "Fl=[";
1710     for (int i = 0; i < 4; i++) {
1711       val = static_cast<int>(input->readLong(2));
1712       if (val) f << std::hex << val << std::dec << ",";
1713       else f << ",";
1714     }
1715     f << "],";
1716     auto numPos = static_cast<int>(input->readLong(2));
1717     if (numPos < 0) return -1;
1718     f << "numFonts=" << input->readLong(2);
1719 
1720     long off[4];
1721     for (auto &d : off) d = long(input->readULong(4));
1722     f << ", Ptrs=[" <<  std::hex << std::setw(8) << off[2] << ", " << std::setw(8) << off[0]
1723       << ", " << std::dec << long(off[1]-off[0])
1724       << ", "	<< std::dec << long(off[3]-off[0]) << "]";
1725 
1726     auto text = std::make_shared<MsWksGraphInternal::TextBox>(pict);
1727     text->m_justify = justify;
1728     text->m_numPositions = numPos;
1729     res = text;
1730     if (!readText(*text)) return -1;
1731     res->m_pos.setEnd(input->tell());
1732     break;
1733   }
1734   case 0xa: { // chart
1735     auto chart  = std::make_shared<MsWksGraphInternal::Chart>(pict);
1736     int chartId = m_state->m_chartId++;
1737     if (!m_tableParser->readChart(chartId, chart->m_style)) {
1738       return -1;
1739     }
1740     m_tableParser->setChartZoneId(chartId, int(m_state->m_zonesList.size()));
1741     chart->m_chartId = chartId;
1742     res = chart;
1743     res->m_pos.setEnd(input->tell());
1744     break;
1745   }
1746   case 0xc: { // equation
1747     auto ole = std::make_shared<MsWksGraphInternal::OLEZone>(pict);
1748     res = ole;
1749     int dim[2];
1750     for (auto &d : dim) d = static_cast<int>(input->readLong(4));
1751     ole->m_dim = MWAWVec2i(dim[0], dim[1]);
1752     val = static_cast<int>(input->readULong(2)); // always 0x4f4d ?
1753     f << "g0=" << std::hex << val << std::dec << ",";
1754     ole->m_oleId=static_cast<int>(input->readULong(4));
1755     val = static_cast<int>(input->readLong(2)); // always 0?
1756     if (val) f << "g1=" << val << ",";
1757     break;
1758   }
1759   case 0xd: { // bitmap
1760     libmwaw::DebugStream f2;
1761     f2 << "Graphd(II): fl(";
1762 
1763     long actPos = input->tell();
1764     for (int i = 0; i < 2; i++)
1765       f2 << input->readLong(2) << ", ";
1766     f2 << "), ";
1767     auto nCol = static_cast<int>(input->readLong(2));
1768     auto nRow = static_cast<int>(input->readLong(2));
1769     if (nRow <= 0 || nCol <= 0) return -1;
1770 
1771     f2 << "nRow=" << nRow << ", " << "nCol=" << nCol << ", ";
1772 
1773     f2 << std::hex << input->readULong(4) << std::dec << ", ";
1774 
1775     for (int i = 0; i < 3; i++) {
1776       f2 << "bdbox" << i << "=(";
1777       for (int d= 0; d < 4; d++) f2 << input->readLong(2) << ", ";
1778       f2 << "), ";
1779       if (i == 1) f2 << "unk=" << input->readLong(2) << ", ";
1780     }
1781     auto sizeLine = static_cast<int>(input->readLong(2));
1782     f2 << "lineSize(?)=" << sizeLine << ", ";
1783     long bitmapSize = input->readLong(4);
1784     f2 << "bitmapSize=" << std::hex << bitmapSize << ", ";
1785 
1786     if (bitmapSize <= 0 || (bitmapSize%nRow) != 0) {
1787       // sometimes, another row is added: only for big picture?
1788       if (bitmapSize>0 && (bitmapSize%(nRow+1)) == 0) nRow++;
1789       else if (bitmapSize < nCol*nRow || bitmapSize > 2*nCol*nRow)
1790         return -1;
1791       else { // maybe a not implemented case
1792         MWAW_DEBUG_MSG(("MsWksGraph::getEntryPicture: bitmap size is a little odd\n"));
1793         f2 << "###";
1794         ascFile.addPos(actPos);
1795         ascFile.addNote(f2.str().c_str());
1796         ascFile.addDelimiter(input->tell(),'|');
1797         break;
1798       }
1799     }
1800 
1801     auto szCol = int(bitmapSize/nRow);
1802     if (szCol < nCol) return -1;
1803 
1804     ascFile.addPos(actPos);
1805     ascFile.addNote(f2.str().c_str());
1806 
1807     pict.m_dataPos = input->tell();
1808     auto pct = std::make_shared<MsWksGraphInternal::DataBitmap>(pict);
1809     pct->m_numRows = nRow;
1810     pct->m_numCols = nCol;
1811     pct->m_dataSize = bitmapSize;
1812     res = pct;
1813     break;
1814   }
1815   case 0xe: {
1816     long actPos = input->tell();
1817     ascFile.addPos(actPos);
1818     ascFile.addNote("Graphe(I)");
1819 
1820     // first: the picture ( fixme: kept while we do not parse the spreadsheet )
1821     input->seek(144, librevenge::RVNG_SEEK_CUR);
1822     actPos = input->tell();
1823     ascFile.addPos(actPos);
1824     ascFile.addNote("Graphe(pict)");
1825     auto dSize = long(input->readLong(4));
1826     if (dSize < 0) return -1;
1827     pict.m_dataPos = actPos+4;
1828 
1829     auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
1830     pct->m_dataEndPos = actPos+4+dSize;
1831     res = pct;
1832     ascFile.skipZone(pct->m_dataPos, pct->m_dataEndPos-1);
1833     input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
1834 
1835     // now the spreadsheet ( a classic WKS file )
1836     actPos = input->tell();
1837     dSize = long(input->readULong(4));
1838     if (dSize < 0) return -1;
1839     ascFile.addPos(actPos);
1840     ascFile.addNote("Graphe(sheet)");
1841     ascFile.skipZone(actPos+4, actPos+3+dSize);
1842 #ifdef DEBUG_WITH_FILES
1843     if (dSize > 0) {
1844       librevenge::RVNGBinaryData file;
1845       input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
1846       input->readDataBlock(dSize, file);
1847       static int volatile sheetName = 0;
1848       libmwaw::DebugStream f2;
1849       f2 << "Sheet-" << ++sheetName << ".wks";
1850       libmwaw::Debug::dumpFile(file, f2.str().c_str());
1851     }
1852 #endif
1853     input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
1854 
1855     actPos = input->tell();
1856     ascFile.addPos(actPos);
1857     ascFile.addNote("Graphe(colWidth?)"); // blocksize, unknown+list of 100 w
1858     break;
1859   }
1860   case 0xf: { // new text box v4 (a picture is stored)
1861     if (vers < 4) return -1;
1862     auto textbox = std::make_shared<MsWksGraphInternal::TextBoxv4>(pict);
1863     res = textbox;
1864     textbox->m_ids[1] = long(input->readULong(4));
1865     textbox->m_ids[2] = long(input->readULong(4));
1866     f << "," << std::hex << input->readULong(4)<< std::dec << ",";
1867     // always 0 ?
1868     for (int i = 0; i < 6; i++) {
1869       val = static_cast<int>(input->readLong(2));
1870       if (val) f << "f" << i << "=" << val << ",";
1871     }
1872     textbox->m_text.setBegin(input->readLong(4));
1873     textbox->m_text.setEnd(input->readLong(4));
1874 
1875     // always 0 ?
1876     val = static_cast<int>(input->readLong(2));
1877     if (val) f << "f10=" << val << ",";
1878     auto sz = long(input->readULong(4));
1879     if (sz+0x3b != dataSize)
1880       f << "###sz=" << sz << ",";
1881 
1882     pict.m_dataPos = input->tell();
1883     if (pict.m_dataPos != pict.m_pos.end()) {
1884 #ifdef DEBUG_WITH_FILES
1885       librevenge::RVNGBinaryData file;
1886       input->readDataBlock(pict.m_pos.end()-pict.m_dataPos, file);
1887       static int volatile textboxName = 0;
1888       libmwaw::DebugStream f2;
1889       f2 << "TextBox-" << ++textboxName << ".pct";
1890       libmwaw::Debug::dumpFile(file, f2.str().c_str());
1891 #endif
1892       ascFile.skipZone(pict.m_dataPos, pict.m_pos.end()-1);
1893     }
1894     break;
1895   }
1896   case 0x10: { // basic table
1897     libmwaw::DebugStream f2;
1898     f2 << "Graph10(II): fl=(";
1899     long actPos = input->tell();
1900     for (int i = 0; i < 3; i++)
1901       f2 << input->readLong(2) << ", ";
1902     f2 << "), ";
1903     auto nRow = static_cast<int>(input->readLong(2));
1904     auto nCol = static_cast<int>(input->readLong(2));
1905     f2 << "nRow=" << nRow << ", " << "nCol=" << nCol << ", ";
1906 
1907     // basic name font
1908     auto nbChar = static_cast<int>(input->readULong(1));
1909     if (nbChar > 31) return -1;
1910     std::string fName;
1911     for (int c = 0; c < nbChar; c++)
1912       fName+=char(input->readLong(1));
1913     f2 << fName << ",";
1914     input->seek(actPos+10+32, librevenge::RVNG_SEEK_SET);
1915     auto fSz = static_cast<int>(input->readLong(2));
1916     if (fSz) f << "fSz=" << fSz << ",";
1917 
1918     ascFile.addDelimiter(input->tell(),'|');
1919     ascFile.addPos(actPos);
1920     ascFile.addNote(f2.str().c_str());
1921     input->seek(actPos+0x40, librevenge::RVNG_SEEK_SET);
1922 
1923     // a pict
1924     actPos = input->tell();
1925     ascFile.addPos(actPos);
1926     ascFile.addNote("Graph10(pict)");
1927     auto dSize = long(input->readLong(4));
1928     if (dSize < 0) return -1;
1929     pict.m_dataPos = actPos+4;
1930 
1931     auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
1932     pct->m_dataEndPos = actPos+4+dSize;
1933     res = pct;
1934     ascFile.skipZone(pct->m_dataPos, pct->m_dataEndPos-1);
1935     input->seek(actPos+4+dSize, librevenge::RVNG_SEEK_SET);
1936 
1937     // the table
1938     f << "numRows=" << nRow << ",nCols=" << nCol << ",";
1939     std::shared_ptr<MsWksGraphInternal::Table> table(new MsWksGraphInternal::Table(pict));
1940     int tableId = m_state->m_tableId++;
1941     if (m_tableParser->readTable(nCol, nRow, tableId, table->m_style)) {
1942       table->m_tableId = tableId;
1943       res=table;
1944     }
1945     break;
1946   }
1947   default:
1948     ascFile.addDelimiter(debData, '|');
1949     break;
1950   }
1951 
1952   if (!res)
1953     res.reset(new MsWksGraphInternal::Zone(pict));
1954   res->m_extra += f.str();
1955 
1956   if (order > -1000)
1957     res->m_order = order;
1958   if (!autoSend)
1959     res->m_doNotSend = true;
1960   res->m_fileId = int(m_state->m_zonesList.size());
1961   m_state->m_zonesList.push_back(res);
1962 
1963   f.str("");
1964   f << "Entries(Graph" << std::hex << res->m_subType << std::dec << "):" << *res;
1965   ascFile.addPos(pos);
1966   ascFile.addNote(f.str().c_str());
1967 
1968   zone = res->m_pos;
1969   zone.setType("Graphic");
1970   input->seek(res->m_pos.end(), librevenge::RVNG_SEEK_SET);
1971 
1972   return res->m_fileId;
1973 }
1974 
computePositions(int zoneId,std::vector<int> & linesH,std::vector<int> & pagesH)1975 void MsWksGraph::computePositions(int zoneId, std::vector<int> &linesH, std::vector<int> &pagesH)
1976 {
1977   auto numLines = int(linesH.size());
1978   auto nPages = int(pagesH.size());
1979   bool isSpreadsheet=m_parserState->m_type==MWAWParserState::Spreadsheet;
1980   for (auto zone : m_state->m_zonesList) {
1981     if (zone->m_zoneId != -1 && zoneId != zone->m_zoneId) continue;
1982     if (zone->m_line >= 0) {
1983       int h = 0;
1984       if (zone->m_line >= numLines) {
1985         MWAW_DEBUG_MSG(("MsWksGraph::computePositions: linepos is too big\n"));
1986         if (numLines)
1987           h = linesH[size_t(numLines)-1];
1988       }
1989       else
1990         h = linesH[size_t(zone->m_line)];
1991       zone->m_finalDecal = MWAWVec2f(0, float(h));
1992     }
1993     if (zone->m_page < 0 && (isSpreadsheet || zone->m_page != -2)) {
1994       float h = zone->m_finalDecal.y();
1995       float middleH=zone->m_box.center().y();
1996       h+=middleH;
1997       int p = 0;
1998       while (p < nPages) {
1999         if (h < float(pagesH[size_t(p)])) break;
2000         h -= float(pagesH[size_t(p++)]);
2001       }
2002       zone->m_page = p;
2003       zone->m_finalDecal.setY(h-middleH);
2004     }
2005   }
2006 }
2007 
getEntryPictureV1(int zoneId,MWAWEntry & zone,bool autoSend)2008 int MsWksGraph::getEntryPictureV1(int zoneId, MWAWEntry &zone, bool autoSend)
2009 {
2010   MWAWInputStreamPtr input=m_document.getInput();
2011   if (input->isEnd()) return -1;
2012 
2013   long pos = input->tell();
2014   if (input->readULong(1) != 1) return -1;
2015 
2016   libmwaw::DebugFile &ascFile = m_document.ascii();
2017   libmwaw::DebugStream f;
2018   auto ptr = long(input->readULong(2));
2019   auto flag = static_cast<int>(input->readULong(1));
2020   long size = long(input->readULong(2))+6;
2021   if (size < 22) return -1;
2022 
2023   // check if we can go to the next zone
2024   if (!input->checkPosition(pos+size)) return -1;
2025   std::shared_ptr<MsWksGraphInternal::DataPict> pict(new MsWksGraphInternal::DataPict());
2026   pict->m_zoneId = zoneId;
2027   pict->m_subType = 0x100;
2028   pict->m_pos.setBegin(pos);
2029   pict->m_pos.setLength(size);
2030 
2031   if (ptr) f << std::hex << "ptr0=" << ptr << ",";
2032   if (flag) f << std::hex << "fl=" << flag << ",";
2033 
2034   ptr = input->readLong(4);
2035   if (ptr)
2036     f << "ptr1=" << std::hex << ptr << std::dec << ";";
2037   pict->m_line = static_cast<int>(input->readLong(2));
2038   auto val = static_cast<int>(input->readLong(2)); // almost always equal to m_linePOs
2039   if (val !=  pict->m_line)
2040     f <<  "linePos2=" << std::hex << val << std::dec << ",";
2041   int dim[4]; // pictbox
2042   for (auto &d : dim) d = static_cast<int>(input->readLong(2));
2043   pict->m_box = MWAWBox2f(MWAWVec2f(float(dim[1]), float(dim[0])), MWAWVec2f(float(dim[3]),float(dim[2])));
2044 
2045   MWAWVec2i pictMin(pict->m_box.min()), pictSize(pict->m_box.size());
2046   if (pictSize.x() < 0 || pictSize.y() < 0) return -1;
2047 
2048   if (pictSize.x() > 3000 || pictSize.y() > 3000 ||
2049       pictMin.x() < -200 || pictMin.y() < -200) return -1;
2050   pict->m_dataPos = input->tell();
2051 
2052   zone = pict->m_pos;
2053   zone.setType("GraphEntry");
2054 
2055   pict->m_extra = f.str();
2056   if (!autoSend)
2057     pict->m_doNotSend=true;
2058   pict->m_fileId = int(m_state->m_zonesList.size());
2059   m_state->m_zonesList.push_back(pict);
2060   f.str("");
2061   f << "Entries(GraphEntry):" << *pict;
2062 
2063   ascFile.skipZone(pict->m_dataPos, pict->m_pos.end()-1);
2064   ascFile.addPos(pos);
2065   ascFile.addNote(f.str().c_str());
2066 
2067   input->seek(pict->m_pos.end(), librevenge::RVNG_SEEK_SET);
2068   return pict->m_fileId;
2069 }
2070 
2071 // a list of picture
readRB(MWAWInputStreamPtr input,MWAWEntry const & entry,int kind)2072 bool MsWksGraph::readRB(MWAWInputStreamPtr input, MWAWEntry const &entry, int kind)
2073 {
2074   libmwaw::DebugFile &ascFile = m_document.ascii();
2075   libmwaw::DebugStream f;
2076 
2077   long beginRB, endRB;
2078   long pos=input->tell();
2079   f << "Entries(" << entry.name() << "):";
2080   const int vers=version();
2081   switch (kind) {
2082   case 0:
2083     pos=beginRB=entry.begin();
2084     endRB=entry.end();
2085     break;
2086   case 2:
2087     if (input->readLong(1)!=3) return false;
2088     f << "id=" << input->readLong(1) << ",";
2089     MWAW_FALLTHROUGH;
2090   case 1: {
2091     unsigned long dSz=input->readULong(4);
2092     beginRB=input->tell();
2093     if (dSz&0x80000000) {
2094       f << "flags[high],";
2095       dSz &= 0x7FFFFFFF;
2096     }
2097     endRB=beginRB+long(dSz);
2098     break;
2099   }
2100   default:
2101     MWAW_DEBUG_MSG(("MsWksGraph::readRB: unknown kind\n"));
2102     return false;
2103   }
2104 
2105   long const headerSize=vers<3 ? 0x150 : 0x164;
2106   if (endRB-beginRB+2 < headerSize || !input->checkPosition(endRB)) return false;
2107 
2108   MsWksGraphInternal::RBZone zone;
2109   if (vers==4)
2110     zone.m_isMain = entry.name()=="RBDR";
2111   zone.m_id = entry.id();
2112 
2113   input->seek(beginRB, librevenge::RVNG_SEEK_SET);
2114   f << input->readLong(4) << ", ";
2115   for (int i = 0; i < 4; i++) {
2116     long val = input->readLong(4);
2117     if (val) f << "#t" << i << "=" << val << ", ";
2118   }
2119   f << "type?=" << std::hex << input->readLong(2) << std::dec << ", ";
2120   f << "numPage=" << input->readLong(2) << ", ";
2121   for (int i = 0; i < 11; i++) {
2122     long val = input->readLong(2);
2123     if (!val) continue;
2124     if (i >= 8 && (val < -100 || val > 100)) f << "###";
2125     f << "f" << i << "=" << val << ", ";
2126   }
2127   f << ", unk=(";
2128   for (int i = 0; i < 2; i++)
2129     f << input->readLong(4) << ",";
2130   f << "), ";
2131   for (int i = 0; i < 9; i++) {
2132     long val = input->readLong(2);
2133     if (val) f << "#u" << i << "=" << val << ", ";
2134   }
2135   f << std::hex << "sz?=" << input->readLong(4) << std::dec << ", ";
2136   for (int i = 0; i < 2; i++) {
2137     long val = input->readLong(2);
2138     if (val) f << "#v" << i << "=" << val << ", ";
2139   }
2140 
2141   f << "unk1=(";
2142   for (int i = 0; i < 9; i++) {
2143     long val = input->readLong(2);
2144     if (val) f << val << ",";
2145     else f << "_,";
2146   }
2147   f << "), ";
2148   if (vers>=3) {
2149     long aPos=input->tell();
2150     std::string oleName;
2151     while (input->tell() < beginRB+headerSize-2) {
2152       auto val  = char(input->readLong(1));
2153       if (val == 0) break;
2154       oleName+= val;
2155       if (oleName.length() >= 10) break;
2156     }
2157     if (!oleName.empty()) {
2158       zone.m_frame = oleName;
2159       f << "ole='" << oleName << "', ";
2160     }
2161     ascFile.addDelimiter(input->tell(),'|');
2162     input->seek(aPos+20, librevenge::RVNG_SEEK_SET);
2163   }
2164   ascFile.addPos(pos);
2165   ascFile.addNote(f.str().c_str());
2166 
2167   pos=input->tell();
2168   f.str("");
2169   f << entry.name() << "-II:";
2170   for (int i=0; i<118; ++i) {
2171     auto val = static_cast<int>(input->readLong(2));
2172     if (val) f << "g" << i << "=" << std::hex << val << std::dec << ",";
2173   }
2174   // can happens at least with vers=2 and this means that the size is not set
2175   if (endRB-beginRB+2 == headerSize)
2176     endRB=-1;
2177   auto N = static_cast<int>(input->readLong(2));
2178   f << "N=" << N << ",";
2179   ascFile.addPos(pos);
2180   ascFile.addNote(f.str().c_str());
2181 
2182   if (N == 0) return true;
2183 
2184   if (endRB>0)
2185     input->pushLimit(endRB);
2186   size_t actId = m_state->m_zonesList.size();
2187   for (int i = 0; i < N; i++) {
2188     pos = input->tell();
2189     MWAWEntry pictZone;
2190     if (getEntryPicture(vers==4 ? 0 : entry.id(), pictZone)>=0)
2191       continue;
2192     MWAW_DEBUG_MSG(("MsWksDocument::readGroup: can not find the end of group\n"));
2193     input->seek(pos, librevenge::RVNG_SEEK_SET);
2194     break;
2195   }
2196   if (endRB>0)
2197     input->popLimit();
2198   for (size_t z = actId; z < m_state->m_zonesList.size(); z++) {
2199     auto pictZone = m_state->m_zonesList[z];
2200     if (!pictZone) continue;
2201     zone.m_idList.push_back(int(z));
2202     if (!zone.m_isMain)
2203       pictZone->m_page = -2;
2204   }
2205   if (endRB>0 && input->tell() < endRB) {
2206     f.str("");
2207     f << entry.name() << "-end:###";
2208     MWAW_DEBUG_MSG(("MsWksGraph::readRB: find some extra data\n"));
2209     ascFile.addPos(input->tell());
2210     ascFile.addNote(f.str().c_str());
2211   }
2212   if (endRB>0)
2213     input->seek(endRB, librevenge::RVNG_SEEK_SET);
2214   checkTextBoxLinks(zone);
2215   int zId = zone.getId();
2216   if (m_state->m_RBsMap.find(zId) != m_state->m_RBsMap.end()) {
2217     MWAW_DEBUG_MSG(("MsWksGraph::readRB: zone %d is already filled\n", zId));
2218     // ok, let's merge the two zone
2219     auto &orig=m_state->m_RBsMap.find(zId)->second;
2220     orig.m_idList.insert(orig.m_idList.end(), zone.m_idList.begin(), zone.m_idList.end());
2221     if (orig.m_frame.empty()) orig.m_frame=zone.m_frame;
2222   }
2223   else
2224     m_state->m_RBsMap[zId]=zone;
2225   return true;
2226 }
2227 
checkTextBoxLinks(MsWksGraphInternal::RBZone & rbZone)2228 void MsWksGraph::checkTextBoxLinks(MsWksGraphInternal::RBZone &rbZone)
2229 {
2230   auto const &listIds = rbZone.m_idList;
2231   std::string const &fName = rbZone.m_frame;
2232   auto numZones = int(m_state->m_zonesList.size());
2233   std::set<long> textIds;
2234   std::map<long,long> prevLinks, nextLinks;
2235   bool ok = true;
2236   for (auto id : listIds) {
2237     if (id < 0 || id >= numZones) continue;
2238     auto zone = m_state->m_zonesList[size_t(id)];
2239     if (zone->type() != MsWksGraphInternal::Zone::Textv4)
2240       continue;
2241     static_cast<MsWksGraphInternal::TextBoxv4 &>(*zone).m_frame = fName;
2242     if (textIds.find(zone->m_ids[0]) != textIds.end()) {
2243       MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: id %lX already exists\n", static_cast<long unsigned int>(zone->m_ids[0])));
2244       ok = false;
2245       break;
2246     }
2247     textIds.insert(zone->m_ids[0]);
2248     if (zone->m_ids[1]>0)
2249       prevLinks.insert(std::map<long,long>::value_type(zone->m_ids[0],zone->m_ids[1]));
2250     if (zone->m_ids[2]>0)
2251       nextLinks.insert(std::map<long,long>::value_type(zone->m_ids[0],zone->m_ids[2]));
2252   }
2253   size_t numLinks = nextLinks.size();
2254   for (auto link : nextLinks) {
2255     if (prevLinks.find(link.second)==prevLinks.end() ||
2256         prevLinks.find(link.second)->second!=link.first) {
2257       MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: can not find prevLinks: %lX<->%lX already exists\n", static_cast<long unsigned int>(link.first), static_cast<long unsigned int>(link.second)));
2258       ok = false;
2259       break;
2260     }
2261     // check loops
2262     size_t w = 0;
2263     long actText = link.second;
2264     while (1) {
2265       if (nextLinks.find(actText)==nextLinks.end())
2266         break;
2267       actText = nextLinks.find(actText)->second;
2268       if (w++ > numLinks) {
2269         MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks:find a loop for id %lX\n", static_cast<long unsigned int>(link.first)));
2270         ok = false;
2271         break;
2272       }
2273     }
2274   }
2275   if (!ok) {
2276     MWAW_DEBUG_MSG(("MsWksGraph::checkTextBoxLinks: problem find with text links\n"));
2277     for (auto zone : m_state->m_zonesList) {
2278       if (zone->type() != MsWksGraphInternal::Zone::Textv4)
2279         continue;
2280       zone->m_ids[1] = zone->m_ids[2] = 0;
2281     }
2282   }
2283 }
2284 
readPictureV4(MWAWInputStreamPtr,MWAWEntry const & entry)2285 bool MsWksGraph::readPictureV4(MWAWInputStreamPtr /*input*/, MWAWEntry const &entry)
2286 {
2287   if (!entry.hasType("PICT")) {
2288     MWAW_DEBUG_MSG(("MsWksGraph::readPictureV4: unknown type='%s'\n", entry.type().c_str()));
2289     return false;
2290   }
2291   entry.setParsed(true);
2292 
2293   MsWksGraphInternal::Zone pict;
2294   pict.m_pos = entry;
2295   pict.m_dataPos = entry.begin();
2296   pict.m_page = -2;
2297   pict.m_zoneId = -1;
2298 
2299   auto pct = std::make_shared<MsWksGraphInternal::DataPict>(pict);
2300   std::shared_ptr<MsWksGraphInternal::Zone>res(pct);
2301   m_document.ascii().skipZone(entry.begin(), entry.end()-1);
2302 
2303   auto zId = int(m_state->m_zonesList.size());
2304   res->m_fileId = zId;
2305   m_state->m_zonesList.push_back(res);
2306 
2307   return true;
2308 }
2309 
2310 ////////////////////////////////////////////////////////////
2311 //
2312 // Low level
2313 //
2314 ////////////////////////////////////////////////////////////
2315 
2316 // read/send a group
sendGroup(int id,MWAWPosition const & pos)2317 void MsWksGraph::sendGroup(int id, MWAWPosition const &pos)
2318 {
2319   if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)] ||
2320       m_state->m_zonesList[size_t(id)]->type()!=MsWksGraphInternal::Zone::Group) {
2321     MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: can not find group %d\n", id));
2322     return;
2323   }
2324   MWAWListenerPtr listener=m_parserState->getMainListener();
2325   if (!listener) return;
2326   auto &group=static_cast<MsWksGraphInternal::GroupZone &>(*m_state->m_zonesList[size_t(id)]);
2327   group.m_isSent = true;
2328   if (listener->getType()==MWAWListener::Graphic) {
2329     sendGroup(group, m_parserState->m_graphicListener);
2330     return;
2331   }
2332   if (!canCreateGraphic(group)) {
2333     if (pos.m_anchorTo == MWAWPosition::Char || pos.m_anchorTo == MWAWPosition::CharBaseLine) {
2334       std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2335       (new MsWksGraphInternal::SubDocument(*this, m_document.getInput(), MsWksGraphInternal::SubDocument::Group, id));
2336       listener->insertTextBox(pos, subdoc);
2337       return;
2338     }
2339     MWAWPosition childPos(pos);
2340     childPos.setSize(MWAWVec2f(0,0));
2341     sendGroupChild(id, childPos);
2342     return;
2343   }
2344   MWAWGraphicEncoder graphicEncoder;
2345   MWAWGraphicListenerPtr graphicListener(new MWAWGraphicListener(*m_parserState, group.m_box, &graphicEncoder));
2346   graphicListener->startDocument();
2347   sendGroup(group, graphicListener);
2348   graphicListener->endDocument();
2349   MWAWEmbeddedObject picture;
2350   if (graphicEncoder.getBinaryResult(picture))
2351     listener->insertPicture(pos, picture);
2352 }
2353 
sendGroupChild(int id,MWAWPosition const & pos)2354 void MsWksGraph::sendGroupChild(int id, MWAWPosition const &pos)
2355 {
2356   if (id<0 || id>=int(m_state->m_zonesList.size()) || !m_state->m_zonesList[size_t(id)] ||
2357       m_state->m_zonesList[size_t(id)]->type()!=MsWksGraphInternal::Zone::Group) {
2358     MWAW_DEBUG_MSG(("MsWksGraph::sendGroupChild: can not find group %d\n", id));
2359     return;
2360   }
2361   MWAWListenerPtr listener=m_parserState->getMainListener();
2362   if (!listener) return;
2363   auto &group=static_cast<MsWksGraphInternal::GroupZone &>(*m_state->m_zonesList[size_t(id)]);
2364   group.m_isSent = true;
2365 
2366   MWAWInputStreamPtr input=m_document.getInput();
2367   size_t numZones=m_state->m_zonesList.size();
2368   size_t numChild=group.m_childs.size(), childNotSent=0;
2369   int numDataToMerge=0;
2370   MWAWBox2f partialBdBox;
2371   MWAWPosition partialPos(pos);
2372   bool isDraw=listener->getType()==MWAWListener::Graphic;
2373   for (size_t c=0; c < numChild; ++c) {
2374     int cId = group.m_childs[c];
2375     if (cId < 0 || cId >= int(numZones) || !m_state->m_zonesList[size_t(cId)])
2376       continue;
2377     auto const &child=*(m_state->m_zonesList[size_t(cId)]);
2378     bool isLast=false;
2379     bool canMerge=false;
2380     if (isDraw)
2381       canMerge=false;
2382     else if (child.type()==MsWksGraphInternal::Zone::Shape || child.type()==MsWksGraphInternal::Zone::Text) {
2383       MWAWBox2f origBdBox=child.getLocalBox();
2384       MWAWVec2f decal = child.m_decal[0] + child.m_decal[1];
2385       MWAWBox2f localBdBox(origBdBox[0]+decal, origBdBox[1]+decal);
2386       if (numDataToMerge == 0)
2387         partialBdBox=localBdBox;
2388       else
2389         partialBdBox=partialBdBox.getUnion(localBdBox);
2390       canMerge=true;
2391     }
2392     else if (child.type()==MsWksGraphInternal::Zone::Group &&
2393              canCreateGraphic(static_cast<MsWksGraphInternal::GroupZone const &>(child))) {
2394       if (numDataToMerge == 0)
2395         partialBdBox=child.getLocalBox();
2396       else
2397         partialBdBox=partialBdBox.getUnion(child.getLocalBox());
2398       canMerge=true;
2399     }
2400     if (canMerge) {
2401       ++numDataToMerge;
2402       if (c+1 < numChild)
2403         continue;
2404       isLast=true;
2405     }
2406 
2407     if (numDataToMerge>1) {
2408       MWAWGraphicEncoder graphicEncoder;
2409       MWAWGraphicListenerPtr graphicListener(new MWAWGraphicListener(*m_parserState, partialBdBox, &graphicEncoder));
2410       graphicListener->startDocument();
2411       size_t lastChild = isLast ? c : c-1;
2412       for (size_t ch=childNotSent; ch <= lastChild; ++ch) {
2413         int localCId = group.m_childs[ch];
2414         if (localCId < 0 || localCId >= int(numZones) || !m_state->m_zonesList[size_t(localCId)])
2415           continue;
2416         auto const &localChild=*(m_state->m_zonesList[size_t(localCId)]);
2417         MWAWBox2f origBdBox=localChild.getLocalBox(false);
2418         MWAWVec2f decal=localChild.m_decal[0]+localChild.m_decal[1];
2419         MWAWPosition pictPos(origBdBox[0]+decal, origBdBox.size(), librevenge::RVNG_POINT);
2420         pictPos.m_anchorTo=MWAWPosition::Page;
2421         pictPos.m_wrapping =  MWAWPosition::WBackground;
2422         if (localChild.type()==MsWksGraphInternal::Zone::Group)
2423           sendGroup(static_cast<MsWksGraphInternal::GroupZone const &>(localChild), graphicListener);
2424         else if (localChild.type()==MsWksGraphInternal::Zone::Shape) {
2425           auto const &shape=static_cast<MsWksGraphInternal::BasicShape const &>(localChild);
2426           graphicListener->insertShape(pictPos, shape.m_shape, shape.getStyle());
2427         }
2428         else if (localChild.type()==MsWksGraphInternal::Zone::Text) {
2429           std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2430           (new MsWksGraphInternal::SubDocument(const_cast<MsWksGraph &>(*this), input, MsWksGraphInternal::SubDocument::TextBox, localCId));
2431           // a textbox can not have border
2432           MWAWGraphicStyle style(localChild.m_style);
2433           style.m_lineWidth=0;
2434           graphicListener->insertTextBox(pictPos, subdoc, style);
2435         }
2436       }
2437       graphicListener->endDocument();
2438       MWAWEmbeddedObject picture;
2439       if (graphicEncoder.getBinaryResult(picture)) {
2440         partialPos.setOrigin(pos.origin()+partialBdBox[0]-group.m_box[0]);
2441         partialPos.setSize(partialBdBox.size());
2442         listener->insertPicture(partialPos, picture);
2443         if (isLast)
2444           break;
2445         childNotSent=c;
2446       }
2447     }
2448     // time to send back the data
2449     for (; childNotSent <= c; ++childNotSent)
2450       send(group.m_childs[childNotSent],pos);
2451     numDataToMerge=0;
2452   }
2453 }
2454 
canCreateGraphic(MsWksGraphInternal::GroupZone const & group) const2455 bool MsWksGraph::canCreateGraphic(MsWksGraphInternal::GroupZone const &group) const
2456 {
2457   if (m_parserState->getMainListener()->getType()==MWAWListener::Graphic) return false;
2458   auto numZones = int(m_state->m_zonesList.size());
2459   for (auto cId : group.m_childs) {
2460     if (cId < 0 || cId >= numZones || !m_state->m_zonesList[size_t(cId)])
2461       continue;
2462     auto const &child=*(m_state->m_zonesList[size_t(cId)]);
2463     if (child.m_page!=group.m_page)
2464       return false;
2465     switch (child.type()) {
2466     case MsWksGraphInternal::Zone::Shape:
2467     case MsWksGraphInternal::Zone::Text:
2468       break;
2469     case MsWksGraphInternal::Zone::Group:
2470       if (!canCreateGraphic(static_cast<MsWksGraphInternal::GroupZone const &>(child)))
2471         return false;
2472       break;
2473     case MsWksGraphInternal::Zone::Bitmap:
2474     case MsWksGraphInternal::Zone::ChartZone:
2475     case MsWksGraphInternal::Zone::OLE:
2476     case MsWksGraphInternal::Zone::Pict:
2477     case MsWksGraphInternal::Zone::TableZone:
2478     case MsWksGraphInternal::Zone::Textv4:
2479     case MsWksGraphInternal::Zone::Unknown:
2480 #if !defined(__clang__)
2481     default:
2482 #endif
2483       return false;
2484     }
2485   }
2486   return true;
2487 }
2488 
sendGroup(MsWksGraphInternal::GroupZone const & group,MWAWGraphicListenerPtr & listener) const2489 void MsWksGraph::sendGroup(MsWksGraphInternal::GroupZone const &group, MWAWGraphicListenerPtr &listener) const
2490 {
2491   if (!listener || !listener->isDocumentStarted()) {
2492     MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: the listener is bad\n"));
2493     return;
2494   }
2495   auto numZones = int(m_state->m_zonesList.size());
2496   MWAWInputStreamPtr input=m_document.getInput();
2497   for (auto cId : group.m_childs) {
2498     if (cId < 0 || cId >= numZones || !m_state->m_zonesList[size_t(cId)])
2499       continue;
2500     auto const &child=*(m_state->m_zonesList[size_t(cId)]);
2501     MWAWVec2f decal=child.m_decal[0]+child.m_decal[1];
2502     MWAWPosition pictPos(child.m_box[0]+decal, child.m_box.size(), librevenge::RVNG_POINT);
2503     pictPos.m_anchorTo=MWAWPosition::Page;
2504     pictPos.m_wrapping =  MWAWPosition::WBackground;
2505 
2506     if (child.type()==MsWksGraphInternal::Zone::Group)
2507       sendGroup(static_cast<MsWksGraphInternal::GroupZone const &>(child), listener);
2508     else if (child.type()==MsWksGraphInternal::Zone::Shape) {
2509       auto const &shape=static_cast<MsWksGraphInternal::BasicShape const &>(child);
2510       listener->insertShape(pictPos, shape.m_shape, shape.getStyle());
2511     }
2512     else if (child.type()==MsWksGraphInternal::Zone::Text) {
2513       std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2514       (new MsWksGraphInternal::SubDocument(const_cast<MsWksGraph &>(*this), input, MsWksGraphInternal::SubDocument::TextBox, cId));
2515       // a textbox can not have border
2516       MWAWGraphicStyle style(child.m_style);
2517       style.m_lineWidth=0;
2518       listener->insertTextBox(pictPos, subdoc, style);
2519     }
2520     else {
2521       MWAW_DEBUG_MSG(("MsWksGraph::sendGroup: find some unexpected child\n"));
2522     }
2523   }
2524 }
2525 
readGroup(MsWksGraphInternal::Zone & header)2526 std::shared_ptr<MsWksGraphInternal::GroupZone> MsWksGraph::readGroup(MsWksGraphInternal::Zone &header)
2527 {
2528   std::shared_ptr<MsWksGraphInternal::GroupZone> group(new MsWksGraphInternal::GroupZone(header));
2529   libmwaw::DebugStream f;
2530   MWAWInputStreamPtr input=m_document.getInput();
2531   input->seek(header.m_dataPos, librevenge::RVNG_SEEK_SET);
2532   float dim[4];
2533   for (auto &d : dim) d = float(input->readLong(4));
2534   group->m_box=MWAWBox2f(MWAWVec2f(dim[0],dim[1]), MWAWVec2f(dim[2],dim[3]));
2535   group->m_finalDecal=MWAWVec2f(0,0);
2536   long ptr[2];
2537   for (auto &p : ptr) p = long(input->readULong(4));
2538   f << "ptr0=" << std::hex << ptr[0] << std::dec << ",";
2539   if (ptr[0] != ptr[1])
2540     f << "ptr1=" << std::hex << ptr[1] << std::dec << ",";
2541   if (version() >= 3) {
2542     auto val = static_cast<int>(input->readULong(4));
2543     if (val) f << "g1=" << val << ",";
2544   }
2545 
2546   input->seek(header.m_pos.end()-2, librevenge::RVNG_SEEK_SET);
2547   auto N = static_cast<int>(input->readULong(2));
2548   for (int i = 0; i < N; i++) {
2549     long pos = input->tell();
2550     MWAWEntry childZone;
2551     int childId = getEntryPicture(header.m_zoneId, childZone, false);
2552     if (childId < 0) {
2553       MWAW_DEBUG_MSG(("MsWksGraph::readGroup: can not find child\n"));
2554       input->seek(pos, librevenge::RVNG_SEEK_SET);
2555       f << "#child,";
2556       break;
2557     }
2558     group->m_childs.push_back(childId);
2559   }
2560   group->m_extra += f.str();
2561   group->m_pos.setEnd(input->tell());
2562   return group;
2563 }
2564 
2565 // read/send a textbox zone
readText(MsWksGraphInternal::TextBox & textBox)2566 bool MsWksGraph::readText(MsWksGraphInternal::TextBox &textBox)
2567 {
2568   if (textBox.m_numPositions < 0) return false; // can an empty text exist
2569 
2570   libmwaw::DebugFile &ascFile = m_document.ascii();
2571   libmwaw::DebugStream f;
2572   f << "Entries(SmallText):";
2573   MWAWInputStreamPtr input=m_document.getInput();
2574   long pos = input->tell();
2575   if (!input->checkPosition(pos+4*(textBox.m_numPositions+1))) return false;
2576 
2577   // first read the set of (positions, font)
2578   f << "pos=[";
2579   int nbFonts = 0;
2580   for (int i = 0; i <= textBox.m_numPositions; i++) {
2581     auto fPos = static_cast<int>(input->readLong(2));
2582     auto form = static_cast<int>(input->readLong(2));
2583     f << fPos << ":" << form << ", ";
2584 
2585     if (fPos < 0 || form < -1) return false;
2586     if ((form == -1 && i != textBox.m_numPositions) ||
2587         (!textBox.m_positions.empty() && fPos < textBox.m_positions.back())) {
2588       MWAW_DEBUG_MSG(("MsWksGraph::readText: find odd positions\n"));
2589       f << "#";
2590       continue;
2591     }
2592 
2593     textBox.m_positions.push_back(fPos);
2594     textBox.m_formats.push_back(form);
2595     if (form >= nbFonts) nbFonts = form+1;
2596   }
2597   f << "] ";
2598   ascFile.addPos(pos);
2599   ascFile.addNote(f.str().c_str());
2600 
2601   pos = input->tell();
2602   f.str("");
2603   f << "SmallText:Fonts ";
2604 
2605   // actualPos, -1, only exists if actualPos!= 0 ? We ignored it.
2606   input->readLong(2);
2607   if (input->readLong(2) != -1)
2608     input->seek(pos,librevenge::RVNG_SEEK_SET);
2609   else {
2610     ascFile.addPos(pos);
2611     ascFile.addNote("SmallText:char Pos");
2612     pos = input->tell();
2613   }
2614   f.str("");
2615 
2616   long endFontPos = input->tell();
2617   auto sizeOfData = long(input->readULong(4));
2618   int numFonts = (sizeOfData%0x12 == 0) ? int(sizeOfData/0x12) : 0;
2619 
2620   if (numFonts >= nbFonts) {
2621     endFontPos = input->tell()+4+sizeOfData;
2622 
2623     ascFile.addPos(pos);
2624     ascFile.addNote("SmallText: Fonts");
2625 
2626     for (int i = 0; i < numFonts; i++) {
2627       pos = input->tell();
2628       MWAWFont font;
2629       if (!readFont(font)) {
2630         input->seek(endFontPos, librevenge::RVNG_SEEK_SET);
2631         break;
2632       }
2633       textBox.m_fontsList.push_back(font);
2634 
2635       f.str("");
2636       f << "SmallText:Font"<< i
2637         << "(" << font.getDebugString(m_parserState->m_fontConverter) << "),";
2638 
2639       ascFile.addPos(pos);
2640       ascFile.addNote(f.str().c_str());
2641       pos = input->tell();
2642     }
2643   }
2644   int nChar = textBox.m_positions.back()-1;
2645   if (nbFonts > int(textBox.m_fontsList.size())) {
2646     MWAW_DEBUG_MSG(("MsWksGraph::readText: can not read the fonts\n"));
2647     ascFile.addPos(pos);
2648     ascFile.addNote("SmallText:###");
2649     input->seek(endFontPos,librevenge::RVNG_SEEK_SET);
2650     textBox.m_fontsList.resize(0);
2651     textBox.m_positions.resize(0);
2652     textBox.m_numPositions = 0;
2653   }
2654 
2655   // now, syntax is : long(size) + size char
2656   //      - 0x16 - 0 - 0 - Fonts (default fonts)
2657   //      - 0x08 followed by two long, maybe interesting to look
2658   //      - 0x0c (or 0x18) seems followed by small int
2659   //      - nbChar : the strings (final)
2660 
2661   f.str("");
2662   f << "SmallText:";
2663   while (1) {
2664     if (input->isEnd()) return false;
2665 
2666     pos = input->tell();
2667     sizeOfData = long(input->readULong(4));
2668     if (sizeOfData == nChar) {
2669       bool ok = true;
2670       // ok we try to read the string
2671       std::string chaine("");
2672       for (long i = 0; i < sizeOfData; i++) {
2673         auto c = static_cast<unsigned char>(input->readULong(1));
2674         if (c == 0) {
2675           ok = false;
2676           break;
2677         }
2678         chaine += char(c);
2679       }
2680 
2681       if (!ok)
2682         input->seek(pos+4,librevenge::RVNG_SEEK_SET);
2683       else {
2684         textBox.m_text = chaine;
2685         f << "=" << chaine;
2686         ascFile.addPos(pos);
2687         ascFile.addNote(f.str().c_str());
2688         return true;
2689       }
2690     }
2691 
2692     if (sizeOfData <= 100+nChar && (sizeOfData%2==0)) {
2693       if (input->seek(sizeOfData, librevenge::RVNG_SEEK_CUR) != 0) return false;
2694       f << "#";
2695       ascFile.addPos(pos);
2696       ascFile.addNote(f.str().c_str());
2697       f.str("");
2698       f << "SmallText:Text";
2699       continue;
2700     }
2701 
2702     // fixme: we can try to find the next string
2703     MWAW_DEBUG_MSG(("MsWksGraph::readText: problem reading text\n"));
2704     f << "#";
2705     ascFile.addPos(pos);
2706     ascFile.addNote(f.str().c_str());
2707 
2708     break;
2709   }
2710   return false;
2711 }
2712 
readFont(MWAWFont & font)2713 bool MsWksGraph::readFont(MWAWFont &font)
2714 {
2715   int vers = version();
2716   MWAWInputStreamPtr input=m_document.getInput();
2717   long pos = input->tell();
2718   libmwaw::DebugStream f;
2719   if (!input->checkPosition(pos+18))
2720     return false;
2721   font = MWAWFont();
2722   for (int i = 0; i < 3; i++) {
2723     auto val = static_cast<int>(input->readLong(2));
2724     if (val)
2725       f << "f" << i << "=" << val << ",";
2726   }
2727   font.setFont(static_cast<int>(input->readULong(2)));
2728   auto flags = static_cast<int>(input->readULong(1));
2729   uint32_t flag = 0;
2730   if (flags & 0x1) flag |= MWAWFont::boldBit;
2731   if (flags & 0x2) flag |= MWAWFont::italicBit;
2732   if (flags & 0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
2733   if (flags & 0x8) flag |= MWAWFont::embossBit;
2734   if (flags & 0x10) flag |= MWAWFont::shadowBit;
2735   if (flags & 0x20) {
2736     if (vers==1)
2737       font.set(MWAWFont::Script(20,librevenge::RVNG_PERCENT,80));
2738     else
2739       font.set(MWAWFont::Script::super100());
2740   }
2741   if (flags & 0x40) {
2742     if (vers==1)
2743       font.set(MWAWFont::Script(-20,librevenge::RVNG_PERCENT,80));
2744     else
2745       font.set(MWAWFont::Script::sub100());
2746   }
2747   if (flags & 0x80) f << "#smaller,";
2748   font.setFlags(flag);
2749 
2750   auto val = static_cast<int>(input->readULong(1));
2751   if (val) f << "#flags2=" << val << ",";
2752   font.setSize(float(input->readULong(2)));
2753 
2754   unsigned char color[3];
2755   for (auto &c : color) c = static_cast<unsigned char>(input->readULong(2)>>8);
2756   font.setColor(MWAWColor(color[0],color[1],color[2]));
2757   font.m_extra = f.str();
2758   return true;
2759 }
2760 
sendTextBox(int zoneId,MWAWListenerPtr listener)2761 void MsWksGraph::sendTextBox(int zoneId, MWAWListenerPtr listener)
2762 {
2763   if (!listener || !listener->canWriteText()) {
2764     MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find get access to the listener\n"));
2765     return;
2766   }
2767   if (zoneId < 0 || zoneId >= int(m_state->m_zonesList.size())) {
2768     MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find textbox %d\n", zoneId));
2769     return;
2770   }
2771   auto zone = m_state->m_zonesList[size_t(zoneId)];
2772   if (!zone) return;
2773   auto &textBox = static_cast<MsWksGraphInternal::TextBox &>(*zone);
2774   listener->setFont(MWAWFont(20,12));
2775   MWAWParagraph para;
2776   para.m_justify=textBox.m_justify;
2777   listener->setParagraph(para);
2778   auto numFonts = int(textBox.m_fontsList.size());
2779   int actFormatPos = 0;
2780   auto numFormats = int(textBox.m_formats.size());
2781   if (numFormats != int(textBox.m_positions.size())) {
2782     MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: positions and formats have different length\n"));
2783     if (numFormats > int(textBox.m_positions.size()))
2784       numFormats = int(textBox.m_positions.size());
2785   }
2786   for (size_t i = 0; i < textBox.m_text.length(); i++) {
2787     if (actFormatPos < numFormats && textBox.m_positions[size_t(actFormatPos)]==int(i)) {
2788       int id = textBox.m_formats[size_t(actFormatPos++)];
2789       if (id < 0 || id >= numFonts) {
2790         MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: can not find a font\n"));
2791       }
2792       else
2793         listener->setFont(textBox.m_fontsList[size_t(id)]);
2794     }
2795     auto c = static_cast<unsigned char>(textBox.m_text[i]);
2796     switch (c) {
2797     case 0x9:
2798       MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some tab\n"));
2799       listener->insertChar(' ');
2800       break;
2801     case 0xd:
2802       if (i+1 != textBox.m_text.length())
2803         listener->insertEOL();
2804       break;
2805     case 0x19:
2806       listener->insertField(MWAWField(MWAWField::Title));
2807       break;
2808     case 0x18:
2809       listener->insertField(MWAWField(MWAWField::PageNumber));
2810       break;
2811     case 0x16:
2812       MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some time\n"));
2813       listener->insertField(MWAWField(MWAWField::Time));
2814       break;
2815     case 0x17:
2816       MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: find some date\n"));
2817       listener->insertField(MWAWField(MWAWField::Date));
2818       break;
2819     case 0x14: // fixme
2820       MWAW_DEBUG_MSG(("MsWksGraph::sendTextBox: footnote are not implemented\n"));
2821       break;
2822     default:
2823       listener->insertCharacter(c);
2824       break;
2825     }
2826   }
2827 }
2828 
send(int id,MWAWPosition const & pos)2829 void MsWksGraph::send(int id, MWAWPosition const &pos)
2830 {
2831   if (id < 0 || id >= int(m_state->m_zonesList.size())) {
2832     MWAW_DEBUG_MSG(("MsWksGraph::send: can not find zone %d\n", id));
2833     return;
2834   }
2835   MWAWListenerPtr listener=m_parserState->getMainListener();
2836   if (!listener) return;
2837   auto zone = m_state->m_zonesList[size_t(id)];
2838   zone->m_isSent = true;
2839 
2840   MWAWPosition pictPos(pos);
2841   if (pos.size()[0]<=0 || pos.size()[1]<=0)
2842     pictPos = zone->getPosition(pos.m_anchorTo);
2843   if (pictPos.m_anchorTo == MWAWPosition::Page)
2844     pictPos.setOrigin(pictPos.origin()+m_state->m_leftTopPos);
2845 
2846   MWAWInputStreamPtr input=m_document.getInput();
2847   bool isDraw=listener->getType()==MWAWListener::Graphic;
2848   switch (zone->type()) {
2849   case MsWksGraphInternal::Zone::Text: {
2850     auto &textbox = static_cast<MsWksGraphInternal::TextBox &>(*zone);
2851     MWAWBox2f box(MWAWVec2f(0,0),textbox.m_box.size());
2852     std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2853     (new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::TextBox, id));
2854     // a textbox can not have border
2855     MWAWGraphicStyle style(textbox.m_style);
2856     style.m_lineWidth=0;
2857     if (isDraw) {
2858       listener->insertTextBox(pictPos, subdoc, style);
2859       return;
2860     }
2861     MWAWPosition textPos(box[0], box.size(), librevenge::RVNG_POINT);
2862     MWAWGraphicEncoder graphicEncoder;
2863     MWAWGraphicListener graphicListener(*m_parserState, box, &graphicEncoder);
2864     graphicListener.startDocument();
2865     textPos.m_anchorTo=MWAWPosition::Page;
2866     textPos.m_wrapping=pos.m_wrapping;
2867     graphicListener.insertTextBox(textPos, subdoc, style);
2868     graphicListener.endDocument();
2869     MWAWEmbeddedObject picture;
2870     if (graphicEncoder.getBinaryResult(picture))
2871       listener->insertPicture(pictPos, picture);
2872     return;
2873   }
2874   case MsWksGraphInternal::Zone::TableZone: {
2875     auto &table = static_cast<MsWksGraphInternal::Table &>(*zone);
2876     if (isDraw) {
2877       listener->openFrame(pictPos, zone->m_style);
2878       m_tableParser->sendTable(table.m_tableId);
2879       listener->closeFrame();
2880       return;
2881     }
2882     std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2883     (new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::Table, table.m_tableId));
2884     listener->insertTextBox(pictPos, subdoc, zone->m_style);
2885     return;
2886   }
2887   case MsWksGraphInternal::Zone::ChartZone: {
2888     auto &chart = static_cast<MsWksGraphInternal::Chart &>(*zone);
2889     std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2890     (new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::Chart, chart.m_chartId));
2891     listener->insertTextBox(pictPos, subdoc, zone->m_style);
2892     return;
2893   }
2894   case MsWksGraphInternal::Zone::Group:
2895     sendGroup(id, pictPos);
2896     return;
2897   case MsWksGraphInternal::Zone::Bitmap: {
2898     auto &bmap = static_cast<MsWksGraphInternal::DataBitmap &>(*zone);
2899     MWAWEmbeddedObject picture;
2900     if (!bmap.getPictureData(input, picture,m_document.getPalette(4)))
2901       break;
2902     m_document.ascii().skipZone(bmap.m_dataPos, bmap.m_pos.end()-1);
2903     listener->insertPicture(pictPos, picture, zone->m_style);
2904     return;
2905   }
2906   case MsWksGraphInternal::Zone::Shape: {
2907     auto &shape = static_cast<MsWksGraphInternal::BasicShape &>(*zone);
2908     listener->insertShape(pictPos, shape.m_shape, shape.getStyle());
2909     return;
2910   }
2911   case MsWksGraphInternal::Zone::Pict: {
2912     MWAWEmbeddedObject picture;
2913     if (!zone->getBinaryData(input, picture))
2914       break;
2915     listener->insertPicture(pictPos, picture, zone->m_style);
2916     return;
2917   }
2918   case MsWksGraphInternal::Zone::Textv4: {
2919     auto &textbox = static_cast<MsWksGraphInternal::TextBoxv4 &>(*zone);
2920     std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
2921     (new MsWksGraphInternal::SubDocument(*this, input, MsWksGraphInternal::SubDocument::TextBoxv4, textbox.m_text, textbox.m_frame));
2922     MWAWGraphicStyle style;
2923     zone->fillFrame(style);
2924     // a textbox can not have a border
2925     style.m_lineWidth=0;
2926     if (zone->m_ids[1] > 0) {
2927       librevenge::RVNGString fName;
2928       fName.sprintf("Frame%ld", zone->m_ids[0]);
2929       style.m_frameName=fName.cstr();
2930     }
2931     if (zone->m_ids[2] > 0) {
2932       librevenge::RVNGString fName;
2933       fName.sprintf("Frame%ld", zone->m_ids[2]);
2934       style.m_frameNextName=fName.cstr();
2935     }
2936     listener->insertTextBox(pictPos, subdoc, style);
2937     return;
2938   }
2939   case MsWksGraphInternal::Zone::OLE: {
2940     auto &ole = static_cast<MsWksGraphInternal::OLEZone &>(*zone);
2941     m_document.sendOLE(ole.m_oleId, pictPos, zone->m_style);
2942     return;
2943   }
2944   case MsWksGraphInternal::Zone::Unknown:
2945 #if !defined(__clang__)
2946   default:
2947 #endif
2948     break;
2949   }
2950 
2951   MWAW_DEBUG_MSG(("MsWksGraph::send: can not send zone %d\n", id));
2952 }
2953 
sendAll(int zoneId,bool mainZone)2954 void MsWksGraph::sendAll(int zoneId, bool mainZone)
2955 {
2956   MWAWPosition undefPos;
2957   for (size_t i = 0; i < m_state->m_zonesList.size(); i++) {
2958     auto zone = m_state->m_zonesList[i];
2959     if (zoneId >= 0 && zoneId!=zone->m_zoneId)
2960       continue;
2961     if (zone->m_doNotSend || (zone->m_isSent && mainZone))
2962       continue;
2963     undefPos.m_anchorTo = mainZone ? MWAWPosition::Page : MWAWPosition::Paragraph;
2964     send(int(i), undefPos);
2965   }
2966 }
2967 
sendObjects(MsWksGraph::SendData const & what)2968 void MsWksGraph::sendObjects(MsWksGraph::SendData const &what)
2969 {
2970   MWAWListenerPtr listener=m_parserState->getMainListener();
2971   if (!listener) {
2972     MWAW_DEBUG_MSG(("MsWksGraph::sendObjects: listener is not set\n"));
2973     return;
2974   }
2975 
2976   bool first = true;
2977   auto numZones = int(m_state->m_zonesList.size());
2978   std::vector<int> listIds;
2979   MsWksGraphInternal::RBZone *rbZone=nullptr;
2980   switch (what.m_type) {
2981   case MsWksGraph::SendData::ALL:
2982     listIds.resize(size_t(numZones));
2983     for (int i = 0; i < numZones; i++) listIds[size_t(i)]=i;
2984     break;
2985   case MsWksGraph::SendData::RBDR:
2986   case MsWksGraph::SendData::RBIL: {
2987     int zId = what.m_type==MsWksGraph::SendData::RBDR ? -1 : what.m_id;
2988     if (m_state->m_RBsMap.find(zId)!=m_state->m_RBsMap.end())
2989       rbZone = &m_state->m_RBsMap.find(zId)->second;
2990     break;
2991   }
2992 #if !defined(__clang__)
2993   default:
2994     break;
2995 #endif
2996   }
2997   if (rbZone)
2998     listIds=rbZone->m_idList;
2999   bool isText=m_parserState->m_type==MWAWParserState::Text;
3000   if (isText && what.m_type==MsWksGraph::SendData::RBIL) {
3001     if (!rbZone) {
3002       MWAW_DEBUG_MSG(("MsWksGraph::sendObjects: can find RBIL zone %d\n", what.m_id));
3003       return;
3004     }
3005     if (listIds.size() != 1) {
3006       if (what.m_anchor == MWAWPosition::Char ||
3007           what.m_anchor == MWAWPosition::CharBaseLine) {
3008         std::shared_ptr<MsWksGraphInternal::SubDocument> subdoc
3009         (new MsWksGraphInternal::SubDocument(*this, m_document.getInput(), MsWksGraphInternal::SubDocument::RBILZone, what.m_id));
3010         MWAWPosition pictPos(MWAWVec2f(0,0), MWAWVec2f(what.m_size), librevenge::RVNG_POINT);
3011         pictPos.setRelativePosition(MWAWPosition::Char,
3012                                     MWAWPosition::XLeft, MWAWPosition::YTop);
3013         pictPos.m_wrapping =  MWAWPosition::WBackground;
3014         listener->insertTextBox(pictPos, subdoc);
3015         return;
3016       }
3017     }
3018   }
3019   MWAWPosition undefPos;
3020   undefPos.m_anchorTo = what.m_anchor;
3021   for (auto id : listIds) {
3022     if (id < 0 || id >= numZones) continue;
3023     auto zone = m_state->m_zonesList[size_t(id)];
3024     if (!zone || zone->m_doNotSend) continue;
3025     if (zone->m_isSent) {
3026       if (what.m_type == MsWksGraph::SendData::ALL ||
3027           (isText && what.m_anchor == MWAWPosition::Page)) continue;
3028     }
3029     if (what.m_anchor == MWAWPosition::Page) {
3030       if (what.m_page > 0 && zone->m_page+1 != what.m_page) continue;
3031       else if (what.m_page==0 && zone->m_page < 0) continue;
3032       else if (what.m_page==-2 && zone->m_page >=0) continue;
3033       undefPos=zone->getPosition(MWAWPosition::Page);
3034     }
3035 
3036     if (isText && first) {
3037       first = false;
3038       if (what.m_anchor == MWAWPosition::Page && !listener->isSectionOpened() && !listener->isParagraphOpened())
3039         listener->insertChar(' ');
3040     }
3041     send(int(id), undefPos);
3042   }
3043 }
3044 
flushExtra()3045 void MsWksGraph::flushExtra()
3046 {
3047   MWAWPosition undefPos;
3048   undefPos.m_anchorTo=MWAWPosition::Char;
3049   for (size_t i = 0; i < m_state->m_zonesList.size(); i++) {
3050     auto zone = m_state->m_zonesList[i];
3051     if (!zone || zone->m_isSent || zone->m_doNotSend) continue;
3052     send(int(i), undefPos);
3053   }
3054 }
3055 
3056 ////////////////////////////////////////////////////////////
3057 // style
3058 ////////////////////////////////////////////////////////////
~Style()3059 MsWksGraph::Style::~Style()
3060 {
3061 }
3062 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
3063