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 #include <stack>
42 
43 #include <librevenge/librevenge.h>
44 
45 #include "MWAWFont.hxx"
46 #include "MWAWGraphicListener.hxx"
47 #include "MWAWGraphicShape.hxx"
48 #include "MWAWGraphicStyle.hxx"
49 #include "MWAWListener.hxx"
50 #include "MWAWParagraph.hxx"
51 #include "MWAWParser.hxx"
52 #include "MWAWPictBitmap.hxx"
53 #include "MWAWPictMac.hxx"
54 #include "MWAWPosition.hxx"
55 #include "MWAWSubDocument.hxx"
56 
57 #include "CanvasParser.hxx"
58 
59 #include "CanvasGraph.hxx"
60 #include "CanvasStyleManager.hxx"
61 
62 #include "libmwaw_internal.hxx"
63 
64 /** Internal: the structures of a CanvasGraph */
65 namespace CanvasGraphInternal
66 {
67 //! Internal: the shape of a CanvasGraph
68 struct Shape {
69   //! constructor
ShapeCanvasGraphInternal::Shape70   Shape()
71     : m_type(-1)
72     , m_box()
73     , m_rotation(0)
74     , m_penSize(1,1)
75     , m_mode(8)
76     , m_patterns{1,1}
77     , m_dash(1)
78     , m_dashWidth()
79     , m_hatchGradChild(-1)
80     , m_values{0,0}
81     , m_colors{MWAWColor::black(), MWAWColor::white()}
82     , m_points()
83     , m_child(-1)
84     , m_origChild(-1)
85     , m_childs()
86     , m_align(0)
87     , m_bitmapType(0)
88     , m_arrow(MWAWGraphicStyle::Arrow::plain())
89     , m_specialType()
90     , m_entry()
91     , m_sent(false)
92   {
93   }
94 
95   //! returns the type name
getTypeNameCanvasGraphInternal::Shape96   std::string getTypeName() const
97   {
98     if (m_type==52 && !m_specialType.empty()) {
99       std::stringstream s;
100       s << "SPEC" << m_specialType;
101       return s.str();
102     }
103     static std::map<int, std::string> const s_typeName= {
104       {2,"text"},
105       {3,"line"},
106       {4,"rect"},
107       {5,"rectOval"},
108       {6,"oval"},
109       {7,"arc"},
110       {9,"polyline"},
111       {10,"spline"},
112       {18,"picture"},
113       {52,"special"},
114       {55,"bitmap"}, // in v3.5 indexed
115       {56,"polydata"},
116       {59,"emptyV3"}, // in v3
117       {99,"group"},
118       {100,"none"}
119     };
120     auto const &it=s_typeName.find(m_type);
121     if (it!=s_typeName.end())
122       return it->second;
123     std::stringstream s;
124     s << "Type" << m_type << "A";
125     return s.str();
126   }
127 
128   //! try to return the special type
getSpecialIdCanvasGraphInternal::Shape129   int getSpecialId() const
130   {
131     static std::map<std::string, int> const s_specialId= {
132       {"CCir", 9}, // concentric circle
133       {"Cube", 0}, // front/back face coord in m_points
134       {"DIMN", 1}, // a dimension with measure
135       {"Enve", 8}, // enveloppe
136       {"grid", 2}, // num subdivision in values[0], values[1]
137       {"HATC", 7}, // hatch
138       {"ObFl", 3}, // gradient
139       {"OLnk", 10}, // object link
140       {"Paln", 4}, // line, poly, spline with big border
141       {"QkTm", 5}, // checkme a quickTime film video in data?
142       {"regP", 6}, // a target arrow
143     };
144     auto const &it=s_specialId.find(m_specialType);
145     if (it!=s_specialId.end())
146       return it->second;
147     return -1;
148   }
149   //! operator<<
operator <<(std::ostream & o,Shape const & s)150   friend std::ostream &operator<<(std::ostream &o, Shape const &s)
151   {
152     o << s.getTypeName() << ",";
153     o << s.m_box << ",";
154     if (s.m_rotation)
155       o << "rot[transf]=" << s.m_rotation << ",";
156     if (s.m_penSize!=MWAWVec2f(1,1))
157       o << "pen[size]=" << s.m_penSize << ",";
158     if (s.m_colors[0]!=MWAWColor::black())
159       o << "col[line]=" << s.m_colors[0] << ",";
160     if (s.m_colors[1]!=MWAWColor::white())
161       o << "col[surf]=" << s.m_colors[1] << ",";
162     switch (s.m_mode) {
163     case 8: // copy
164       break;
165     case 9:
166       o << "xor,";
167       break;
168     default:
169       o << "mode=" << s.m_mode << ",";
170       break;
171     }
172     for (int i=0; i<2; ++i) {
173       if (s.m_patterns[i]==1) continue;
174       o << "patt[" << (i==0 ? "line" : "surf") << "]=" << s.m_patterns[i] << ",";
175     }
176     if (!s.m_dashWidth.empty()) {
177       o << "dash=[";
178       for (auto w : s.m_dashWidth) o << w << ",";
179       o << "],";
180     }
181     else if (s.m_dash!=1)
182       o << "dash=" << s.m_dash << ",";
183     if (s.m_hatchGradChild>0)
184       o << "child[hatch/grad]=S" << s.m_hatchGradChild << ",";
185     if (!s.m_points.empty()) {
186       o << "pts=[";
187       for (auto const &pt: s.m_points)
188         o << pt << ",";
189       o << "],";
190     }
191     if (s.m_values[0]) {
192       switch (s.m_type) {
193       case 3:
194         o << "dir=" << s.m_values[0] << ",";
195         break;
196       case 5:
197         o << "rad[H]=" << s.m_values[0] << ",";
198         break;
199       case 6: // rotation angle
200       case 7:
201         o << "angle0=" <<  s.m_values[0] << ",";
202         break;
203       case 55: // 1-8-32
204         o << "depth=" << s.m_values[0] << ",";
205         break;
206       default:
207         o << "f0=" <<  s.m_values[0] << ",";
208         break;
209       }
210     }
211     if (s.m_values[1]) {
212       switch (s.m_type) {
213       case 3:
214         o << "arrow=" << s.m_values[1] << ",";
215         break;
216       case 5:
217         o << "rad[V]=" << s.m_values[1] << ",";
218         break;
219       case 7:
220         o << "angle1=" <<  s.m_values[1] << ",";
221         break;
222       default:
223         o << "f1=" <<  s.m_values[1] << ",";
224         break;
225       }
226     }
227     if (s.m_child>0)
228       o << "child=S" << s.m_child << ",";
229     if (s.m_origChild>0)
230       o << "child[orig]=S" << s.m_origChild << ",";
231     if (s.m_entry.valid())
232       o << "data=" << std::hex << s.m_entry.begin() << "<->" << s.m_entry.end() << std::dec << ",";
233     if (s.m_align)
234       o << "align=" << s.m_align << ",";
235     if (s.m_bitmapType)
236       o << "bitmap[type]=" << s.m_bitmapType << ",";
237 
238     return o;
239   }
240   //! the shape type
241   int m_type;
242   //! the bounding box
243   MWAWBox2f m_box;
244   //! the transformed child rotation
245   int m_rotation;
246   //! the pen size
247   MWAWVec2f m_penSize;
248   //! the copy mode (8: copy, 9: xor)
249   int m_mode;
250   //! the line, surface pattern
251   int m_patterns[2];
252   //! the line dash
253   int m_dash;
254   //! the dash array: a sequence of (fullsize, emptysize) v3.5
255   std::vector<float> m_dashWidth;
256   //! the hatch or gradient child
257   int m_hatchGradChild;
258   //! the values
259   int m_values[2];
260   //! the color
261   MWAWColor m_colors[2];
262   //! the points: line, ...
263   std::vector<MWAWVec2f> m_points;
264   //! the main child (all)
265   int m_child;
266   //! the child before the transformation
267   int m_origChild;
268   //! the childs (group 99)
269   std::vector<int> m_childs;
270   //! the text alignment: 0:left, 1:center, ...
271   int m_align;
272   //! the bitmap type
273   int m_bitmapType;
274   //! the line/arc arrow
275   MWAWGraphicStyle::Arrow m_arrow;
276   //! the special type
277   std::string m_specialType;
278   //! the data zone
279   MWAWEntry m_entry;
280   //! a flag to know if the shape is already send
281   mutable bool m_sent;
282 };
283 
284 //! Internal: the local state of a CanvasGraph
285 struct LocalTransform {
286   //! default constructor
LocalTransformCanvasGraphInternal::LocalTransform287   LocalTransform(MWAWPosition const &pos, MWAWGraphicStyle const &style)
288     : m_position(pos)
289     , m_style(style)
290   {
291   }
292   MWAWPosition m_position;
293   MWAWGraphicStyle m_style;
294 };
295 
296 //! Internal: given a list of vertices, an indice and an offset computes a new point
getOffsetPoint(std::vector<MWAWVec2f> const & vertices,size_t id,float offset)297 MWAWVec2f getOffsetPoint(std::vector<MWAWVec2f> const &vertices, size_t id, float offset)
298 {
299   if (vertices.size()<=1 || id>=vertices.size()) {
300     MWAW_DEBUG_MSG(("CanvasGraphInternal::getOffsetPoints: bad index=%d\n",int(id)));
301     return vertices.empty() ? MWAWVec2f(0,0) : vertices[0];
302   }
303   MWAWVec2f dirs[]= {MWAWVec2f(0,0), MWAWVec2f(0,0)};
304   float scales[]= {0,0};
305   for (size_t d=0; d<2; ++d) {
306     if ((d==0 && id==0) || (d==1 && id+1==vertices.size()))
307       continue;
308     dirs[d]=vertices[id+(d==0 ? 0 : 1)]-vertices[id-(d==0 ? 1 : 0)];
309     float len=dirs[d][0]*dirs[d][0]+dirs[d][1]*dirs[d][1];
310     if (len<=0) continue;
311     scales[d]=offset/std::sqrt(len);
312   }
313   MWAWVec2f const &pt=vertices[id];
314   MWAWVec2f pts[]= {pt+MWAWVec2f(-scales[0]*dirs[0][1], scales[0]*dirs[0][0]),
315                     pt+MWAWVec2f(-scales[1]*dirs[1][1], scales[1]*dirs[1][0])
316                    };
317 
318   float const epsilon=1e-6f;
319   float cr=dirs[0][0]*dirs[1][1]-dirs[0][1]*dirs[1][0];
320   if (cr>-epsilon && cr<epsilon)
321     return pts[id==0 ? 1 : 0];
322   // M=P0+u*d0, M=P1+v*d1, P0P1=u*d0-v*d1, P0P1^d1=u*d0^d1
323   MWAWVec2f P0P1=pts[1]-pts[0];
324   float u=(P0P1[0]*dirs[1][1]-P0P1[1]*dirs[1][0])/cr;
325   return pts[0]+u*dirs[0];
326 }
327 
328 //! Internal: try to smooth a list of points
smoothPoints(std::vector<MWAWVec2f> const & vertices)329 std::vector<MWAWVec2f> smoothPoints(std::vector<MWAWVec2f> const &vertices)
330 {
331   std::vector<MWAWVec2f> res;
332   size_t N=vertices.size();
333   if (N<=1)
334     return res;
335 
336   res.push_back(vertices[0]);
337   for (size_t j=1; j+1<N; ++j) {
338     MWAWVec2f dir=vertices[j+1]-vertices[j-1];
339     MWAWVec2f AB=vertices[j]-vertices[j-1];
340     float len2=(dir[0]*dir[0]+dir[1]*dir[1]);
341     float cr=AB[0]*dir[1]-AB[1]*dir[0];
342     float offset=cr/3/(len2>0 ? len2 : 1);
343     res.push_back(vertices[j]+offset*MWAWVec2f(-dir[1],dir[0]));
344   }
345   res.push_back(vertices.back());
346   return res;
347 }
348 
349 ////////////////////////////////////////
350 //! Internal: the state of a CanvasGraph
351 struct State {
352   //! constructor
StateCanvasGraphInternal::State353   State()
354     : m_input()
355 
356     , m_idToGradientMap()
357     , m_idToShapeMap()
358   {
359   }
360 
361   //! the main input
362   MWAWInputStreamPtr m_input;
363 
364   //! the map id to gradient
365   std::map<int, MWAWGraphicStyle::Gradient> m_idToGradientMap;
366   //! the map id to shape
367   std::map<int, Shape> m_idToShapeMap;
368 };
369 
370 ////////////////////////////////////////
371 //! Internal: the subdocument of a CanvasGraph
372 class SubDocument final : public MWAWSubDocument
373 {
374 public:
375   //! constructor from a zoneId
SubDocument(CanvasGraph & parser,MWAWInputStreamPtr const & input,int zoneId)376   SubDocument(CanvasGraph &parser, MWAWInputStreamPtr const &input, int zoneId)
377     : MWAWSubDocument(parser.m_mainParser, input, MWAWEntry())
378     , m_graphParser(parser)
379     , m_id(zoneId)
380     , m_measure()
381   {
382   }
383   //! constructor from string
SubDocument(CanvasGraph & parser,MWAWInputStreamPtr const & input,librevenge::RVNGString const & measure)384   SubDocument(CanvasGraph &parser, MWAWInputStreamPtr const &input, librevenge::RVNGString const &measure)
385     : MWAWSubDocument(parser.m_mainParser, input, MWAWEntry())
386     , m_graphParser(parser)
387     , m_id(-1)
388     , m_measure(measure)
389   {
390   }
391 
392   //! destructor
~SubDocument()393   ~SubDocument() final {}
394 
395   //! operator!=
operator !=(MWAWSubDocument const & doc) const396   bool operator!=(MWAWSubDocument const &doc) const final
397   {
398     if (MWAWSubDocument::operator!=(doc)) return true;
399     auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
400     if (!sDoc) return true;
401     if (&m_graphParser != &sDoc->m_graphParser) return true;
402     if (m_id != sDoc->m_id) return true;
403     if (m_measure != sDoc->m_measure) return true;
404     return false;
405   }
406 
407   //! the parser function
408   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
409 
410 protected:
411   //! the graph parser
412   CanvasGraph &m_graphParser;
413   //! the subdocument id
414   int m_id;
415   //! the measure
416   librevenge::RVNGString m_measure;
417 private:
418   SubDocument(SubDocument const &orig) = delete;
419   SubDocument &operator=(SubDocument const &orig) = delete;
420 };
421 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)422 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
423 {
424   if (!listener || !listener->canWriteText()) {
425     MWAW_DEBUG_MSG(("CanvasGraphInternal::SubDocument::parse: no listener\n"));
426     return;
427   }
428   if (m_id<0) {
429     if (m_measure.empty()) {
430       MWAW_DEBUG_MSG(("CanvasGraphInternal::SubDocument::parse: can not find the measure\n"));
431       return;
432     }
433     listener->setFont(MWAWFont(3,10));
434     MWAWParagraph para;
435     para.m_justify = MWAWParagraph::JustificationCenter;
436     listener->setParagraph(para);
437     listener->insertUnicodeString(m_measure);
438     return;
439   }
440   long pos = m_input->tell();
441   m_graphParser.sendText(m_id);
442   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
443 }
444 
445 }
446 
447 ////////////////////////////////////////////////////////////
448 // constructor/destructor, ...
449 ////////////////////////////////////////////////////////////
CanvasGraph(CanvasParser & parser)450 CanvasGraph::CanvasGraph(CanvasParser &parser)
451   : m_parserState(parser.getParserState())
452   , m_state(new CanvasGraphInternal::State)
453   , m_mainParser(&parser)
454   , m_styleManager(parser.m_styleManager)
455 {
456 }
457 
~CanvasGraph()458 CanvasGraph::~CanvasGraph()
459 {
460 }
461 
version() const462 int CanvasGraph::version() const
463 {
464   return m_parserState->m_version;
465 }
466 
setInput(MWAWInputStreamPtr & input)467 void CanvasGraph::setInput(MWAWInputStreamPtr &input)
468 {
469   m_state->m_input=input;
470 }
471 
getInput()472 MWAWInputStreamPtr &CanvasGraph::getInput()
473 {
474   return m_state->m_input;
475 }
476 
sendShape(int id)477 bool CanvasGraph::sendShape(int id)
478 {
479   auto const it=m_state->m_idToShapeMap.find(id);
480   if (id<=0 || it==m_state->m_idToShapeMap.end()) {
481     MWAW_DEBUG_MSG(("CanvasGraph::sendShape: can not find shape %d\n", id));
482     return false;
483   }
484   return send(it->second);
485 }
486 
487 ////////////////////////////////////////////////////////////
488 //
489 // Intermediate level
490 //
491 ////////////////////////////////////////////////////////////
492 
493 ////////////////////////////////////////////////////////////
494 // shapes
495 ////////////////////////////////////////////////////////////
496 
readShapes(int numShapes,unsigned long shapeLength,unsigned long dataLength)497 bool CanvasGraph::readShapes(int numShapes, unsigned long shapeLength, unsigned long dataLength)
498 {
499   if (long(shapeLength)<0 || !m_mainParser->decode(long(shapeLength)) ||
500       (long(dataLength)<0 || !m_mainParser->decode(long(dataLength)))) {
501     MWAW_DEBUG_MSG(("CanvasGraph::readShapes: can not decode the input\n"));
502     return false;
503   }
504   bool const isWindows=m_mainParser->isWindowsFile();
505   MWAWInputStreamPtr input = getInput();
506   long pos=input ? input->tell() : 0;
507   long endPos=pos+long(shapeLength);
508   // checkme:
509   // on Windows, I found 4 extra bits after each 65532 bits
510   //             I supposed that these shapes are stored in blocks of 65536 bits on Windows, ...
511   //             (this probably implies that data blocks with size >65536 are managed differently :-~)
512   long extraCheckSumSz=isWindows ? 4*(numShapes/762) : 0;
513   if (!input->checkPosition(endPos+long(dataLength)) || (long(shapeLength)-extraCheckSumSz)/86 < numShapes) {
514     MWAW_DEBUG_MSG(("CanvasGraph::readShapes: zone seems too short\n"));
515     return false;
516   }
517 
518   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
519   libmwaw::DebugStream f;
520   f << "Entries(Shape):";
521   ascFile.addPos(pos);
522   ascFile.addNote(f.str().c_str());
523 
524   MWAWEntry dataZone;
525   dataZone.setBegin(endPos);
526   dataZone.setLength(long(dataLength));
527   std::vector<MWAWEntry> dataZonesList;
528   if (!isWindows)
529     dataZonesList.push_back(dataZone);
530   else {
531     input->seek(endPos, librevenge::RVNG_SEEK_SET);
532 
533     long finalEnd=dataZone.end();
534     for (int i=0; i<int(dataLength/16); ++i) {
535       long actPos=input->tell();
536       f.str("");
537       f << "Shape-Dt" << i << ":";
538       f << input->readULong(4) << ",";
539       f << input->readULong(4) << ",";
540       auto len=input->readULong(4);
541       f << "len=" << len << ",";
542 
543       dataZone.setBegin(finalEnd);
544       dataZone.setLength(long(len));
545       dataZonesList.push_back(dataZone);
546 
547       if (len) {
548         if (!m_mainParser->decode(long(len))) {
549           MWAW_DEBUG_MSG(("CanvasGraph::readShapes: can not decode a data zone\n"));
550           return false;
551         }
552         ascFile.addPos(finalEnd);
553         ascFile.addNote("_");
554         finalEnd += len;
555         ascFile.addPos(finalEnd);
556         ascFile.addNote("_");
557       }
558       ascFile.addDelimiter(input->tell(), '|');
559       ascFile.addPos(actPos);
560       ascFile.addNote(f.str().c_str());
561       input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
562     }
563   }
564 
565   input->seek(pos, librevenge::RVNG_SEEK_SET);
566   for (int i=0; i<numShapes; ++i) {
567     if (isWindows && i>0 && (i%762)==0) {
568       ascFile.addPos(input->tell());
569       ascFile.addNote("_");
570       input->seek(4, librevenge::RVNG_SEEK_CUR);
571     }
572     pos=input->tell();
573     readShape(i, dataZonesList);
574     input->seek(pos+86, librevenge::RVNG_SEEK_SET);
575   }
576   if (input->tell()!=endPos) {
577     ascFile.addPos(input->tell());
578     ascFile.addNote("Shape-End:");
579   }
580 
581   ascFile.addPos(dataZone.begin());
582   ascFile.addNote("Shape-Data:");
583   if (!dataZonesList.empty())
584     input->seek(dataZonesList.back().end(), librevenge::RVNG_SEEK_SET);
585 
586   return true;
587 }
588 
readShape(int n,std::vector<MWAWEntry> const & dataZonesList)589 bool CanvasGraph::readShape(int n, std::vector<MWAWEntry> const &dataZonesList)
590 {
591   MWAWInputStreamPtr input = getInput();
592   long pos=input->tell();
593   if (!input->checkPosition(pos+86)) {
594     MWAW_DEBUG_MSG(("CanvasGraph::readShape: zone seems too short\n"));
595     return false;
596   }
597   bool const isWindows=m_mainParser->isWindowsFile();
598   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
599   libmwaw::DebugStream f;
600   int const vers=version();
601   int val;
602   float dim[4];
603   for (auto &d : dim) d=float(input->readLong(2));
604   int type=int(input->readULong(1));
605 
606   if (type==59 || type==100) {
607     input->seek(pos+86, librevenge::RVNG_SEEK_SET);
608     ascFile.addPos(pos);
609     ascFile.addNote("_");
610     return true;
611   }
612   m_state->m_idToShapeMap[n]=CanvasGraphInternal::Shape();
613   CanvasGraphInternal::Shape &shape=m_state->m_idToShapeMap.find(n)->second;
614   shape.m_type=type;
615   float penSize[2];
616   for (auto &p :  penSize) p=float(input->readULong(1));
617   for (auto &p :  penSize) p+=float(input->readULong(1))/256;
618   shape.m_penSize=MWAWVec2f(penSize[0], penSize[1]);
619   shape.m_mode=int(input->readULong(1));
620   for (auto &pat : shape.m_patterns) pat=int(input->readULong(1));
621   val=int(input->readULong(2));
622   bool hasDash=false;
623   if (val&0x1000)
624     hasDash=true;
625   if (val&0x8000)
626     f << "locked,";
627   val&=0x6fff;
628   if (val) // 0 or 1
629     f << "fl=" << std::hex << val << std::dec << ",";
630   MWAWEntry data;
631   long begPos=input->readLong(4);
632   size_t dataId=0;
633   if (isWindows && (begPos>>16)) {
634     dataId=size_t(begPos>>16);
635     begPos=(begPos&0xffff);
636   }
637   data.setBegin(begPos);
638   data.setLength(input->readLong(4));
639   if (n>0 && data.valid()) {
640     if (dataId<dataZonesList.size() && data.end()<=dataZonesList[dataId].length()) {
641       shape.m_entry.setBegin(dataZonesList[dataId].begin()+data.begin());
642       shape.m_entry.setLength(data.length());
643     }
644     else if ((dim[0]<0 || dim[0]>0) && (dim[1]<0 || dim[1]>0)) { // dim[0|1]==0 is a symptom of a junk zone
645       MWAW_DEBUG_MSG(("CanvasGraph::readShape: the zone data seems bad\n"));
646       f << "###data=" << std::hex << data.begin() << "<->" << data.end() << std::dec << "[" << dataId << "],";
647     }
648   }
649   for (auto &v : shape.m_values) v=int(input->readLong(2));
650   if (vers==2 || !hasDash)
651     shape.m_bitmapType=int(input->readLong(2)); // 0|1
652   else
653     shape.m_dash=int(input->readULong(2));
654   val=int(input->readULong(2));
655   if (val)
656     f << "parent?[id]=" << val << ",";
657   val=int(input->readULong(2));
658   if (val)
659     f << "next?[id]=" << val << ",";
660   val=int(input->readULong(2));
661   if (vers>2)
662     shape.m_hatchGradChild=val;
663   else if (val)
664     f << "unkn=" << val << ",";
665   shape.m_origChild=int(input->readULong(2));
666   val=int(input->readULong(2)); // 0
667   if (val) f << "g0=" << val << ",";
668   shape.m_child=int(input->readULong(2));
669   ascFile.addDelimiter(input->tell(),'|');
670   input->seek(pos+46, librevenge::RVNG_SEEK_SET);
671   ascFile.addDelimiter(input->tell(),'|');
672   if (type==2) {
673     val=int(input->readULong(2));
674     if (val)
675       f << "N[C]=" << val << ",";
676     val=int(input->readULong(2)); // 0
677     if (val) f << "g1=" << val << ",";
678     if (vers==2) {
679       shape.m_align=int(input->readULong(1)); // 0: left, 1: center
680       if (shape.m_align) f << "align=" << shape.m_align << ",";
681       input->seek(1, librevenge::RVNG_SEEK_CUR);
682     }
683     else {
684       val=int(input->readULong(2)); // 0
685       if (val) f << "g2=" << val << ",";
686     }
687   }
688   for (int st=type==2 ? 1 : 0; st<2; ++st) {
689     unsigned char col[3];
690     for (auto &c : col) c=(unsigned char)(input->readULong(2)>>8);
691     shape.m_colors[st]=MWAWColor(col[0],col[1],col[2]);
692   }
693   shape.m_rotation=int(input->readULong(2));
694   val=int(input->readULong(2));
695   if (val) f << "h1=" << val << ",";
696   for (auto &d : dim) d+=float(input->readULong(2))/65536.f;
697   if (isWindows)
698     shape.m_box=MWAWBox2f(MWAWVec2f(dim[0],dim[1]),MWAWVec2f(dim[2],dim[3]));
699   else
700     shape.m_box=MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2]));
701   if (type==52) {
702     for (int i=0; i<6; ++i) { // 0
703       val=int(input->readULong(2));
704       if (!val) continue;
705       if (i==5)
706         f << "prev[hatch/grad]=S" << val << ",";
707       else
708         f << "h" << 2+i << "=" << val << ",";
709     }
710     std::string what;
711     for (int i=0; i<4; ++i) what+=char(input->readULong(1));
712     shape.m_specialType=what;
713   }
714   auto const extra=f.str();
715   f.str("");
716   f << "Shape-" << n << ":" << shape << extra;
717   if (input->tell()!=pos+86)
718     ascFile.addDelimiter(input->tell(),'|');
719   ascFile.addPos(pos);
720   ascFile.addNote(f.str().c_str());
721 
722   if (n>0 && shape.m_entry.valid()) {
723     shape.m_entry.setId(n);
724     readShapeData(shape);
725   }
726 
727   input->seek(pos+86, librevenge::RVNG_SEEK_SET);
728   return true;
729 }
730 
readShapeData(CanvasGraphInternal::Shape & shape)731 bool CanvasGraph::readShapeData(CanvasGraphInternal::Shape &shape)
732 {
733   bool const isWindows=m_mainParser->isWindowsFile();
734   MWAWInputStreamPtr input = getInput();
735   int expectedSize=shape.m_type==2 ? 47 : shape.m_type==3 ? 46 : shape.m_type==7 ? 48 : shape.m_type==99 ? 2 : 0;
736   auto const &entry=shape.m_entry;
737   if (!entry.valid() || !input->checkPosition(entry.end()) || (expectedSize!=0 && entry.length()<expectedSize)) {
738     MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: zone %d seems bad\n", entry.id()));
739     return false;
740   }
741   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
742   libmwaw::DebugStream f;
743   f << "Shape-" << entry.id() << "[data," << shape.getTypeName() << "]:";
744   input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
745   long endPos=entry.end();
746   ascFile.addPos(endPos);
747   ascFile.addNote("_");
748   switch (shape.m_type) {
749   case 2: // a text zone, will be read by sendText
750     break;
751   case 3: { // line
752     float dim[4];
753     for (auto &d : dim) d=float(input->readLong(2));
754     for (auto &d : dim) d+=float(input->readLong(2))/65536;
755     for (int st=0; st<4; st+=2) {
756       if (isWindows)
757         shape.m_points.push_back(MWAWVec2f(dim[st], dim[st+1]));
758       else
759         shape.m_points.push_back(MWAWVec2f(dim[st+1], dim[st]));
760       f << shape.m_points.back() << (st==0 ? "<->" : ",");
761     }
762 
763     std::string extra;
764     if (m_styleManager->readArrow(shape.m_arrow, extra))
765       f << "arrow=[" << shape.m_arrow << extra << "],";
766     else
767       f << "###";
768     input->seek(entry.begin()+16+26, librevenge::RVNG_SEEK_SET);
769     for (int i=0; i<2; ++i) {
770       int val=int(input->readLong(2));
771       if (val) f << "f" << i << "=" << val << ",";
772     }
773     if (entry.length()<46+2) break;  // v3.5
774     int N=int(input->readULong(2));
775     if (entry.length()<46+2+4*N && isWindows) { // rare but may happen if the file is converted
776       input->seek(-2, librevenge::RVNG_SEEK_CUR);
777       input->setReadInverted(false);
778       N=int(input->readULong(2));
779     }
780     if (entry.length()<46+2+4*N) {
781       f << "###N=" << N << ",";
782       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: the number of dashes in zone %d seems bad\n", entry.id()));
783       if (isWindows)
784         input->setReadInverted(true);
785       break;
786     }
787     f << "dash=[";
788     for (int i=0; i<N; ++i) {
789       shape.m_dashWidth.push_back(float(input->readULong(4))/65536.f);
790       f << shape.m_dashWidth.back() << ",";
791     }
792     f << "],";
793     if (isWindows)
794       input->setReadInverted(true);
795     break;
796   }
797   case 4:
798   case 5: // a BW bitmap, will be read by getBitmapBW
799     break;
800   case 7: { // arc
801     std::string extra;
802     if (m_styleManager->readArrow(shape.m_arrow, extra))
803       f << "arrow=[" << shape.m_arrow << extra << "],";
804     else
805       f << "###";
806     input->seek(entry.begin()+26, librevenge::RVNG_SEEK_SET);
807     for (int i=0; i<3; ++i) { // 0
808       int val=int(input->readLong(2));
809       if (val) f << "f" << i << "=" << val << ",";
810     }
811     float dim[4];
812     for (auto &d : dim) d=float(input->readLong(2));
813     for (auto &d : dim) d+=float(input->readLong(2))/65536;
814     f << "dim=" << MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2])); // checkme probably junk
815     break;
816   }
817   case 9:
818   case 10: { // polygone
819     if (entry.length()<8) {
820       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: the entry seems too short\n"));
821       f << "####";
822       break;
823     }
824     for (int i=0; i<2; ++i) { // small numbers
825       int val=int(input->readLong(2));
826       if (val)
827         f << "f" << i << "=" << val << ",";
828     }
829     int N=int(input->readULong(4));
830     if (N<0 || 1+N>entry.length()/8 || (shape.m_type==10 && (N%2)!=0)) {
831       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the number of points of a polyline\n"));
832       f << "###N=" << N << ",";
833     }
834     else {
835       f << "N=" << N << ",";
836       f << "pts=[";
837       for (int i=0; i<N; ++i) {
838         float dim[2];
839         for (auto &d : dim) d=float(input->readLong(4))/65536;
840         shape.m_points.push_back(MWAWVec2f(dim[1], dim[0]));
841         f << shape.m_points.back() << ",";
842       }
843       f << "],";
844     }
845     break;
846   }
847   case 18: // a picture, will be read by getPicture
848     break;
849   case 52: { // special
850     int specialId=shape.getSpecialId();
851     switch (specialId) {
852     case 0: // cube
853       if (entry.length()<64) {
854         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the cube points\n"));
855         f << "###sz";
856         break;
857       }
858       for (int i=0; i<8; ++i) { // front face, back face
859         float pts[2];
860         for (auto &c : pts) c=float(input->readULong(4))/65536.f;
861         shape.m_points.push_back(MWAWVec2f(pts[1],pts[0]));
862         f << shape.m_points.back() << ",";
863       }
864       break;
865     case 1: // DIMN, will be read when we create the shape
866       break;
867     case 3: { // ObFL : gradient
868       MWAWGraphicStyle::Gradient grad;
869       if (!m_styleManager->readGradient(entry, grad)) {
870         f << "###sz";
871         break;
872       }
873       if (m_state->m_idToGradientMap.find(entry.id()) != m_state->m_idToGradientMap.end()) {
874         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: the gradient %d already exists\n", entry.id()));
875       }
876       else
877         m_state->m_idToGradientMap[entry.id()]=grad;
878 
879       ascFile.addPos(entry.begin());
880       ascFile.addNote(f.str().c_str());
881       return true;
882     }
883     case 4: // Paln: will be read when we send the data
884       ascFile.addPos(entry.begin());
885       ascFile.addNote(f.str().c_str());
886       return true;
887     case 5: // QkTm: a QuickTime movie? must be read when we send the data
888       break;
889     case 7: { // hatch
890       if (entry.length()<78) {
891         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the hatch data\n"));
892         f << "###sz";
893         break;
894       }
895       long pos=input->tell();
896       float dim[2];
897       for (int i=0; i<2; ++i) {
898         for (float &d : dim) d=float(input->readLong(4))/65536.f;
899         f <<  "dir" << i << "=" << MWAWVec2f(dim[0],dim[1]) << ",";
900       }
901       // CHECKME: normally, there is also some dash properties, where ?
902       librevenge::RVNGString text;
903       if (m_mainParser->readString(text, 60)) // find nothing
904         f << text.cstr() << ",";
905       else
906         f << "###string,";
907       input->seek(pos+76, librevenge::RVNG_SEEK_SET);
908       ascFile.addDelimiter(input->tell(),'|');
909       int N=int(input->readULong(2));
910       f << "N=" << N << ",";
911       if (N<=0 || entry.length()<78+8*N) {
912         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the number of hatch\n"));
913         f << "###sz";
914         break;
915       }
916       ascFile.addPos(pos);
917       ascFile.addNote(f.str().c_str());
918 
919       pos=input->tell();
920       f.str("");
921       f << "Shape-" << entry.id() << "[points," << shape.getTypeName() << "]:";
922       for (int i=0; i<2*N; ++i) {
923         for (float &d : dim) d=float(input->readLong(4))/65536.f;
924         shape.m_points.push_back(MWAWVec2f(dim[0],dim[1]));
925         f << shape.m_points.back() << ((i%2)==0 ? "<->" : ",");
926       }
927       ascFile.addPos(pos);
928       ascFile.addNote(f.str().c_str());
929       return true;
930     }
931     case 8: { // Enve
932       if (entry.length()<24) {
933         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: the enveloppe zone seems bad\n"));
934         f << "###sz";
935         break;
936       }
937       for (int i=0; i<2; ++i) { // 0
938         int val=int(input->readLong(2));
939         if (val)
940           f << "f" << i << "=" << val << ",";
941       }
942       int N=int(input->readULong(4));
943       if (N<2 || (entry.length()-8)/8<N || 8+N*8>entry.length()) {
944         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: the number of points seems bad\n"));
945         f << "###N=" << N << ",";
946         break;
947       }
948       f << "points=[";
949       for (int i=0; i<N; ++i) {
950         float dim[2];
951         for (float &d : dim) d=float(input->readLong(4))/65536.f;
952         shape.m_points.push_back(MWAWVec2f(dim[1],dim[0]));
953         f << shape.m_points.back() << ",";
954       }
955       f << "],";
956       if (input->tell()!=entry.end())
957         ascFile.addDelimiter(input->tell(),'|');
958       ascFile.addPos(entry.begin());
959       ascFile.addNote(f.str().c_str());
960       return true;
961     }
962     case 10: { // OLnk
963       long pos=input->tell();
964       if (entry.length()==10) { // special child of DIMN used to keep the relation between 2 lines?
965         int val=int(input->readULong(2));
966         if (val!=1)
967           f << "f0=" << val << ",";
968         for (int i=0; i<2; ++i) {
969           shape.m_childs.push_back(int(input->readULong(2)));
970           f << "child" << i << "=S" << shape.m_childs.back() << ",";
971         }
972         ascFile.addDelimiter(input->tell(),'|');
973         ascFile.addPos(pos);
974         ascFile.addNote(f.str().c_str());
975         return true;
976       }
977       if (entry.length()<136) {
978         MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the line connector data\n"));
979         f << "###sz";
980         break;
981       }
982       MWAWVec2f pts[4];
983       for (int i=0; i<4; ++i) {
984         float dim[2];
985         for (float &d : dim) d=float(input->readLong(4))/65536.f;
986         pts[i]=MWAWVec2f(dim[1],dim[0]);
987         f << pts[i] << ((i%2)==0 ? "<->" : ",");
988       }
989       for (int i=0; i<3; ++i) { // f0=small number
990         int val=int(input->readLong(2));
991         if (val)
992           f << "f" << i << "=" << val << ",";
993       }
994       int type=int(input->readLong(2)); // normally 0-4, 4 is the stair connector
995       f << "type=" << type << ",";
996       if (type!=4)
997         shape.m_points= {pts[0],pts[1]};
998       else {
999         float c=(pts[0][0]+pts[1][0])/2;
1000         shape.m_points= {pts[0], MWAWVec2f(c,pts[0][1]), MWAWVec2f(c,pts[1][1]), pts[1]};
1001       }
1002       ascFile.addDelimiter(input->tell(),'|');
1003       ascFile.addPos(pos);
1004       ascFile.addNote(f.str().c_str());
1005       return true;
1006     }
1007     default:
1008       f << "###";
1009       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: reading data of a special %d shape is not implemented\n", specialId));
1010       break;
1011     }
1012     break;
1013   }
1014   case 55: // will be read by getBitmap
1015     break;
1016   case 56: { // bdbox, points
1017     int N=int(input->readULong(2));
1018     if (N>entry.length()) // can happen when a file is converted between mac and windows
1019       N=(N>>8)|((N&0xff)<<8);
1020     if (N<4 || N>entry.length() || (N%4)!=2) {
1021       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the number of points of a polydata\n"));
1022       f << "###N=" << N << ",";
1023       break;
1024     }
1025     N/=4;
1026     if (isWindows)
1027       input->setReadInverted(false);
1028     f << "pts=[";
1029     for (int i=0; i<N; ++i) {
1030       float dim[2];
1031       for (auto &d : dim) d=float(input->readLong(2));
1032       MWAWVec2f pt(dim[1], dim[0]);
1033       if (i>=2)
1034         shape.m_points.push_back(pt);
1035       f << pt << ",";
1036     }
1037     f << "],";
1038     if (isWindows)
1039       input->setReadInverted(true);
1040     break;
1041   }
1042   case 99: { // group
1043     int N=int(input->readULong(2));
1044     if (2+2*N>entry.length()) {
1045       MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: can not find the number of childs\n"));
1046       f << "###N=" << N << ",";
1047       break;
1048     }
1049     f << "childs=[";
1050     for (int i=0; i<N; ++i) {
1051       shape.m_childs.push_back(int(input->readULong(2)));
1052       f << "S" << shape.m_childs.back() << ",";
1053     }
1054     f << "],";
1055     break;
1056   }
1057   default:
1058     f << "###type,";
1059     MWAW_DEBUG_MSG(("CanvasGraph::readShapeData: unexpected type=%d\n", shape.m_type));
1060     break;
1061   }
1062   if (input->tell()!=entry.begin() && input->tell()!=entry.end())
1063     ascFile.addDelimiter(input->tell(),'|');
1064   ascFile.addPos(entry.begin());
1065   ascFile.addNote(f.str().c_str());
1066 
1067   return true;
1068 }
1069 
getBitmap(CanvasGraphInternal::Shape const & shape,MWAWEmbeddedObject & obj)1070 bool CanvasGraph::getBitmap(CanvasGraphInternal::Shape const &shape, MWAWEmbeddedObject &obj)
1071 {
1072   bool const isWindows=m_mainParser->isWindowsFile();
1073   if (!isWindows) {
1074     if (shape.m_values[0]==1) // normally, must not happen...
1075       return getBitmapBW(shape, obj);
1076     if (shape.m_values[0]<=0) {
1077       MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: unexpected depth=%d\n", shape.m_values[0]));
1078       return false;
1079     }
1080   }
1081 
1082   MWAWInputStreamPtr input = getInput();
1083   if (!input || !shape.m_entry.valid() || !input->checkPosition(shape.m_entry.end())) {
1084     MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: the entry size seems bad\n"));
1085     return false;
1086   }
1087   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1088   libmwaw::DebugStream f;
1089   f << "Entries(BitmapCol):";
1090   input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1091 
1092   int nBitsByPixel=0;
1093   MWAWVec2i dim;
1094   int width;
1095   std::vector<MWAWColor> colors;
1096   if (!isWindows) {
1097     nBitsByPixel=shape.m_values[0];
1098     dim=MWAWBox2i(shape.m_box).size();
1099     int scale=std::abs(shape.m_bitmapType);
1100     dim *= scale;
1101     width=(nBitsByPixel*dim[0]+7)/8;
1102     if (width&1) ++width;
1103     if (width*dim[1]!=shape.m_entry.length()) {
1104       MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: unexpected size\n"));
1105       f << "###" << width << "x" << dim[1] << "!=" << shape.m_entry.length();
1106       ascFile.addPos(shape.m_entry.begin());
1107       ascFile.addNote(f.str().c_str());
1108       return false;
1109     }
1110   }
1111   else {
1112     long headerSize=int(input->readULong(4));
1113     width=int(input->readULong(2));
1114     f << "w=" << width << ",";
1115     int val=int(input->readLong(2)); // 0|-1
1116     if (val) f << "f0=" << val << ",";
1117     int numColors=int(input->readULong(2));
1118     if (numColors==2)
1119       return getBitmapBW(shape, obj);
1120     val=int(input->readLong(2)); // 0|-1
1121     if (val) f << "f1=" << val << ",";
1122     val=int(input->readULong(4)); // probably a local data size, ie. size before colors
1123     if (val!=0x28) f << "f2=" << val << ",";
1124     int dims[2];
1125     for (auto &d:dims) d=int(input->readLong(4));
1126     dim=MWAWVec2i(dims[0],dims[1]);
1127     f << "dim=" << dim << ",";
1128     val=int(input->readLong(2));
1129     if (val!=1) f << "f2=" << val << ",";
1130     nBitsByPixel=int(input->readLong(2));
1131     if (nBitsByPixel!=8)
1132       f << "num[bits/pixel]=" << nBitsByPixel << ",";
1133     if (val<=0) {
1134       MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: unexpected depth\n"));
1135       f << "###";
1136       ascFile.addPos(shape.m_entry.begin());
1137       ascFile.addNote(f.str().c_str());
1138       return false;
1139     }
1140     if (dim[0]<=0 || dim[1]<=0 || width<(dim[0]*nBitsByPixel+7)/8 ||
1141         headerSize<52+4*numColors || width*dim[1]+headerSize!=shape.m_entry.length()) {
1142       MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: unexpected size\n"));
1143       f << "###";
1144       ascFile.addPos(shape.m_entry.begin());
1145       ascFile.addNote(f.str().c_str());
1146       return false;
1147     }
1148     ascFile.addDelimiter(input->tell(), '|');
1149     input->seek(shape.m_entry.begin()+52, librevenge::RVNG_SEEK_SET);
1150     ascFile.addDelimiter(input->tell(), '|');
1151     for (int i=0; i<numColors; ++i) {
1152       unsigned char col[4];
1153       for (auto &c : col) c=(unsigned char)(input->readULong(1));
1154       colors.push_back(MWAWColor(col[2],col[1],col[0]));
1155       f << colors.back() << ",";
1156     }
1157     input->seek(shape.m_entry.begin()+headerSize, librevenge::RVNG_SEEK_SET);
1158   }
1159   ascFile.addPos(shape.m_entry.begin());
1160   ascFile.addNote(f.str().c_str());
1161 
1162   if (nBitsByPixel!=4 && nBitsByPixel!=8 && nBitsByPixel!=24 && nBitsByPixel!=32) {
1163     MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: find unexpected depth=%d\n", nBitsByPixel));
1164     return false;
1165   }
1166   if (nBitsByPixel==4 || nBitsByPixel==8) {
1167     std::vector<MWAWColor> const &fColors= isWindows ? colors : m_styleManager->getColorsList();
1168     int numColors=int(fColors.size());
1169     if (numColors<2) {
1170       MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: can not find the picture colors\n"));
1171       return false;
1172     }
1173 
1174     MWAWPictBitmapIndexed pict(dim);
1175     pict.setColors(fColors);
1176     for (int y=0; y<dim[1]; ++y) {
1177       long pos=input->tell();
1178       f.str("");
1179       f << "BitmapCol" << y << "]:";
1180       for (int w=0; w<dim[0];) {
1181         int value=int(input->readULong(1));
1182         for (int st=0; st<2; ++st) {
1183           if (w>=dim[0]) break;
1184           int val;
1185           if (nBitsByPixel==8) {
1186             if (st==1)
1187               break;
1188             val=value;
1189           }
1190           else
1191             val=(st==0) ? (value>>4) : (value&0xf);
1192           if (val>numColors) {
1193             static bool first=true;
1194             if (first) {
1195               MWAW_DEBUG_MSG(("CanvasGraph::getBitmap: find unexpected indices\n"));
1196               first=false;
1197             }
1198             pict.set(w, isWindows ? dim[1]-1-y : y, 0);
1199           }
1200           else
1201             pict.set(w, isWindows ? dim[1]-1-y : y, val);
1202           ++w;
1203         }
1204       }
1205       ascFile.addPos(pos);
1206       ascFile.addNote(f.str().c_str());
1207       input->seek(pos+width, librevenge::RVNG_SEEK_SET);
1208     }
1209     return pict.getBinary(obj);
1210   }
1211 
1212   MWAWPictBitmapColor pict(dim, nBitsByPixel==32);
1213   for (int y=0; y<dim[1]; ++y) {
1214     long pos=input->tell();
1215     f.str("");
1216     f << "BitmapCol" << y << "]:";
1217     unsigned char cols[4]= {0,0,0,0};
1218     for (int w=0; w<dim[0]; ++w) {
1219       for (int c=0; c<nBitsByPixel/8; ++c) cols[c]=(unsigned char)(input->readULong(1));
1220       if (nBitsByPixel==32)
1221         pict.set(w, isWindows ? dim[1]-1-y : y, MWAWColor(cols[1], cols[2], cols[3], (unsigned char)(255-cols[0])));
1222       else
1223         pict.set(w, isWindows ? dim[1]-1-y : y,
1224                  isWindows ? MWAWColor(cols[2], cols[1], cols[0]) : MWAWColor(cols[0], cols[1], cols[2]));
1225     }
1226     ascFile.addPos(pos);
1227     ascFile.addNote(f.str().c_str());
1228     input->seek(pos+width, librevenge::RVNG_SEEK_SET);
1229   }
1230   return pict.getBinary(obj);
1231 }
1232 
getBitmapBW(CanvasGraphInternal::Shape const & shape,MWAWEmbeddedObject & obj)1233 bool CanvasGraph::getBitmapBW(CanvasGraphInternal::Shape const &shape, MWAWEmbeddedObject &obj)
1234 {
1235   bool const isWindows=m_mainParser->isWindowsFile();
1236   MWAWInputStreamPtr input = getInput();
1237   if (!input || !shape.m_entry.valid() || !input->checkPosition(shape.m_entry.end()) ||
1238       (isWindows&&shape.m_entry.length()<60)) {
1239     MWAW_DEBUG_MSG(("CanvasGraph::getBitmapBW: the entry size seems bad\n"));
1240     return false;
1241   }
1242   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1243   libmwaw::DebugStream f;
1244   f << "Entries(BitmapBW):";
1245   MWAWVec2i dim;
1246   int width=0;
1247   std::vector<MWAWColor> colors;
1248 
1249   input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1250   if (!isWindows) {
1251     dim=MWAWBox2i(shape.m_box).size();
1252     int scale=std::abs(shape.m_bitmapType);
1253     dim *= scale;
1254     width=(dim[0]+7)/8;
1255     if (width&1) ++width;
1256     if (width*dim[1]!=shape.m_entry.length()) {
1257       MWAW_DEBUG_MSG(("CanvasGraph::getBitmapBW: unexpected size\n"));
1258       f << "###" << width << "x" << dim[1] << "!=" << shape.m_entry.length();
1259       ascFile.addPos(shape.m_entry.begin());
1260       ascFile.addNote(f.str().c_str());
1261       return false;
1262     }
1263     colors.push_back(shape.m_colors[0]);
1264     colors.push_back(MWAWColor::white());
1265   }
1266   else {
1267     long headerSize=int(input->readULong(4));
1268     if (headerSize!=60)
1269       f << "header[size]=" << headerSize << ",";
1270     width=int(input->readULong(2));
1271     f << "w=" << width << ",";
1272     int val=int(input->readLong(2)); // 0|-1
1273     if (val) f << "f0=" << val << ",";
1274     int numColors=int(input->readULong(2));
1275     if (numColors!=2) {
1276       MWAW_DEBUG_MSG(("CanvasGraph::getBitmapBW: the number of colors seems bad\n"));
1277       f << "##num[colors]=" << numColors << ",";
1278     }
1279     val=int(input->readLong(2)); // 0|-1
1280     if (val) f << "f1=" << val << ",";
1281     val=int(input->readULong(4)); // probably a local data size, ie. size before colors
1282     if (val!=0x28)
1283       f << "f2=" << val << ",";
1284     int dims[2];
1285     for (auto &d:dims) d=int(input->readLong(4));
1286     dim=MWAWVec2i(dims[0],dims[1]);
1287     f << "dim=" << dim << ",";
1288     for (int i=0; i<2; ++i) { // f1=numPlanes?, f2=num bits by pixel
1289       val=int(input->readLong(2));
1290       if (val!=1)
1291         f << "f" << i+3 << "=" << val << ",";
1292     }
1293     if (dim[0]<=0 || dim[1]<=0 || width<dim[0]/8 ||
1294         headerSize<60 || width*dim[1]+headerSize!=shape.m_entry.length()) {
1295       MWAW_DEBUG_MSG(("CanvasGraph::getBitmapBW: unexpected size\n"));
1296       f << "###";
1297       ascFile.addPos(shape.m_entry.begin());
1298       ascFile.addNote(f.str().c_str());
1299       return false;
1300     }
1301     ascFile.addDelimiter(input->tell(), '|');
1302     input->seek(shape.m_entry.begin()+52, librevenge::RVNG_SEEK_SET);
1303     ascFile.addDelimiter(input->tell(), '|');
1304     colors.resize(2);
1305     for (size_t i=0; i<2; ++i) {
1306       unsigned char col[4];
1307       for (auto &c : col) c=(unsigned char)(input->readULong(1));
1308       colors[1-i]=MWAWColor(col[0],col[1],col[2]);
1309       f << colors[1-i] << ",";
1310     }
1311     input->seek(shape.m_entry.begin()+headerSize, librevenge::RVNG_SEEK_SET);
1312   }
1313   ascFile.addPos(shape.m_entry.begin());
1314   ascFile.addNote(f.str().c_str());
1315 
1316   MWAWPictBitmapIndexed pict(dim);
1317   pict.setColors(colors);
1318   for (int y=0; y<dim[1]; ++y) {
1319     long pos=input->tell();
1320     f.str("");
1321     f << "BitmapBW" << y << "]:";
1322     int x=0;
1323     for (int w=0; w<width; ++w) {
1324       int val=int(input->readULong(1));
1325       for (int v=0, depl=0x80; v<8; ++v, depl>>=1) {
1326         if (x>=dim[0])
1327           break;
1328         pict.set(x++,isWindows ? dim[1]-1-y : y, (val&depl) ? 0 : 1);
1329       }
1330     }
1331     ascFile.addPos(pos);
1332     ascFile.addNote(f.str().c_str());
1333     input->seek(pos+width, librevenge::RVNG_SEEK_SET);
1334   }
1335   return pict.getBinary(obj);
1336 }
1337 
readFileBitmap(long length)1338 bool CanvasGraph::readFileBitmap(long length)
1339 {
1340   MWAWInputStreamPtr input = getInput();
1341   long pos=input ? input->tell() : 0;
1342   long endPos=pos+length;
1343   if (!input || !input->checkPosition(endPos) || length<40) {
1344     MWAW_DEBUG_MSG(("CanvasGraph::readFileBitmap: the zone seems to short\n"));
1345     return false;
1346   }
1347   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1348   libmwaw::DebugStream f;
1349   f << "Entries(FileBitmap):";
1350 
1351   long headerSize=long(input->readULong(4));
1352   int dims[2];
1353   for (auto &d : dims) d=int(input->readULong(4));
1354   MWAWVec2i dim(dims[0], dims[1]);
1355   f << "dim=" << dim << ",";
1356   if (dim[0]<=0 || dim[1]<=0 || length<=0 || headerSize<40) {
1357     MWAW_DEBUG_MSG(("CanvasGraph::readFileBitmap: can not read the bitmap definition\n"));
1358     f << "###";
1359     ascFile.addPos(pos);
1360     ascFile.addNote(f.str().c_str());
1361     if (length<=0 || !input->checkPosition(endPos))
1362       return false;
1363     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1364     return true;
1365   }
1366 
1367   int val=int(input->readLong(2));
1368   if (val!=1) f << "type?=" << val << ",";
1369   int nBytes=int(input->readULong(2));
1370   if (nBytes==4)
1371     f << "n[bytes]=4,";
1372   else if (nBytes!=8) {
1373     MWAW_DEBUG_MSG(("CanvasGraph::readFileBitmap: unknown number of bytes\n"));
1374     f << "###n[bytes]=" << nBytes << ",";
1375     ascFile.addPos(pos);
1376     ascFile.addNote(f.str().c_str());
1377     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1378     return true;
1379   }
1380   int const width=nBytes==4 ? 4*((dim[0]/2+3)/4) : 4*((dim[0]+3)/4); // align to 4 bytes
1381   int const numColors=nBytes==4 ? 16 : 256;
1382   if (length<headerSize+4*numColors+width*dim[1]) {
1383     MWAW_DEBUG_MSG(("CanvasGraph::readFileBitmap: can not read the bitmap definition\n"));
1384     f << "###";
1385     ascFile.addPos(pos);
1386     ascFile.addNote(f.str().c_str());
1387     input->seek(endPos, librevenge::RVNG_SEEK_SET);
1388     return true;
1389   }
1390   ascFile.addDelimiter(input->tell(), '|');
1391   ascFile.addPos(pos);
1392   ascFile.addNote(f.str().c_str());
1393   input->seek(pos+4+headerSize, librevenge::RVNG_SEEK_SET);
1394 
1395   pos=input->tell();
1396   std::vector<MWAWColor> colors;
1397   colors.reserve(size_t(numColors));
1398   for (int i=0; i<numColors; ++i) {
1399     unsigned char col[4];
1400     for (auto &c : col) c=(unsigned char)(input->readULong(1));
1401     colors.push_back(MWAWColor(col[0], col[1], col[2], (unsigned char)(255-col[3])));
1402   }
1403   MWAWPictBitmapIndexed pict(dim);
1404   pict.setColors(colors);
1405   for (int y=0; y<dim[1]; ++y) {
1406     long bPos=input->tell();
1407     if (nBytes==4) {
1408       for (int w=0; w<dim[0]; w+=2) {
1409         val=int(input->readULong(1));
1410         pict.set(w, y, (val>>4));
1411         if (w+1<dim[0])
1412           pict.set(w+1, y, (val&0xf));
1413       }
1414     }
1415     else {
1416       for (int w=0; w<dim[0]; ++w) {
1417         val=int(input->readULong(1));
1418         pict.set(w, y, val);
1419       }
1420     }
1421     input->seek(bPos+width, librevenge::RVNG_SEEK_SET);
1422   }
1423   ascFile.skipZone(pos, endPos-1);
1424   input->seek(endPos, librevenge::RVNG_SEEK_SET);
1425 #ifdef DEBUG_WITH_FILES
1426   MWAWEmbeddedObject obj;
1427   if (pict.getBinary(obj) && !obj.m_dataList.empty())
1428     libmwaw::Debug::dumpFile(obj.m_dataList[0], "file.png");
1429 #endif
1430   return true;
1431 }
1432 
getPicture(CanvasGraphInternal::Shape const & shape,MWAWEmbeddedObject & obj)1433 bool CanvasGraph::getPicture(CanvasGraphInternal::Shape const &shape, MWAWEmbeddedObject &obj)
1434 {
1435   MWAWInputStreamPtr input = getInput();
1436   if (!input || !shape.m_entry.valid() || !input->checkPosition(shape.m_entry.end())) {
1437     MWAW_DEBUG_MSG(("CanvasGraph::getPicture: the entry size seems bad\n"));
1438     return false;
1439   }
1440   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1441   input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1442   MWAWBox2f box;
1443   int dSz=int(shape.m_entry.length());
1444   auto res = MWAWPictData::check(input, dSz, box);
1445   if (res == MWAWPict::MWAW_R_BAD) {
1446     MWAW_DEBUG_MSG(("CanvasGraph::getPicture:: can not find the picture\n"));
1447     ascFile.addPos(shape.m_entry.begin());
1448     ascFile.addNote("Entries(Picture):###");
1449     return false;
1450   }
1451   input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1452   std::shared_ptr<MWAWPict> thePict(MWAWPictData::get(input, dSz));
1453   bool ok=thePict && thePict->getBinary(obj);
1454 #ifdef DEBUG_WITH_FILES
1455   librevenge::RVNGBinaryData file;
1456   input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET);
1457   input->readDataBlock(dSz, file);
1458   static int volatile pictName = 0;
1459   libmwaw::DebugStream s;
1460   s << "PICT-" << ++pictName << ".pct";
1461   libmwaw::Debug::dumpFile(file, s.str().c_str());
1462   if (!ok) {
1463     ascFile.addPos(shape.m_entry.begin());
1464     ascFile.addNote("Entries(Picture):###");
1465   }
1466   else
1467     ascFile.skipZone(shape.m_entry.begin(), shape.m_entry.begin()-1+dSz);
1468 #endif
1469   return ok;
1470 }
1471 ////////////////////////////////////////////////////////////
1472 //
1473 // Low level
1474 //
1475 ////////////////////////////////////////////////////////////
1476 
markSent(int id)1477 void CanvasGraph::markSent(int id)
1478 {
1479   if (id<=0) return;
1480 #ifdef DEBUG
1481   auto const it=m_state->m_idToShapeMap.find(id);
1482   if (it==m_state->m_idToShapeMap.end() || it->second.m_sent) {
1483     MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find shape %d\n", id));
1484     return;
1485   }
1486   auto const &shape=it->second;
1487   shape.m_sent=true;
1488   markSent(shape.m_child);
1489   markSent(shape.m_origChild);
1490   for (auto const &cId : shape.m_childs)
1491     markSent(cId);
1492 #endif
1493 }
1494 
checkUnsent() const1495 void CanvasGraph::checkUnsent() const
1496 {
1497 #ifdef DEBUG
1498   bool first=true;
1499   for (auto const &it : m_state->m_idToShapeMap) {
1500     if (it.first<3 || it.second.m_sent || it.second.m_type==100)
1501       continue;
1502     if (first) {
1503       first=false;
1504       std::cerr << "Find unsent graphs:";
1505     }
1506     std::cerr << it.first << ":" << it.second.m_type << ",";
1507   }
1508   if (!first)
1509     std::cerr << "\n";
1510 #endif
1511 }
1512 
1513 ////////////////////////////////////////////////////////////
1514 // send data to the listener
1515 ////////////////////////////////////////////////////////////
1516 
update(CanvasGraphInternal::Shape const & shape,MWAWGraphicStyle & style) const1517 void CanvasGraph::update(CanvasGraphInternal::Shape const &shape, MWAWGraphicStyle &style) const
1518 {
1519   style.m_lineWidth=(shape.m_penSize[0]+shape.m_penSize[1])/2;
1520   for (int st=0; st<2; ++st) {
1521     // no need to compute surface style
1522     if (st==1 && shape.m_type==3)
1523       break;
1524     if (shape.m_patterns[st]==0) {
1525       // no color
1526       if (st==0)
1527         style.m_lineWidth=0;
1528       continue;
1529     }
1530     if (st==0) {
1531       if (!shape.m_dashWidth.empty())
1532         style.m_lineDashWidth=shape.m_dashWidth;
1533       else if (shape.m_dash!=1) {
1534         switch (shape.m_dash) {
1535         case 2: // 4-4
1536         case 3: // 4-2
1537         case 4: // 8-2
1538           style.m_lineDashWidth.resize(2);
1539           style.m_lineDashWidth[0]=(shape.m_dash==3 ? 8 : 4);
1540           style.m_lineDashWidth[1]=(shape.m_dash==2 ? 4 : 2);
1541           break;
1542         case 5: // 8-1,2-1
1543         case 6: // 8-2,4-2
1544           style.m_lineDashWidth.resize(4);
1545           style.m_lineDashWidth[0]=8;
1546           style.m_lineDashWidth[1]=(shape.m_dash==5 ? 1 : 2);
1547           style.m_lineDashWidth[2]=(shape.m_dash==5 ? 2 : 4);
1548           style.m_lineDashWidth[3]=(shape.m_dash==5 ? 1 : 2);
1549           break;
1550         case 7:
1551           style.m_lineDashWidth= {8,1,2,1,2,1};
1552           break;
1553         default:
1554           MWAW_DEBUG_MSG(("CanvasGraph::update[style]: unknown dash style=%d\n", shape.m_dash));
1555           break;
1556         }
1557       }
1558     }
1559     if (shape.m_patterns[st]<155) {
1560       MWAWGraphicStyle::Pattern pat;
1561       if (!m_styleManager->get(shape.m_patterns[st]-1, pat)) {
1562         MWAW_DEBUG_MSG(("CanvasGraph::update[style]: can not find patterns %d\n", shape.m_patterns[st]));
1563       }
1564       else {
1565         for (int i=0; i<2; ++i)
1566           pat.m_colors[1-i]=shape.m_colors[i];
1567         if (st==0)
1568           pat.getAverageColor(style.m_lineColor);
1569         else
1570           style.setPattern(pat);
1571       }
1572     }
1573     else {
1574       float percent=float(255-shape.m_patterns[st])/100.f;
1575       MWAWColor finalColor=MWAWColor::barycenter(percent, shape.m_colors[1], 1.f-percent, shape.m_colors[0]);
1576       if (st==0)
1577         style.m_lineColor=finalColor;
1578       else
1579         style.setSurfaceColor(finalColor);
1580     }
1581   }
1582   if (shape.m_type==3) {
1583     // TODO: find where the arrow are stored in the arc's shape
1584     int fl=shape.m_values[1];
1585     if (fl&1) {
1586       style.m_arrows[1]=shape.m_arrow;
1587       style.m_arrows[1].m_width*=float(style.m_lineWidth);
1588     }
1589     if (fl&2) {
1590       style.m_arrows[0]=shape.m_arrow;
1591       style.m_arrows[0].m_width*=float(style.m_lineWidth);
1592     }
1593   }
1594 }
1595 
send(CanvasGraphInternal::Shape const & shape,CanvasGraphInternal::LocalTransform const * local)1596 bool CanvasGraph::send(CanvasGraphInternal::Shape const &shape, CanvasGraphInternal::LocalTransform const *local)
1597 {
1598   MWAWGraphicListenerPtr listener=m_parserState->m_graphicListener;
1599   if (!listener) {
1600     MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find the listener\n"));
1601     return false;
1602   }
1603   if (shape.m_sent) {
1604     MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: find an already sent shape\n"));
1605     return false;
1606   }
1607   shape.m_sent=true;
1608 
1609   int const vers=version();
1610   MWAWPosition pos=local ? local->m_position : MWAWPosition(shape.m_box[0], shape.m_box.size(), librevenge::RVNG_POINT);
1611   pos.m_anchorTo = MWAWPosition::Page;
1612 
1613   if (shape.m_type==99) {
1614     if (shape.m_childs.size()>1)
1615       listener->openGroup(pos);
1616     for (auto const id : shape.m_childs) {
1617       auto const it=m_state->m_idToShapeMap.find(id);
1618       if (it==m_state->m_idToShapeMap.end()) {
1619         MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find shape %d\n", id));
1620         continue;
1621       }
1622       send(it->second);
1623     }
1624     if (shape.m_childs.size()>1)
1625       listener->closeGroup();
1626     return true;
1627   }
1628 
1629   MWAWGraphicStyle style;
1630   if (local)
1631     style=local->m_style;
1632   else
1633     update(shape, style);
1634   style.m_rotate=-float(shape.m_rotation);
1635   int hatchGradChild=shape.m_hatchGradChild;
1636   if (hatchGradChild>0 && shape.getSpecialId()==-1) { // look for a gradient
1637     int cChild=hatchGradChild;
1638     std::set<int> found;
1639     while (cChild>0) {
1640       if (found.find(cChild)!=found.end()) {
1641         MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: find loop in hatch/grad child\n"));
1642         break;
1643       }
1644       found.insert(cChild);
1645       auto const it=m_state->m_idToShapeMap.find(cChild);
1646       if (it==m_state->m_idToShapeMap.end()) {
1647         MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find hatch/grad child=%d\n", cChild));
1648         break;
1649       }
1650       int cId=it->second.getSpecialId();
1651       if (cId==3) { // gradient
1652         auto const &gIt=m_state->m_idToGradientMap.find(cChild);
1653         if (gIt!=m_state->m_idToGradientMap.end())
1654           style.m_gradient=gIt->second;
1655         else {
1656           MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find gradient=%d\n", cChild));
1657         }
1658         break;
1659       }
1660       cChild=it->second.m_hatchGradChild;
1661     }
1662   }
1663 
1664   CanvasGraphInternal::LocalTransform lTransform(pos, style);
1665   // first check if we need to use the original shape
1666   if (shape.m_origChild) {
1667     auto const it=m_state->m_idToShapeMap.find(shape.m_origChild);
1668     if (it==m_state->m_idToShapeMap.end()) {
1669       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find the original child\n"));
1670     }
1671     // check if original contain text or if original is a bitmap
1672     // TODO: do we need to use original if the child is a group?
1673     else if (it->second.m_type==2 || (it->second.m_type>=4 && it->second.m_type<=5 && it->second.m_entry.valid())) {
1674       send(it->second, &lTransform);
1675       // TODO: if the form is skewed, distorted, we need to retrieve the shape.m_child to draw the original shape in the shape.m_child :-~
1676       markSent(shape.m_child);
1677       return true;
1678     }
1679     else if (shape.m_type!=18)
1680       markSent(shape.m_origChild);
1681   }
1682   // now look if the shape has a more precise child, if yes, use it
1683   if (shape.m_child) {
1684     auto const it=m_state->m_idToShapeMap.find(shape.m_child);
1685     if (it==m_state->m_idToShapeMap.end()) {
1686       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find a child\n"));
1687     }
1688     else {
1689       send(it->second, &lTransform);
1690       return true;
1691     }
1692   }
1693 
1694   bool isSent=false;
1695   MWAWGraphicShape finalShape;
1696   switch (shape.m_type) {
1697   case 2: {
1698     std::shared_ptr<MWAWSubDocument> doc(new CanvasGraphInternal::SubDocument(*this, getInput(), shape.m_entry.id()));
1699     listener->insertTextBox(pos, doc, style);
1700     isSent=true;
1701     break;
1702   }
1703   case 3: // line
1704     if (shape.m_points.size()!=2) {
1705       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: oops can not find the line's points\n"));
1706       return false;
1707     }
1708     finalShape=MWAWGraphicShape::line(shape.m_points[0], shape.m_points[1]);
1709     if (shape.m_values[1]&4) { // measure
1710       listener->openGroup(pos);
1711       listener->insertShape(pos, finalShape, style);
1712 
1713       MWAWVec2f lineSz=pos.size();
1714       MWAWVec2f center=pos.origin() + 0.5f*lineSz;
1715       MWAWPosition measurePos(pos);
1716       measurePos.setOrigin(center-MWAWVec2f(30,6));
1717       measurePos.setSize(MWAWVec2f(60,12));
1718       measurePos.setOrder(pos.order()+1);
1719       std::stringstream s;
1720       s << std::setprecision(0) << std::fixed << std::sqrt(lineSz[0]*lineSz[0]+lineSz[1]*lineSz[1]) << " pt";
1721       std::shared_ptr<MWAWSubDocument> doc(new CanvasGraphInternal::SubDocument(*this, getInput(), librevenge::RVNGString(s.str().c_str())));
1722       MWAWGraphicStyle measureStyle;
1723       measureStyle.m_lineWidth=0;
1724       measureStyle.setSurfaceColor(MWAWColor::white());
1725       listener->insertTextBox(measurePos, doc, measureStyle);
1726 
1727       listener->closeGroup();
1728       isSent=true;
1729     }
1730     break;
1731   case 4: { // rect
1732     MWAWEmbeddedObject obj;
1733     if (shape.m_entry.valid() && getBitmapBW(shape, obj)) {
1734       listener->insertPicture(pos, obj, style);
1735       isSent=true;
1736       break;
1737     }
1738     finalShape=MWAWGraphicShape::rectangle(shape.m_box);
1739     break;
1740   }
1741   case 5: { // rectOval
1742     MWAWEmbeddedObject obj;
1743     finalShape=MWAWGraphicShape::rectangle(shape.m_box, MWAWVec2f(float(shape.m_values[0])/2.f, float(shape.m_values[1])/2.f));
1744     if (shape.m_entry.valid() && getBitmapBW(shape, obj)) {
1745       if (style.hasSurface())
1746         listener->insertShape(pos, finalShape, style);
1747       listener->insertPicture(pos, obj, style);
1748       isSent=true;
1749     }
1750     break;
1751   }
1752   case 6: // circle
1753     finalShape=MWAWGraphicShape::circle(shape.m_box);
1754     break;
1755   case 7: { // arc
1756     int angle[2] = { int(90-shape.m_values[0]-shape.m_values[1]), int(90-shape.m_values[0]) };
1757     if (shape.m_values[1]<0) {
1758       angle[0]=int(90-shape.m_values[0]);
1759       angle[1]=int(90-shape.m_values[0]-shape.m_values[1]);
1760     }
1761     else if (shape.m_values[1]==360)
1762       angle[0]=int(90-shape.m_values[0]-359);
1763     if (angle[1]>360) {
1764       int numLoop=int(angle[1]/360)-1;
1765       angle[0]-=numLoop*360;
1766       angle[1]-=numLoop*360;
1767       while (angle[1] > 360) {
1768         angle[0]-=360;
1769         angle[1]-=360;
1770       }
1771     }
1772     if (angle[0] < -360) {
1773       int numLoop=int(angle[0]/360)+1;
1774       angle[0]-=numLoop*360;
1775       angle[1]-=numLoop*360;
1776       while (angle[0] < -360) {
1777         angle[0]+=360;
1778         angle[1]+=360;
1779       }
1780     }
1781     // we must compute the real bd box
1782     float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 };
1783     int limitAngle[2];
1784     for (int i = 0; i < 2; ++i)
1785       limitAngle[i] = (angle[i] < 0) ? int(angle[i]/90)-1 : int(angle[i]/90);
1786     for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; ++bord) {
1787       float ang = (bord == limitAngle[0]) ? float(angle[0]) :
1788                   (bord == limitAngle[1]+1) ? float(angle[1]) : float(90 * bord);
1789       ang *= float(M_PI/180.);
1790       float actVal[2] = { std::cos(ang), -std::sin(ang)};
1791       if (actVal[0] < minVal[0]) minVal[0] = actVal[0];
1792       else if (actVal[0] > maxVal[0]) maxVal[0] = actVal[0];
1793       if (actVal[1] < minVal[1]) minVal[1] = actVal[1];
1794       else if (actVal[1] > maxVal[1]) maxVal[1] = actVal[1];
1795     }
1796     MWAWBox2f circleBox=shape.m_box;
1797     // we have the shape box, we need to reconstruct the circle box
1798     if (maxVal[0]>minVal[0] && maxVal[1]>minVal[1]) {
1799       float scaling[2]= { (shape.m_box[1][0]-shape.m_box[0][0])/(maxVal[0]-minVal[0]),
1800                           (shape.m_box[1][1]-shape.m_box[0][1])/(maxVal[1]-minVal[1])
1801                         };
1802       float constant[2]= { shape.m_box[0][0]-minVal[0] *scaling[0], shape.m_box[0][1]-minVal[1] *scaling[1]};
1803       circleBox=MWAWBox2f(MWAWVec2f(constant[0]-scaling[0], constant[1]-scaling[1]),
1804                           MWAWVec2f(constant[0]+scaling[0], constant[1]+scaling[1]));
1805     }
1806     finalShape = MWAWGraphicShape::pie(shape.m_box, circleBox, MWAWVec2f(float(angle[0]), float(angle[1])));
1807     break;
1808   }
1809   case 9: // polyline
1810   case 56: // polydata
1811     if (shape.m_points.size()<2) {
1812       // I find sometimes only one point, probably safe to ignore
1813       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: oops can not find the polyline's points for shape\n"));
1814       return false;
1815     }
1816     if (style.hasSurface())
1817       finalShape=MWAWGraphicShape::polygon(shape.m_box);
1818     else
1819       finalShape=MWAWGraphicShape::polyline(shape.m_box);
1820     finalShape.m_vertices=shape.m_points;
1821     break;
1822   case 10: { // spline
1823     if (shape.m_points.size()<2 || (shape.m_points.size()%(vers==2 ? 2 : 4))!=0) {
1824       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: oops can not find the spline's points\n"));
1825       return false;
1826     }
1827     finalShape=MWAWGraphicShape::path(shape.m_box);
1828     std::vector<MWAWGraphicShape::PathData> &path=finalShape.m_path;
1829     path.push_back(MWAWGraphicShape::PathData('M', shape.m_points[0]));
1830 
1831     if (vers==2) {
1832       for (size_t p=2; p < shape.m_points.size(); p+=2) {
1833         bool hasFirstC=shape.m_points[p-1]!=shape.m_points[p-2];
1834         bool hasSecondC=shape.m_points[p]!=shape.m_points[p+1];
1835         if (!hasFirstC && !hasSecondC)
1836           path.push_back(MWAWGraphicShape::PathData('L', shape.m_points[p]));
1837         else
1838           path.push_back(MWAWGraphicShape::PathData('C', shape.m_points[p], shape.m_points[p-1], shape.m_points[p+1]));
1839       }
1840     }
1841     else { // each extremity is dupplicated, so we have 0-1-2-3 4-5-6-7 with P3=P4(almost alway), ...
1842       for (size_t p=3; p < shape.m_points.size(); p+=4) {
1843         if (p>=4 && shape.m_points[p-4]!=shape.m_points[p-3])
1844           path.push_back(MWAWGraphicShape::PathData('M', shape.m_points[p-3]));
1845         bool hasFirstC=shape.m_points[p-3]!=shape.m_points[p-2];
1846         bool hasSecondC=shape.m_points[p-1]!=shape.m_points[p];
1847         if (!hasFirstC && !hasSecondC)
1848           path.push_back(MWAWGraphicShape::PathData('L', shape.m_points[p]));
1849         else
1850           path.push_back(MWAWGraphicShape::PathData('C', shape.m_points[p], shape.m_points[p-2], shape.m_points[p-1]));
1851       }
1852     }
1853     if (style.hasSurface())
1854       path.push_back(MWAWGraphicShape::PathData('Z'));
1855     break;
1856   }
1857   case 18: {
1858     if (shape.m_origChild==0 && shape.m_entry.valid()) {
1859       MWAWEmbeddedObject obj;
1860       if (getPicture(shape, obj)) {
1861         listener->insertPicture(pos, obj, style);
1862         isSent=true;
1863         break;
1864       }
1865     }
1866     auto const it=m_state->m_idToShapeMap.find(shape.m_origChild);
1867     if (shape.m_origChild<=0 || it==m_state->m_idToShapeMap.end()) {
1868       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find picture container child=%d\n", shape.m_origChild));
1869       return false;
1870     }
1871     send(it->second, &lTransform);
1872     isSent=true;
1873     break;
1874   }
1875   case 52:
1876     sendSpecial(shape, lTransform);
1877     isSent=true;
1878     break;
1879   case 55: {
1880     MWAWEmbeddedObject obj;
1881     if (shape.m_entry.valid() && getBitmap(shape, obj)) {
1882       listener->insertPicture(pos, obj, style);
1883       isSent=true;
1884       break;
1885     }
1886     return false;
1887   }
1888   default:
1889     MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: unknown type=%d\n", shape.m_type));
1890     finalShape=MWAWGraphicShape::rectangle(shape.m_box);
1891     break;
1892   }
1893   if (!isSent)
1894     listener->insertShape(pos, finalShape, style);
1895   if (hatchGradChild>0) {
1896     auto const it=m_state->m_idToShapeMap.find(hatchGradChild);
1897     if (it==m_state->m_idToShapeMap.end()) {
1898       MWAW_DEBUG_MSG(("CanvasGraph::send[shape]: can not find hatch/grad child=%d\n", hatchGradChild));
1899       return false;
1900     }
1901     send(it->second);
1902   }
1903 
1904   return true;
1905 }
1906 
sendDimension(CanvasGraphInternal::Shape const & shape,CanvasGraphInternal::LocalTransform const & local)1907 bool CanvasGraph::sendDimension(CanvasGraphInternal::Shape const &shape, CanvasGraphInternal::LocalTransform const &local)
1908 {
1909   MWAWGraphicListenerPtr listener=m_parserState->m_graphicListener;
1910   if (!listener) {
1911     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: can not find the listener\n"));
1912     return false;
1913   }
1914 
1915   auto const &entry=shape.m_entry;
1916   if (!entry.valid()) {
1917     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: sorry, can not find the data\n"));
1918     return false;
1919   }
1920 
1921   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1922   libmwaw::DebugStream f;
1923   if (entry.length()<384) {
1924     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: the data seens too short\n"));
1925     f << "###sz";
1926     ascFile.addPos(entry.begin());
1927     ascFile.addNote(f.str().c_str());
1928     return false;
1929   }
1930 
1931   auto input=getInput();
1932   input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
1933   int type=int(input->readLong(2)); // 4-15
1934   if (type) f << "type=" << type << ",";
1935   f << "points=[";
1936   std::vector<MWAWVec2f> pts;
1937   for (int i=0; i<18; ++i) {
1938     float dims[2];
1939     // fract type: between -2 and 2
1940     for (auto &d : dims) d=4*float(input->readLong(4))/65536.f/65536.f;
1941     ascFile.addDelimiter(input->tell(),'|');
1942     ascFile.addDelimiter(input->tell()-4,',');
1943     pts.push_back(MWAWVec2f(dims[1],dims[0]));
1944     f << pts.back() << ",";
1945   }
1946   f << "],";
1947   ascFile.addPos(entry.begin());
1948   ascFile.addNote(f.str().c_str());
1949 
1950   input->seek(entry.begin()+146, librevenge::RVNG_SEEK_SET);
1951 
1952   long posi=input->tell();
1953   f.str("");
1954   f << "Shape-" << entry.id() << "[data1," << shape.getTypeName() << "]:";
1955   input->seek(posi+36, librevenge::RVNG_SEEK_SET);
1956   ascFile.addDelimiter(input->tell(),'|');
1957   bool arrowInside=true;
1958   bool hasFrame=false;
1959   int val;
1960   for (int i=0; i<3; ++i) {
1961     val=int(input->readLong(2));
1962     int expected[]= {1,0,0};
1963     if (val==expected[i]) continue;
1964     char const *wh[]= {"arrows[inside]", "text[centered]", "frame[text]"};
1965     if (val==0) {
1966       if (i==0) arrowInside=false;
1967       f << wh[i] << "=off,";
1968     }
1969     else if (val==1) {
1970       if (i==2) hasFrame=true;
1971       f << wh[i] << "=on,";
1972     }
1973     else
1974       f << "###" << wh[i] << "=" << val << ",";
1975   }
1976   for (int i=0; i<6; ++i) {
1977     val=int(input->readLong(2));
1978     int const expected[]= { 1, 1, 1, 0, 3, 1};
1979     if (val==expected[i]) continue;
1980     char const *wh[]= {
1981       "leader",  // none, left, right, automatic
1982       nullptr,
1983       "display[text]", // hori, hori/90, aligned, above, below
1984       "what", // 1: line, 3: arc?
1985       "precision", // X, X.X, X.XX, X.XXX, X.XXXX, X X/X
1986       "tolerance", // none, one, two, limit
1987     };
1988     if (i==3 && val==3)
1989       f << "print[angle],";
1990     else if (wh[i])
1991       f << wh[i] << "=" << val << ",";
1992     else
1993       f << "f" << i << "=" << val << ",";
1994   }
1995   f << "tolerances=[";
1996   for (int i=0; i<2; ++i) f << float(input->readLong(4))/65536.f << ",";
1997   f << "],";
1998   val=int(input->readLong(2));
1999   if (val!=1)
2000     f << "f6=" << val << ",";
2001   ascFile.addPos(posi);
2002   ascFile.addNote(f.str().c_str());
2003   input->seek(posi+64, librevenge::RVNG_SEEK_SET);
2004 
2005   posi=input->tell();
2006   f.str("");
2007   f << "Shape-" << entry.id() << "[format," << shape.getTypeName() << "]:";
2008   librevenge::RVNGString format;
2009   if (m_mainParser->readString(format, 19))
2010     f << "name=" << format.cstr() << ",";
2011   else {
2012     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: can not read the format's name\n"));
2013     f << "###format,";
2014   }
2015   input->seek(posi+20, librevenge::RVNG_SEEK_SET);
2016   for (int i=0; i<13; ++i) { // f2=48|4b|6b, f4=0|1
2017     val=int(input->readLong(2));
2018     int const expected[]= {1, 0, 0x48/* a flag to know what is changed*/, 0, 0, /* scale in=>scale out, 100, 100*/ 1, 0, 1, 0, 100, 0, 100, 0};
2019     if (val==expected[i])
2020       continue;
2021     if (i==4) {
2022       if (val==1)
2023         f << "custom[unit],";
2024       else
2025         f << "###custom[unit]=" << val << ",";
2026     }
2027     else
2028       f << "f" << i << "=" << val << ",";
2029   }
2030   f << "margins?=[";
2031   for (int i=0; i<4; ++i) f << float(input->readLong(4))/65536.f << ",";
2032   f << "],";
2033   f << "margins2?=[";
2034   for (int i=0; i<4; ++i) f << float(input->readLong(4))/65536.f << ",";
2035   f << "],";
2036   for (int i=0; i<6; ++i) { // g5=1
2037     val=int(input->readLong(2));
2038     if (val)
2039       f << "g" << i << "=" << val << ",";
2040   }
2041   ascFile.addPos(posi);
2042   ascFile.addNote(f.str().c_str());
2043   input->seek(posi+90, librevenge::RVNG_SEEK_SET);
2044 
2045   posi=input->tell();
2046   f.str("");
2047   f << "Shape-" << entry.id() << "[data3," << shape.getTypeName() << "]:";
2048   librevenge::RVNGString name;
2049   if (m_mainParser->readString(name, 19))
2050     f << "encoding=" << name.cstr() << ",";
2051   else {
2052     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: can not read the encoding\n"));
2053     f << "###encoding,";
2054   }
2055   input->seek(posi+20, librevenge::RVNG_SEEK_SET);
2056   if (m_mainParser->readString(name, 63))
2057     f << "style=" << name.cstr() << ",";
2058   else {
2059     MWAW_DEBUG_MSG(("CanvasGraph::sendDimension: can not read the style name\n"));
2060     f << "###style,";
2061   }
2062 
2063   ascFile.addPos(posi);
2064   ascFile.addNote(f.str().c_str());
2065 
2066   MWAWVec2f bDir=shape.m_box.size();
2067   for (auto &pt : pts)
2068     pt=shape.m_box[0]+MWAWVec2f(pt[0]*bDir[0], pt[1]*bDir[1]);
2069 
2070   MWAWGraphicStyle style=local.m_style;
2071   MWAWPosition pos;
2072   pos.m_anchorTo = MWAWPosition::Page;
2073 
2074   listener->openGroup(local.m_position);
2075 
2076   MWAWGraphicShape fShape;
2077   MWAWBox2f shapeBox;
2078 
2079   MWAWVec2f textOrigin;
2080   librevenge::RVNGString text;
2081   if (type==12) { // a sector instead of a line
2082     // circle between pts[0], pts[1]->pts[2]
2083     float angles[2];
2084     for (size_t i=0; i<2; ++i) {
2085       MWAWVec2f dir=pts[i+1]-pts[0];
2086       angles[i]=180*std::atan2(-dir[1],dir[0])/float(M_PI);
2087     }
2088     if (angles[1]<angles[0])
2089       std::swap(angles[0],angles[1]);
2090     MWAWVec2f dir=pts[5]-pts[0];
2091     float len=std::sqrt(dir[0]*dir[0]+dir[1]*dir[1]);
2092     MWAWBox2f circleBox(pts[0]-len*MWAWVec2f(1,1), pts[0]+len*MWAWVec2f(1,1));
2093     for (int st=0; st<2; ++st) {
2094       float angle[2];
2095       if (arrowInside) {
2096         if (st==1)
2097           break;
2098         angle[0]=angles[0];
2099         angle[1]=angles[1];
2100       }
2101       else if (st==0) {
2102         angle[0]=angles[0]-10;
2103         angle[1]=angles[0];
2104       }
2105       else {
2106         angle[0]=angles[1];
2107         angle[1]=angles[1]+10;
2108       }
2109       // we must compute the real bd box
2110       float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 };
2111       int limitAngle[2];
2112       for (int i = 0; i < 2; ++i)
2113         limitAngle[i] = (angle[i] < 0) ? int(angle[i]/90)-1 : int(angle[i]/90);
2114       for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; ++bord) {
2115         float ang = (bord == limitAngle[0]) ? float(angle[0]) :
2116                     (bord == limitAngle[1]+1) ? float(angle[1]) : float(90 * bord);
2117         ang *= float(M_PI/180.);
2118         float actVal[2] = { std::cos(ang), -std::sin(ang)};
2119         if (actVal[0] < minVal[0]) minVal[0] = actVal[0];
2120         else if (actVal[0] > maxVal[0]) maxVal[0] = actVal[0];
2121         if (actVal[1] < minVal[1]) minVal[1] = actVal[1];
2122         else if (actVal[1] > maxVal[1]) maxVal[1] = actVal[1];
2123       }
2124       MWAWBox2f arcBox=circleBox;
2125       // we have the shape box, we need to reconstruct the circle box
2126       if (maxVal[0]>minVal[0] && maxVal[1]>minVal[1]) {
2127         float scaling[2]= { (circleBox[1][0]-circleBox[0][0])/(maxVal[0]-minVal[0]),
2128                             (circleBox[1][1]-circleBox[0][1])/(maxVal[1]-minVal[1])
2129                           };
2130         float constant[2]= { circleBox[0][0]-minVal[0] *scaling[0], circleBox[0][1]-minVal[1] *scaling[1]};
2131         arcBox=MWAWBox2f(MWAWVec2f(constant[0]-scaling[0], constant[1]-scaling[1]),
2132                          MWAWVec2f(constant[0]+scaling[0], constant[1]+scaling[1]));
2133       }
2134       style.setSurfaceColor(MWAWColor::white(), 0);
2135       style.m_arrows[st]=arrowInside ? MWAWGraphicStyle::Arrow::plain(): MWAWGraphicStyle::Arrow();
2136       style.m_arrows[1-st]=MWAWGraphicStyle::Arrow::plain();
2137 
2138       fShape = MWAWGraphicShape::arc(arcBox, circleBox, MWAWVec2f(float(angle[0]), float(angle[1])));
2139       shapeBox=fShape.getBdBox();
2140       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2141       listener->insertShape(pos, fShape, style);
2142     }
2143 
2144     // TODO: use format for unit, ...
2145     textOrigin=pts[9];
2146     std::stringstream s;
2147     s << std::setprecision(0) << std::fixed << angles[1]-angles[0] << " ";
2148     text=s.str().c_str();
2149     libmwaw::appendUnicode(0xb0, text);
2150   }
2151   else if (type>12 && type<=14) { // radius/diameter inside an circle/ellipse
2152     size_t orig=type==13 ? 0 : 4;
2153     fShape=MWAWGraphicShape::line(pts[orig],pts[3]);
2154     shapeBox=fShape.getBdBox();
2155     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2156     style.m_arrows[0]=style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
2157     listener->insertShape(pos, fShape, style);
2158 
2159     fShape=MWAWGraphicShape::line(pts[1],pts[3]);
2160     shapeBox=fShape.getBdBox();
2161     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2162     style.m_arrows[0]=style.m_arrows[1]=MWAWGraphicStyle::Arrow();
2163     listener->insertShape(pos, fShape, style);
2164 
2165     textOrigin=pts[1];
2166     // TODO: use format for unit, ...
2167     MWAWVec2f lineSz=pts[orig]-pts[3];
2168     std::stringstream s;
2169     s << std::setprecision(0) << std::fixed << std::sqrt(lineSz[0]*lineSz[0]+lineSz[1]*lineSz[1]) << " pt";
2170     text=s.str().c_str();
2171   }
2172   else if (type==15) { // four segments, no text
2173     for (size_t i=0; i<4; ++i) {
2174       fShape=MWAWGraphicShape::line(pts[1],pts[i+14]);
2175       shapeBox=fShape.getBdBox();
2176       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2177       listener->insertShape(pos, fShape, style);
2178     }
2179   }
2180   else {
2181     for (size_t i=0; i<2; ++i) {
2182       size_t const limits[]= {4,6, 7,9 }; // outside1, outside2
2183       fShape=MWAWGraphicShape::line(pts[limits[2*i]],pts[limits[2*i+1]]);
2184       shapeBox=fShape.getBdBox();
2185       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2186       listener->insertShape(pos, fShape, style);
2187     }
2188 
2189     if (arrowInside) {
2190       style.m_arrows[0]=style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain();
2191       fShape=MWAWGraphicShape::line(pts[5],pts[8]);
2192       shapeBox=fShape.getBdBox();
2193       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2194       listener->insertShape(pos, fShape, style);
2195     }
2196     else {
2197       style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain();
2198       for (size_t i=0; i<2; ++i) {
2199         size_t const limits[]= {5,10, 8,11 }; // arrows1, arrows2
2200         fShape=MWAWGraphicShape::line(pts[limits[2*i]],pts[limits[2*i+1]]);
2201         shapeBox=fShape.getBdBox();
2202         pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2203         listener->insertShape(pos, fShape, style);
2204       }
2205     }
2206 
2207     // sometimes there is also a line to rely pts[5/8] and the text
2208 
2209     textOrigin=pts[3];
2210     // TODO: use format for unit, ...
2211     MWAWVec2f lineSz=pts[5]-pts[8];
2212     std::stringstream s;
2213     s << std::setprecision(0) << std::fixed << std::sqrt(lineSz[0]*lineSz[0]+lineSz[1]*lineSz[1]) << " pt";
2214     text=s.str().c_str();
2215   }
2216 
2217   if (!text.empty()) {
2218     // TODO: use local style to define the text's color...
2219     style=MWAWGraphicStyle();
2220     MWAWPosition measurePos(pos);
2221     measurePos.m_anchorTo = MWAWPosition::Page;
2222     measurePos.setOrigin(textOrigin-MWAWVec2f(30,6));
2223     measurePos.setSize(MWAWVec2f(60,12));
2224     std::shared_ptr<MWAWSubDocument> doc(new CanvasGraphInternal::SubDocument(*this, getInput(), text));
2225     MWAWGraphicStyle measureStyle;
2226     measureStyle.m_lineWidth=hasFrame ? 1 : 0;
2227     measureStyle.setSurfaceColor(MWAWColor::white());
2228     listener->insertTextBox(measurePos, doc, measureStyle);
2229   }
2230   listener->closeGroup();
2231   return true;
2232 }
2233 
sendMultiLines(CanvasGraphInternal::Shape const & shape,CanvasGraphInternal::LocalTransform const & local)2234 bool CanvasGraph::sendMultiLines(CanvasGraphInternal::Shape const &shape, CanvasGraphInternal::LocalTransform const &local)
2235 {
2236   MWAWGraphicListenerPtr listener=m_parserState->m_graphicListener;
2237   if (!listener) {
2238     MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find the listener\n"));
2239     return false;
2240   }
2241 
2242   auto const &entry=shape.m_entry;
2243   auto input=getInput();
2244   if (!entry.valid() || !input->checkPosition(entry.end())) {
2245     MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: sorry, can not find the data\n"));
2246     return false;
2247   }
2248 
2249   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
2250   libmwaw::DebugStream f;
2251   bool const isWindows=m_mainParser->isWindowsFile();
2252   int const headerSize=24+(isWindows ? 2 : 0);
2253   long const lineSize=isWindows ? 182 : 174;
2254   if (entry.length()<headerSize+lineSize) {
2255     MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: the data seens too short\n"));
2256     f << "###sz";
2257     ascFile.addPos(entry.begin());
2258     ascFile.addNote(f.str().c_str());
2259     return false;
2260   }
2261 
2262   input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2263   int numLines=0, numPoints=0, type=0;
2264   bool addEndCaps=false;
2265   int val;
2266   if (isWindows) {
2267     val=int(input->readLong(2));
2268     if (val) f << "f0=" << val << ","; // find 2
2269   }
2270   for (int i=0; i<6; ++i) {
2271     val=int(input->readLong(2));
2272     if (val==0)
2273       continue;
2274     char const *wh[]= {"num[points]", "type" /*17,18,20*/, "num[lines]", "equidistant", "identical", "end[caps]"};
2275     if (wh[i]==nullptr)
2276       f << "f" << i+1 << "=" << val << ",";
2277     else if (i==0) {
2278       numPoints=val;
2279       f << "num[pts]=" << val << ",";
2280     }
2281     else if (i==1) {
2282       type=val;
2283       if (val!=17)
2284         f << "type=" << val << ",";
2285     }
2286     else if (i==2) {
2287       numLines=val;
2288       f << "num[lines]=" << val << ",";
2289     }
2290     else {
2291       if (val==1) {
2292         if (i==5) addEndCaps=true;
2293         f << wh[i] << ",";
2294       }
2295       else
2296         f << "#" << wh[i] << "=" << val << ",";
2297     }
2298   }
2299   float dim[4];
2300   for (float &d : dim) d=float(input->readLong(4))/65536.f;
2301   MWAWVec2f pts[]= {MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2])};
2302   f <<  MWAWBox2f(pts[0], pts[1]) << ",";
2303   if (numLines<=0 || entry.length()<headerSize+numLines*lineSize) {
2304     MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find the paln lines\n"));
2305     f << "###lines";
2306     ascFile.addPos(entry.begin());
2307     ascFile.addNote(f.str().c_str());
2308     return false;
2309   }
2310   ascFile.addPos(entry.begin());
2311   ascFile.addNote(f.str().c_str());
2312   std::vector<float> offsets;
2313   std::vector<MWAWGraphicStyle> styles;
2314   styles.resize(size_t(numLines));
2315   for (int i=0; i<numLines; ++i) {
2316     auto &style=styles[size_t(i)];
2317     long posi=input->tell();
2318     f.str("");
2319     f << "Shape-" << entry.id() << "[line" << i << "," << shape.getTypeName() << "]:";
2320     val=int(input->readLong(2));
2321     int offsetSign=1;
2322     if (val==-1 || val==1) {
2323       offsetSign=-1;
2324       f << "offsetNeg,";
2325     }
2326     else if (val) {
2327       MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: unknown offset sign\n"));
2328       f << "###offsetSign=" << val << ",";
2329     }
2330     int pattern=int(input->readLong(2));
2331     if (pattern!=1) f << "pat=" << pattern << ",";
2332     float w[2]= {1,1};
2333     if (isWindows) {
2334       for (auto &we : w) we=float(input->readULong(4))/65536.f;
2335       f << "w=" << w[0] << "x" << w[1] << ",";
2336     }
2337     else {
2338       w[0]=float(input->readLong(2)); // horizontal width
2339       if (w[0]<1 || w[0]>1)
2340         f << "w[hori]=" << w[0] << ",";
2341     }
2342     float offset=float(input->readULong(4))/65536.f;
2343     if (offset>0 || offset<0) f << "offset=" << offset << ",";
2344     offsets.push_back(float(offsetSign)*offset);
2345     val=int(input->readULong(2));
2346     if (val!=0x8000)
2347       f << "fl=" << std::hex << val << std::dec << ",";
2348     MWAWColor colors[2]; // front back color
2349     for (int st=0; st<2; ++st) {
2350       unsigned char col[3];
2351       for (auto &c : col) c=(unsigned char)(input->readULong(2)>>8);
2352       MWAWColor color(col[0],col[1],col[2]);
2353       if (st==0)
2354         colors[0]=color;
2355       if (color!=(st==0 ? MWAWColor::black() : MWAWColor::white()))
2356         f << "col" << st << "=" << color << ",";
2357     }
2358     for (int j=0; j<3; ++j) {
2359       val=int(input->readULong(2));
2360       int const expected[]= {0, 0x7c, 0xa5};
2361       if (val!=expected[j]) f << "f" << j+2 << "=" << val << ",";
2362     }
2363     for (int j=0; j<9; ++j) {
2364       val=int(input->readULong(2));
2365       if (val) f << "g" << j << "=" << val << ",";
2366     }
2367     for (int j=0; j<3; ++j) {
2368       val=int(input->readULong(j==2 ? 2 : 4));
2369       int const expected[]= {0x184508, 0x1844f8, 0x8018};
2370       if (val!=expected[j]) f << "id" << j << "=" << std::hex << val << std::dec << ",";
2371     }
2372     for (int st=0; st<2; ++st) {
2373       unsigned char col[3];
2374       for (auto &c : col) c=(unsigned char)(input->readULong(2)>>8);
2375       MWAWColor color(col[0],col[1],col[2]) ;
2376       if (st==0) colors[1]=color;
2377       if (color!=(st==1 ? MWAWColor::black() : MWAWColor::white()))
2378         f << "col" << st+2 << "=" << color << ",";
2379     }
2380     for (int j=0; j<2; ++j) {
2381       val=int(input->readLong(2));
2382       if (val) f << "h" << j << "=" << val << ",";
2383     }
2384     ascFile.addPos(posi);
2385     ascFile.addNote(f.str().c_str());
2386 
2387     posi=input->tell();
2388     f.str("");
2389     f << "Shape-" << entry.id() << "[line" << i << "A," << shape.getTypeName() << "]:";
2390     val=int(input->readLong(2));
2391     if (val!=2) f << "f0=" << val << ",";
2392     for (int j=0; j<6; ++j) {
2393       val=int(input->readLong(2));
2394       if (val)
2395         f << "f" << j+1 << "=" << val << ",";
2396     }
2397     for (int j=0; j< 7; ++j) {
2398       val=int(input->readLong(2));
2399       int const expected[]= {1, 0, 0, 0, 1, 1, 0};
2400       if (val!=expected[j])
2401         f << "f" << j+7 << "=" << val << ",";
2402     }
2403     float iDim[4];
2404     for (auto &d : iDim) d=isWindows ? float(input->readLong(4))/65536.f : float(input->readLong(2));
2405     f << MWAWBox2f(MWAWVec2f(iDim[1],iDim[0]),MWAWVec2f(iDim[3],iDim[2])) << ",";
2406     int N=int(input->readULong(2));
2407     if (N>12) {
2408       MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find the number of dashes\n"));
2409       f << "###dash=" << N << ",";
2410     }
2411     else if (N) {
2412       f << "dash=[";
2413       for (int d=0; d<N; ++d) {
2414         style.m_lineDashWidth.push_back(float(input->readLong(4))/65536);
2415         f << style.m_lineDashWidth.back() << ",";
2416       }
2417       f << "],";
2418     }
2419     ascFile.addDelimiter(input->tell(),'|');
2420     if (!isWindows) {
2421       input->seek(posi+94, librevenge::RVNG_SEEK_SET);
2422       ascFile.addDelimiter(input->tell(),'|');
2423       w[1]=float(input->readLong(2));
2424       if (w[1]<1 || w[1]>1) f << "w[vert]=" << w[1] << ",";
2425       for (int j=0; j<2; ++j) {
2426         val=int(input->readLong(2));
2427         if (val)
2428           f << "g" << j << "=" << val << ",";
2429       }
2430     }
2431     input->seek(posi+100+(isWindows ? 2 : 0), librevenge::RVNG_SEEK_SET);
2432     ascFile.addPos(posi);
2433     ascFile.addNote(f.str().c_str());
2434     // time to update the style
2435     style.m_lineWidth=(w[0]+w[1])/2;
2436     if (pattern<155) {
2437       MWAWGraphicStyle::Pattern pat;
2438       if (!m_styleManager->get(pattern-1, pat)) {
2439         MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find patterns %d\n", pattern));
2440       }
2441       else {
2442         for (int j=0; j<2; ++j)
2443           pat.m_colors[1-j]=colors[j];
2444         pat.getAverageColor(style.m_lineColor);
2445       }
2446     }
2447     else {
2448       float percent=float(255-pattern)/100.f;
2449       MWAWColor finalColor=MWAWColor::barycenter(percent, colors[1], 1.f-percent, colors[0]);
2450       style.m_lineColor=finalColor;
2451     }
2452   }
2453 
2454   long posi=input->tell();
2455   std::vector<MWAWVec2f> points;
2456   if (posi!=entry.end()) {
2457     f.str("");
2458     f << "Shape-" << entry.id() << "[points," << shape.getTypeName() << "]:";
2459     if (posi+numPoints*8 <= entry.end()) {
2460       for (int j=0; j<numPoints; ++j) {
2461         float pt[2];
2462         for (auto &p : pt) p=float(input->readLong(4))/65536;
2463         points.push_back(MWAWVec2f(pt[1],pt[0]));
2464         f << points.back() << ",";
2465       }
2466     }
2467     else {
2468       MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find retrieve some points=%d\n", numPoints));
2469       f << "##N=" << numPoints << ",";
2470     }
2471     ascFile.addPos(posi);
2472     ascFile.addNote(f.str().c_str());
2473   }
2474   auto N=points.size();
2475   bool ok=type==17 ? N==0 : N>=2;
2476   if (!ok) {
2477     MWAW_DEBUG_MSG(("CanvasGraph::sendMultiLines: can not find points for type=%d\n", type));
2478     return false;
2479   }
2480   if (N==0) {
2481     points= {pts[0], pts[1]};
2482     N=2;
2483   }
2484 
2485   // times to draw the shapes
2486 
2487   MWAWPosition pos;
2488   pos.m_anchorTo = MWAWPosition::Page;
2489   listener->openGroup(local.m_position);
2490   std::vector<MWAWVec2f> originals;
2491   MWAWGraphicShape fShape;
2492   MWAWVec2f bDir=shape.m_box.size();
2493   for (auto const &pt : points)
2494     originals.push_back(shape.m_box[0]+MWAWVec2f(pt[0]*bDir[0], pt[1]*bDir[1]));
2495   bool hasSurface=local.m_style.hasSurface();
2496   if (hasSurface && numLines>=2 && type!=20) {
2497     // first draw the surface
2498     fShape=MWAWGraphicShape::polygon(shape.m_box);
2499     for (size_t p=0; p<N; ++p)
2500       fShape.m_vertices.push_back
2501       (CanvasGraphInternal::getOffsetPoint(originals, p, offsets[0]));
2502     for (size_t p=N; p>0; --p)
2503       fShape.m_vertices.push_back
2504       (CanvasGraphInternal::getOffsetPoint(originals, p-1, offsets[size_t(numLines-1)]));
2505     auto shapeBox=fShape.getBdBox();
2506     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2507     auto style=local.m_style;
2508     style.m_lineWidth=0;
2509     listener->insertShape(pos, fShape, style);
2510   }
2511   // now draw the line
2512   fShape=type==20 ? MWAWGraphicShape::path(shape.m_box) :
2513          MWAWGraphicShape::polyline(shape.m_box);
2514   for (int l=0; l<numLines; ++l) {
2515     fShape.m_vertices.clear();
2516     for (size_t p=0; p<N; ++p)
2517       fShape.m_vertices.push_back
2518       (CanvasGraphInternal::getOffsetPoint(originals, p, offsets[size_t(l)]));
2519     if (type==20) {
2520       // recreate the spline (fixme: do that correctly)
2521       fShape.m_path.clear();
2522       std::vector<MWAWGraphicShape::PathData> &path=fShape.m_path;
2523       std::vector<MWAWVec2f> newPoints=CanvasGraphInternal::smoothPoints(fShape.m_vertices);
2524       path.push_back(MWAWGraphicShape::PathData('M', newPoints[0]));
2525       for (size_t j=1; j<newPoints.size(); ++j) {
2526         MWAWVec2f dir=newPoints[j+1==newPoints.size() ? j : j+1]-newPoints[j-1];
2527         fShape.m_path.push_back(MWAWGraphicShape::PathData('S', newPoints[j], newPoints[j]-0.1f*dir));
2528       }
2529     }
2530     auto shapeBox=fShape.getBdBox();
2531     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2532     listener->insertShape(pos, fShape, styles[size_t(l)]);
2533   }
2534   // close the borders
2535   fShape=MWAWGraphicShape::polyline(shape.m_box);
2536   if (numLines>=2 && addEndCaps) {
2537     for (int bo=0; bo<2; ++bo) {
2538       fShape.m_vertices.clear();
2539       size_t wh=bo==0 ? 0 : N-1;
2540       for (int w=0; w<2; ++w)
2541         fShape.m_vertices.push_back
2542         (CanvasGraphInternal::getOffsetPoint(originals, wh, offsets[w==0 ? 0 : size_t(numLines-1)]));
2543       auto shapeBox=fShape.getBdBox();
2544       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2545       listener->insertShape(pos, fShape, styles[bo==1 ? 0 : size_t(numLines-1)]);
2546     }
2547   }
2548   listener->closeGroup();
2549   return true;
2550 }
2551 
sendSpecial(CanvasGraphInternal::Shape const & shape,CanvasGraphInternal::LocalTransform const & local)2552 bool CanvasGraph::sendSpecial(CanvasGraphInternal::Shape const &shape, CanvasGraphInternal::LocalTransform const &local)
2553 {
2554   MWAWGraphicListenerPtr listener=m_parserState->m_graphicListener;
2555   if (!listener) {
2556     MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: can not find the listener\n"));
2557     return false;
2558   }
2559   int id=shape.getSpecialId();
2560   auto const &box=shape.m_box;
2561   MWAWGraphicShape fShape;
2562   MWAWPosition pos;
2563   pos.m_anchorTo = MWAWPosition::Page;
2564   switch (id) {
2565   case 0: { // cube
2566     if (shape.m_points.size()!=8) {
2567       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: can not find the cube points\n"));
2568       return false;
2569     }
2570     int const faces[]= {
2571       0, 2, 6, 4, // X==0
2572       1, 3, 7, 5, // X==1
2573       0, 1, 5, 4, // Y==0
2574       2, 3, 7, 6, // Y==1
2575       0, 1, 3, 2, // Z==0
2576       4, 5, 7, 6, // Z==1
2577     };
2578     listener->openGroup(local.m_position);
2579     fShape.m_type=local.m_style.hasSurface() ? MWAWGraphicShape::Polygon : MWAWGraphicShape::Polyline;
2580     MWAWVec2f const dir=shape.m_box[1]-shape.m_box[0];
2581     MWAWVec2f const dirs[]= {shape.m_points[1]-shape.m_points[0],
2582                              shape.m_points[2]-shape.m_points[0],
2583                              shape.m_points[4]-shape.m_points[0]
2584                             };
2585     int wh=(dirs[0][0]*dirs[2][1]-dirs[0][1]*dirs[2][0]>0) ? 0 : 1;
2586     wh+=(dirs[1][0]*dirs[2][1]-dirs[1][1]*dirs[2][0]>0) ? 0 : 2;
2587     if (dirs[0][0]*dirs[1][1]-dirs[0][1]*dirs[1][0]>0 && (wh==0 || wh==3)) wh=3-wh;
2588 
2589     for (int f=0; f<3; ++f) {
2590       size_t face;
2591       switch (f) {
2592       case 0:
2593         face=4;
2594         break;
2595       case 1:
2596         face = (wh==0 || wh==1) ? 2 : 3;
2597         break;
2598       default:
2599         face = (wh==0 || wh==2) ? 1 : 0;
2600         break;
2601       }
2602 
2603       MWAWBox2f shapeBox;
2604       fShape.m_vertices.resize(4);
2605       for (size_t p=0; p<4; ++p) {
2606         MWAWVec2f const &pt=shape.m_points[size_t(faces[4*face+p])];
2607         fShape.m_vertices[p]=shape.m_box[0]+MWAWVec2f(pt[0]*dir[0], pt[1]*dir[1]);
2608       }
2609       fShape.m_bdBox=shapeBox;
2610 
2611       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2612       listener->insertShape(pos, fShape, local.m_style);
2613     }
2614     listener->closeGroup();
2615     break;
2616   }
2617   case 1:
2618     return sendDimension(shape, local);
2619   case 2: { // grid
2620     listener->openGroup(local.m_position);
2621     if (shape.m_values[0]<=0 || shape.m_values[1]<=0 ||
2622         shape.m_values[0]>100 || shape.m_values[1]>100) {
2623       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial[grid]: can not find the number of rows/columns\n"));
2624       return false;
2625     }
2626     MWAWVec2f dim((box[1][0]-box[0][0])/float(shape.m_values[0]),
2627                   (box[1][1]-box[0][1])/float(shape.m_values[1]));
2628     for (int i=0; i<=shape.m_values[0]; ++i) {
2629       float X=box[0][0]+float(i)*dim[0];
2630       fShape=MWAWGraphicShape::line(MWAWVec2f(X,box[0][1]), MWAWVec2f(X,box[1][1]));
2631       MWAWBox2f shapeBox=fShape.getBdBox();
2632       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2633       listener->insertShape(pos, fShape, local.m_style);
2634     }
2635     for (int j=0; j<=shape.m_values[1]; ++j) {
2636       float Y=box[0][1]+float(j)*dim[1];
2637       fShape=MWAWGraphicShape::line(MWAWVec2f(box[0][0],Y), MWAWVec2f(box[1][0],Y));
2638       MWAWBox2f shapeBox=fShape.getBdBox();
2639       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2640       listener->insertShape(pos, fShape, local.m_style);
2641     }
2642     listener->closeGroup();
2643     break;
2644   }
2645   case 3: // ObFl: done
2646     break;
2647   case 4: // Paln
2648     return sendMultiLines(shape, local);
2649   case 5: { // QkTm
2650     if (shape.m_entry.valid()) {
2651       // TODO replace this code when we find how to read data
2652       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial[QkTm]: sorry, reading QkTm data is not implemented\n"));
2653     }
2654     listener->openGroup(local.m_position);
2655     // box
2656     fShape=MWAWGraphicShape::rectangle(box);
2657     MWAWBox2f shapeBox=fShape.getBdBox();
2658     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2659     listener->insertShape(pos, fShape, local.m_style);
2660     // diag1 line
2661     fShape=MWAWGraphicShape::line(box[0], box[1]);
2662     shapeBox=fShape.getBdBox();
2663     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2664     listener->insertShape(pos, fShape, local.m_style);
2665     // diag2 line
2666     fShape=MWAWGraphicShape::line(MWAWVec2f(box[0][0], box[1][1]), MWAWVec2f(box[1][0], box[0][1]));
2667     shapeBox=fShape.getBdBox();
2668     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2669     listener->insertShape(pos, fShape, local.m_style);
2670     listener->closeGroup();
2671     break;
2672   }
2673   case 6: { // regP
2674     MWAWGraphicStyle style;
2675     listener->openGroup(local.m_position);
2676     MWAWVec2f center=0.5f*(box[0]+box[1]);
2677     // H line
2678     fShape=MWAWGraphicShape::line(MWAWVec2f(box[0][0], center[1]), MWAWVec2f(box[1][0], center[1]));
2679     MWAWBox2f shapeBox=fShape.getBdBox();
2680     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2681     listener->insertShape(pos, fShape, style);
2682     // V line
2683     fShape=MWAWGraphicShape::line(MWAWVec2f(center[0], box[0][1]), MWAWVec2f(center[0], box[1][1]));
2684     shapeBox=fShape.getBdBox();
2685     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2686     listener->insertShape(pos, fShape, style);
2687     // circle
2688     style.m_lineWidth=2;
2689     MWAWVec2f delta=0.2*(box[1]-box[0]);
2690     fShape=MWAWGraphicShape::circle(MWAWBox2f(box[0]+delta,box[1]-delta));
2691     shapeBox=fShape.getBdBox();
2692     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2693     listener->insertShape(pos, fShape, style);
2694 
2695     listener->closeGroup();
2696     break;
2697   }
2698   case 7: { // hatch
2699     if (shape.m_points.empty() || (shape.m_points.size()%2)) {
2700       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: sorry, can not find the hatch line\n"));
2701       break;
2702     }
2703     MWAWGraphicStyle style;
2704     listener->openGroup(local.m_position);
2705     for (size_t p=0; p+1<shape.m_points.size(); p+=2) {
2706       fShape=MWAWGraphicShape::line(shape.m_points[p], shape.m_points[p+1]);
2707       MWAWBox2f shapeBox=fShape.getBdBox();
2708       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2709       listener->insertShape(pos, fShape, local.m_style);
2710     }
2711     listener->closeGroup();
2712     break;
2713   }
2714   case 8: // Enve, looks like a secondary representation, ...
2715     break;
2716   case 9: { // CCir
2717     if (shape.m_values[0]<=0 || shape.m_values[0]>20) {
2718       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: sorry, the number of circles seems bad\n"));
2719       break;
2720     }
2721     listener->openGroup(local.m_position);
2722     MWAWVec2f center=0.5f*(box[0]+box[1]);
2723     MWAWVec2f diag=0.5f*box.size();
2724     for (int i=0; i<shape.m_values[0]; ++i) {
2725       MWAWVec2f newDiag;
2726       if (shape.m_values[1]<=0)
2727         newDiag=float(shape.m_values[0]-i)/float(shape.m_values[0])*diag;
2728       else {
2729         newDiag=diag-float(shape.m_values[1]*i)*MWAWVec2f(1,1);
2730         for (int c=0; c<2; ++c) {
2731           if (newDiag[c]<0)
2732             newDiag[c]=0;
2733         }
2734       }
2735       fShape=MWAWGraphicShape::circle(MWAWBox2f(center-newDiag, center+newDiag));
2736       MWAWBox2f shapeBox=fShape.getBdBox();
2737       pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2738       listener->insertShape(pos, fShape, local.m_style);
2739     }
2740     listener->closeGroup();
2741     break;
2742   }
2743   case 10: { // OLnk
2744     if (!shape.m_childs.empty()) // child of a DIMN node, safe to ignore...
2745       break;
2746     if (shape.m_points.size()<2) {
2747       MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: sorry, can not find the connector points\n"));
2748       break;
2749     }
2750     fShape=MWAWGraphicShape::polyline(box);
2751     fShape.m_vertices=shape.m_points;
2752     MWAWBox2f shapeBox=fShape.getBdBox();
2753     pos=MWAWPosition(shapeBox[0], shapeBox.size(), librevenge::RVNG_POINT);
2754     listener->insertShape(pos, fShape, local.m_style);
2755     break;
2756   }
2757   default:
2758     MWAW_DEBUG_MSG(("CanvasGraph::sendSpecial: sorry, sending type=%d is not implemented\n", id));
2759     break;
2760   }
2761   return true;
2762 }
2763 
sendText(int zId)2764 bool CanvasGraph::sendText(int zId)
2765 {
2766   auto const it=m_state->m_idToShapeMap.find(zId);
2767   if (it==m_state->m_idToShapeMap.end()) {
2768     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not find shape %d\n", zId));
2769     return false;
2770   }
2771   return sendText(it->second);
2772 }
2773 
sendText(CanvasGraphInternal::Shape const & shape)2774 bool CanvasGraph::sendText(CanvasGraphInternal::Shape const &shape)
2775 {
2776   auto input=getInput();
2777   MWAWGraphicListenerPtr listener=m_parserState->m_graphicListener;
2778   if (!input || !listener) {
2779     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not find the listener\n"));
2780     return false;
2781   }
2782   auto const &entry=shape.m_entry;
2783   int const vers=version();
2784   if (shape.m_type!=2 || !entry.valid() || !input->checkPosition(entry.end())) {
2785     MWAW_DEBUG_MSG(("CanvasGraph::sendText: unexpected type for a text shape=%d\n", shape.m_type));
2786     return false;
2787   }
2788   MWAWParagraph para;
2789   if (vers==2) {
2790     switch (shape.m_align) {
2791     case 0: // left
2792       break;
2793     case 1:
2794       para.m_justify=MWAWParagraph::JustificationCenter;
2795       break;
2796     case 2:
2797       para.m_justify=MWAWParagraph::JustificationRight;
2798       break;
2799     default:
2800       MWAW_DEBUG_MSG(("CanvasGraph::sendText: find align=%d\n", shape.m_align));
2801       break;
2802     }
2803   }
2804   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
2805   libmwaw::DebugStream f;
2806   f << "Entries(Text)[S" << entry.id() << "]:";
2807   input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
2808   long endPos=entry.end();
2809   for (int i=0; i<9; ++i) {
2810     int val=int(input->readLong(2));
2811     int const expected[]= {3,0,12,0,0,0,-2,0,0};
2812     if (val==expected[i]) continue;
2813     if (i==8 && vers>=3) {
2814       switch (val) {
2815       case 0: // left
2816         break;
2817       case 1:
2818         f << "center,";
2819         para.m_justify=MWAWParagraph::JustificationCenter;
2820         break;
2821       case -1:
2822         f << "right,";
2823         para.m_justify=MWAWParagraph::JustificationRight;
2824         break;
2825       case 4:
2826         f << "full,";
2827         para.m_justify=MWAWParagraph::JustificationFull;
2828         break;
2829       default:
2830         MWAW_DEBUG_MSG(("CanvasGraph::sendText: find align=%d\n", val));
2831         f << "##align=" << val << ",";
2832         break;
2833       }
2834     }
2835     f << "f" << i << "=" << val << ",";
2836   }
2837   long dims[4];
2838   dims[0]=int(input->readULong(4));
2839   f << "N[char]=" << dims[0] << ",";
2840   int val=int(input->readULong(2));
2841   if (val&1)
2842     f << "sym[hor],";
2843   if (val&2)
2844     f << "sym[ver],";
2845   if (val&0xfffc)
2846     f << "sym?=" << std::hex << (val&0xfffc) << std::dec << ",";
2847   val=int(input->readLong(2)); // 0
2848   if (val) f << "rot=" << val << ",";
2849   f << "dim=[";
2850   for (int i=1; i<4; ++i) {
2851     dims[i]=int(input->readULong(4));
2852     if (dims[i])
2853       f << dims[i] << ",";
2854     else
2855       f << "_,";
2856   }
2857   f << "],";
2858   ascFile.addPos(entry.begin());
2859   ascFile.addNote(f.str().c_str());
2860 
2861   long pos=input->tell();
2862   f.str("");
2863   f << "TextA:";
2864   if (47+dims[0]>entry.length()) {
2865     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not find the text\n"));
2866     f << "###";
2867     ascFile.addPos(pos);
2868     ascFile.addNote(f.str().c_str());
2869     return false;
2870   }
2871   std::string text;
2872   long textPos=input->tell();
2873   for (int i=0; i<dims[0]; ++i) text+=char(input->readULong(1));
2874   f << text;
2875   if (vers>=3 && (dims[0]&1)) input->seek(1, librevenge::RVNG_SEEK_CUR);
2876   ascFile.addPos(pos);
2877   ascFile.addNote(f.str().c_str());
2878 
2879   pos=input->tell();
2880   f.str("");
2881   f << "Text[plc-header]:";
2882   bool ok=true;
2883   if (dims[1]<16 || pos+dims[1]>endPos) {
2884     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not read the plc zone\n"));
2885     ok=false;
2886   }
2887   int N[2]= {0,0};
2888   if (ok) {
2889     for (int i=0; i<2; ++i) {
2890       N[i]=int(input->readULong(2));
2891       f << "N" << i << "=" << N[i] << ",";
2892     }
2893     f << "ids=[";
2894     for (int i=0; i<4; ++i)
2895       f << std::hex << input->readULong(4) << std::dec << ",";
2896     f << "],";
2897   }
2898   int const fontSz=vers==2 ? 18 : 50;
2899   if (ok && (20+(N[0]+1)*(vers==2 ? 4 : 6)>dims[1] || input->tell()+(N[0]+1)*4+N[1]*fontSz>entry.end())) {
2900     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not find the format size\n"));
2901     f << "###";
2902     ok=false;
2903   }
2904   ascFile.addPos(pos);
2905   ascFile.addNote(f.str().c_str());
2906 
2907   std::map<int,int> posToFontIdMap;
2908   std::vector<MWAWFont> fonts;
2909   if (ok) {
2910     pos=input->tell();
2911     f.str("");
2912     f << "Text-plc:";
2913     for (int i=0; i<N[0]+1; ++i) {
2914       int cPos=int(input->readULong(vers==2 ? 2 : 4));
2915       int fId=int(input->readULong(2));
2916       f << cPos << ":F" << fId << ",";
2917       posToFontIdMap[cPos]=fId;
2918     }
2919     if (dims[1]!=20+(N[0]+1)*4) {
2920       ascFile.addDelimiter(input->tell(),'|');
2921       input->seek(pos+dims[1]-20, librevenge::RVNG_SEEK_SET);
2922     }
2923     ascFile.addPos(pos);
2924     ascFile.addNote(f.str().c_str());
2925   }
2926   if (ok && fontSz*N[1]>dims[3]) {
2927     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not find the font\n"));
2928     f << "###";
2929     ok=false;
2930   }
2931   if (ok) {
2932     long endFontPos=input->tell()+dims[3];
2933     auto fontConverter=m_parserState->m_fontConverter;
2934     fonts.resize(size_t(N[1]));
2935     for (size_t i=0; i<size_t(N[1]); ++i) {
2936       pos=input->tell();
2937       MWAWFont &font=fonts[i];
2938       f.str("");
2939       val=int(input->readLong(2));
2940       if (val!=1)
2941         f << "used=" << val << ",";
2942       f << "dims?=["; // related to h?
2943       for (int j=0; j<2; ++j) f << std::hex << input->readULong(2) << std::dec << ",";
2944       f << "],";
2945       font.setId(int(input->readULong(2)));
2946       int fl=int(input->readULong(1));
2947       uint32_t flags = 0;
2948       if (fl&0x1) flags |= MWAWFont::boldBit;
2949       if (fl&0x2) flags |= MWAWFont::italicBit;
2950       if (fl&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple);
2951       if (fl&0x8) flags |= MWAWFont::embossBit;
2952       if (fl&0x10) flags |= MWAWFont::shadowBit;
2953       if (fl&0xe0) f << "fl=" << std::hex << (fl&0xe0) << std::dec << ",";
2954       val=int(input->readULong(1));
2955       if (val) f << "fl1=" << std::hex << val << std::dec;
2956       font.setSize(float(input->readULong(2)));
2957       unsigned char col[3];
2958       for (auto &c : col) c=(unsigned char)(input->readULong(2)>>8);
2959       font.setColor(MWAWColor(col[0],col[1], col[2]));
2960       if (fontSz>=50) {
2961         for (int j=0; j<10; ++j) {
2962           val=int(input->readLong(2));
2963           int const expected=(j>=2 && j<=5) ? 1 : 0;
2964           if (val==expected) continue;
2965           if (j==0) { // normally between -2 and 2
2966             if (val>0 && val<6)
2967               font.setDeltaLetterSpacing(1+float(val)*0.3f, librevenge::RVNG_PERCENT);
2968             else if (val>=-6 && val<0)
2969               font.setDeltaLetterSpacing(float(val)/2, librevenge::RVNG_POINT);
2970             else {
2971               MWAW_DEBUG_MSG(("CanvasGraph::sendText: unknown delta spacing\n"));
2972               f << "##delta[spacing]=" << val << ",";
2973             }
2974           }
2975           else if (j==6)
2976             font.set(MWAWFont::Script(float(val),librevenge::RVNG_POINT));
2977           else if (j==9) {
2978             if (val&1)
2979               flags |= MWAWFont::smallCapsBit;
2980             if (val&2)
2981               flags |= MWAWFont::uppercaseBit;
2982             if (val&4)
2983               flags |= MWAWFont::lowercaseBit;
2984             if (val&8)
2985               flags |= MWAWFont::initialcaseBit;
2986             val &= 0xFFF0;
2987             if (val) {
2988               MWAW_DEBUG_MSG(("CanvasGraph::sendText: unknown small caps bits\n"));
2989               f << "##smallCaps=" << val << ",";
2990             }
2991           }
2992           else
2993             f << "f" << j << "=" << val << ",";
2994         }
2995       }
2996       font.setFlags(flags);
2997       std::string const extra=f.str();
2998       f.str("");
2999       f << "Text-F" << i << ":" << font.getDebugString(fontConverter) << extra;
3000       if (input->tell()!=pos+fontSz)
3001         ascFile.addDelimiter(input->tell(),'|');
3002       input->seek(pos+fontSz, librevenge::RVNG_SEEK_SET);
3003       ascFile.addPos(pos);
3004       ascFile.addNote(f.str().c_str());
3005     }
3006     input->seek(endFontPos, librevenge::RVNG_SEEK_SET);
3007   }
3008 
3009   pos=input->tell();
3010   std::vector<float> lineHeights;
3011   if (dims[2]<4 || pos+dims[2]>endPos) {
3012     MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not read the line zone\n"));
3013     ascFile.addPos(pos);
3014     ascFile.addNote("Shape-data,text###");
3015     ok=false;
3016   }
3017   else if (ok) {
3018     f.str("");
3019     f << "Text-line:";
3020     bool useDouble=vers>2;
3021     if (vers==2) { // v2.0 use float, v2.1 double?
3022       input->seek(2, librevenge::RVNG_SEEK_CUR);
3023       useDouble=input->readULong(2)==0;
3024       input->seek(-4, librevenge::RVNG_SEEK_CUR);
3025     }
3026     if (!useDouble) {
3027       for (int i=0; i<int(dims[2]/4); ++i) {
3028         lineHeights.push_back(float(input->readULong(2)));
3029         f << lineHeights.back() << "<->" << input->readULong(2) << ","; // max height, min height?
3030       }
3031     }
3032     else {
3033       int num=int(dims[2]/8);
3034       for (int i=0; i<num-1; ++i) {
3035         lineHeights.push_back(float(input->readULong(4))/65536);
3036         f << lineHeights.back() << "<->" << float(input->readULong(4))/65536 << ","; // max height, min height?
3037       }
3038     }
3039     if (input->tell() != pos+dims[2])
3040       ascFile.addDelimiter(input->tell(),'|');
3041     input->seek(pos+dims[2], librevenge::RVNG_SEEK_SET);
3042     ascFile.addPos(pos);
3043     ascFile.addNote(f.str().c_str());
3044   }
3045   size_t numLines=0;
3046   bool newLine=true;
3047   // time to send the text
3048   long actPos=input->tell();
3049   input->seek(textPos, librevenge::RVNG_SEEK_SET);
3050   listener->setParagraph(para);
3051   for (int i=0; i<dims[0]; ++i) {
3052     if (newLine) {
3053       if (numLines<lineHeights.size() && lineHeights[numLines]>0)
3054         para.setInterline(lineHeights[numLines]>100 ? 100 : lineHeights[numLines], librevenge::RVNG_POINT);
3055       else
3056         para.setInterline(1, librevenge::RVNG_PERCENT);
3057       listener->setParagraph(para);
3058       newLine=false;
3059     }
3060     auto it=posToFontIdMap.find(i);
3061     if (it!=posToFontIdMap.end()) {
3062       if (it->second<0 || it->second>=int(fonts.size())) {
3063         MWAW_DEBUG_MSG(("CanvasGraph::sendText: can not read find the font=%d\n", it->second));
3064       }
3065       else
3066         listener->setFont(fonts[size_t(it->second)]);
3067     }
3068     unsigned char c=(unsigned char)(input->readULong(1));
3069     switch (c) {
3070     case 0x9:
3071       listener->insertTab();
3072       break;
3073     case 0xd:
3074       if (i+1==dims[0])
3075         break;
3076       listener->insertEOL();
3077       newLine=true;
3078       break;
3079     default:
3080       if (c<=0x1f) {
3081         MWAW_DEBUG_MSG(("CanvasGraph::sendText: find unexpected char=%x\n", (unsigned int)(c)));
3082       }
3083       else
3084         listener->insertCharacter(c);
3085     }
3086   }
3087   if (!ok)
3088     return false;
3089   input->seek(actPos, librevenge::RVNG_SEEK_SET);
3090 
3091   pos=input->tell();
3092   if (pos!=endPos) { // v2 empty (or 1 char), v3 a DeR2 zone, v2.1 ?
3093     f.str("");
3094     f << "Text-end:";
3095     ascFile.addPos(pos);
3096     ascFile.addNote(f.str().c_str());
3097   }
3098   return true;
3099 }
3100 
3101 
3102 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
3103