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