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