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 <cstring>
35 #include <iomanip>
36 #include <iostream>
37 #include <limits>
38 #include <map>
39 #include <sstream>
40 
41 #include <librevenge/librevenge.h>
42 
43 #include "MWAWTextListener.hxx"
44 #include "MWAWDebug.hxx"
45 #include "MWAWFont.hxx"
46 #include "MWAWFontConverter.hxx"
47 #include "MWAWList.hxx"
48 #include "MWAWPageSpan.hxx"
49 #include "MWAWParagraph.hxx"
50 #include "MWAWPictData.hxx"
51 #include "MWAWPosition.hxx"
52 #include "MWAWRSRCParser.hxx"
53 #include "MWAWSubDocument.hxx"
54 
55 #include "MoreParser.hxx"
56 
57 #include "MoreText.hxx"
58 
59 /** Internal: the structures of a MoreText */
60 namespace MoreTextInternal
61 {
62 ////////////////////////////////////////
63 //! Internal: the paragraph of a MoreText
64 struct Paragraph final : public MWAWParagraph {
65   //! constructor
ParagraphMoreTextInternal::Paragraph66   Paragraph()
67     : MWAWParagraph()
68     , m_listType(0)
69     , m_customListLevel()
70     , m_pageBreak(false)
71     , m_keepOutlineTogether(false)
72   {
73     m_marginsFromParent[0]=0.3;
74     m_marginsFromParent[1]=0;
75   }
76   Paragraph(Paragraph const &)=default;
77   Paragraph &operator=(Paragraph const &)=default;
78   //! destructor
79   ~Paragraph() final;
80   //! set the left margin in inch
setLeftMarginMoreTextInternal::Paragraph81   void setLeftMargin(double margin, bool fromParent)
82   {
83     if (fromParent) {
84       m_marginsFromParent[0]=margin;
85       m_margins[1]=0;
86     }
87     else {
88       m_marginsFromParent[0]=-100;
89       m_margins[1]=margin;
90     }
91   }
92   //! set the right margin in inch
setRightMarginMoreTextInternal::Paragraph93   void setRightMargin(double margin, bool fromParent)
94   {
95     if (fromParent) {
96       m_marginsFromParent[1]=margin;
97       m_margins[2]=0;
98     }
99     else {
100       m_marginsFromParent[1]=-100;
101       m_margins[2]=margin;
102     }
103   }
104   //! update the paragraph to obtain the final paragraph
updateToFinalStateMoreTextInternal::Paragraph105   void updateToFinalState(MWAWParagraph const &parent, int level, MWAWListManager &listManager)
106   {
107     bool leftUseParent=m_marginsFromParent[0]>-10;
108     if (leftUseParent)
109       m_margins[1]=*parent.m_margins[1]+m_marginsFromParent[0];
110     if (m_marginsFromParent[1]>-10)
111       m_margins[2]=*parent.m_margins[2]+m_marginsFromParent[1];
112     if (level<0)
113       return;
114 
115     MWAWListLevel listLevel;
116     switch (m_listType) {
117     case 1: // leader
118       listLevel.m_type = MWAWListLevel::BULLET;
119       listLevel.m_bullet = "+"; // in fact + + and -
120       break;
121     case 2: // hardvard
122       listLevel.m_suffix = (level <= 3) ? "." : ")";
123       if (level == 1) listLevel.m_type = MWAWListLevel::UPPER_ROMAN;
124       else if (level == 2) listLevel.m_type = MWAWListLevel::UPPER_ALPHA;
125       else if (level == 3) listLevel.m_type = MWAWListLevel::DECIMAL;
126       else if (level == 4) listLevel.m_type =  MWAWListLevel::LOWER_ALPHA;
127       else if ((level%3)==2) {
128         listLevel.m_prefix = "(";
129         listLevel.m_type = MWAWListLevel::DECIMAL;
130       }
131       else if ((level%3)==0) {
132         listLevel.m_prefix = "(";
133         listLevel.m_type = MWAWListLevel::LOWER_ALPHA;
134       }
135       else
136         listLevel.m_type = MWAWListLevel::LOWER_ROMAN;
137       break;
138     case 3: // numeric
139       listLevel.m_type = MWAWListLevel::DECIMAL;
140       listLevel.m_suffix = ".";
141       break;
142     case 4: // legal
143       listLevel.m_type = MWAWListLevel::DECIMAL;
144       listLevel.m_numBeforeLabels = level-1;
145       listLevel.m_suffix = ".";
146       listLevel.m_labelWidth = 0.2*level;
147       break;
148     case 5: // bullets
149       listLevel.m_type = MWAWListLevel::BULLET;
150       libmwaw::appendUnicode(0x2022, listLevel.m_bullet);
151       break;
152     case 0: // none
153       return;
154     default:
155       if (m_listType<11)
156         return;
157 
158       listLevel=m_customListLevel;
159       break;
160     }
161 
162     m_listLevelIndex = level+1;
163     std::shared_ptr<MWAWList> parentList;
164     if (*parent.m_listId>=0)
165       parentList=listManager.getList(*parent.m_listId);
166     auto list=listManager.getNewList(parentList, level+1, listLevel);
167     if (list)
168       m_listId=list->getId();
169     else
170       m_listLevel=listLevel;
171 
172     m_margins[1]=m_margins[1].get()-listLevel.m_labelWidth;
173   }
174   //! the left and right margins from parent in inches
175   double m_marginsFromParent[2];
176   //! the list type (0: none, 1: leader, ...)
177   int m_listType;
178   //! a custom list level ( only defined if m_listType>=0xb)
179   MWAWListLevel m_customListLevel;
180   //! true if we need to add a page break before
181   bool m_pageBreak;
182   //! true if we need to keep outline together
183   bool m_keepOutlineTogether;
184 };
185 
~Paragraph()186 Paragraph::~Paragraph()
187 {
188 }
189 
190 ////////////////////////////////////////
191 //! Internal: the outline data of a MoreText
192 struct Outline {
193   //! constructor
OutlineMoreTextInternal::Outline194   Outline()
195   {
196     // set to default value
197     for (auto &font : m_fonts) font=MWAWFont(3,12);
198     m_paragraphs[0].m_listType=1;
199   }
200   //! the paragraphs : organizer, slide, tree, unknowns
201   Paragraph m_paragraphs[4];
202   //! the fonts : organizer, slide, tree unknowns
203   MWAWFont m_fonts[4];
204 };
205 
206 ////////////////////////////////////////
207 //! Internal and low level: the outline modifier header of a MoreText
208 struct OutlineMod {
209   //! constructor
OutlineModMoreTextInternal::OutlineMod210   OutlineMod()
211     : m_type(-1)
212     , m_flags(0)
213     , m_entry()
214     , m_extra("")
215   {
216     for (auto &unkn : m_unknowns) unkn=0;
217   }
218   //! returns the data id to change in Outline
getModIdMoreTextInternal::OutlineMod219   int getModId() const
220   {
221     if (m_unknowns[0] || (m_flags&0xF)!= 1)
222       return 3;
223     switch (m_flags>>4) {
224     case 1:
225       return 0;
226     case 2:
227       return 1;
228     case 4:
229       return 2;
230     default:
231       break;
232     }
233     return 3;
234   }
235   //! operator<<
operator <<(std::ostream & o,OutlineMod const & head)236   friend std::ostream &operator<<(std::ostream &o, OutlineMod const &head)
237   {
238     switch (head.m_flags>>4) {
239     case 1: // organizer
240       break;
241     case 2:
242       o << "slide,";
243       break;
244     case 4:
245       o << "tree,";
246       break;
247     default:
248       o << "##wh=" << std::hex << (head.m_flags>>4) << std::dec << ",";
249       break;
250     }
251     switch (head.m_type) {
252     case 0x301:
253       o << "font,";
254       break;
255     case 0x402:
256       o << "fSize,";
257       break;
258     case 0x603:
259       o << "fFlags,";
260       break;
261     case 0x804:
262       o << "fColor,";
263       break;
264     case 0xa05:
265       o << "interline,";
266       break;
267     case 0xc0f:
268       o << "firstIndent,";
269       break;
270     case 0xf07:
271       o << "tabs,";
272       break;
273     case 0x1006:
274       o << "justify,";
275       break;
276     case 0x1208:
277       o << "bef/afterspace,";
278       break;
279     case 0x1409:
280       o << "lMargin,";
281       break;
282     case 0x160a:
283       o << "rMargin,";
284       break;
285     case 0x190b:
286       o << "list[type],";
287       break;
288     case 0x1a0c:
289       o << "break,";
290       break;
291     case 0x1c0d:
292       o << "keepline,";
293       break;
294     case 0x1e0e:
295       o << "keep[outline],";
296       break;
297     case -1:
298       break;
299     default:
300       // 2914|2b15|3319|3b1c|6532|6934|7137|773a|7b3c -> backside|backpattern
301       o << "type=" << std::hex << head.m_type << std::dec << ",";
302       break;
303     }
304     for (int i=0; i<2; i++) { // unkn0=0|1|2(related to clone?), unkn1=0|1|999
305       if (head.m_unknowns[i])
306         o << "unkn" << i << "=" << head.m_unknowns[i] << ",";
307     }
308     if (head.m_flags&0xF)
309       o << "fl=" << std::hex << (head.m_flags&0xF) << std::dec << ",";
310     if (head.m_entry.valid())
311       o << std::hex << head.m_entry.begin() << "<->" << head.m_entry.end() << std::dec << ",";
312     o << head.m_extra;
313     return o;
314   }
315   //! the type
316   int m_type;
317   //! the flag
318   int m_flags;
319   //! the data entry
320   MWAWEntry m_entry;
321   //! some unknown flags
322   int m_unknowns[2];
323   //! extra data
324   std::string m_extra;
325 };
326 
327 ////////////////////////////////////////
328 //! Internal: the comment data of a MoreText
329 struct Comment {
330   //! constructor
CommentMoreTextInternal::Comment331   Comment()
332     : m_entry()
333     , m_extra("")
334   {
335   }
336   //! operator<<
operator <<(std::ostream & o,Comment const & comment)337   friend std::ostream &operator<<(std::ostream &o, Comment const &comment)
338   {
339     o << comment.m_extra;
340     return o;
341   }
342   //! the text entry
343   MWAWEntry m_entry;
344   //! extra data
345   std::string m_extra;
346 };
347 
348 ////////////////////////////////////////
349 //! Internal: the topic data of a MoreText
350 struct Topic {
351   //! an enum used to define the different type of data attached to a topic
352   enum AttachementType { AOutline=0, AComment, ASpeakerNote };
353   //! constructor
TopicMoreTextInternal::Topic354   Topic()
355     : m_entry()
356     , m_level(0)
357     , m_isCloned(false)
358     , m_cloneId(-1)
359     , m_numPageBreak(-1)
360     , m_isStartSlide(false)
361     , m_extra("")
362   {
363     for (auto &hasData : m_hasList) hasData=false;
364     for (auto &attachData : m_attachList) attachData=-1;
365   }
366   //! operator<<
operator <<(std::ostream & o,Topic const & topic)367   friend std::ostream &operator<<(std::ostream &o, Topic const &topic)
368   {
369     if (topic.m_level>0)
370       o << "level=" << topic.m_level << ",";
371     if (topic.m_hasList[AOutline])
372       o << "outline,";
373     if (topic.m_hasList[AComment])
374       o << "comment,";
375     if (topic.m_hasList[ASpeakerNote])
376       o << "speakerNote,";
377     if (topic.m_isCloned)
378       o << "cloned,";
379     if (topic.m_cloneId >= 0)
380       o << "cloneId=" << topic.m_cloneId << ",";
381     if (topic.m_isStartSlide)
382       o << "newSlide,";
383     o << topic.m_extra;
384     return o;
385   }
386   //! the text entry
387   MWAWEntry m_entry;
388   //! the topic level
389   int m_level;
390   //! true if the entry is cloned
391   bool m_isCloned;
392   //! if not 0, indicate that we must cloned the cloneId^th clone
393   int m_cloneId;
394   //! a list of boolean use to note if a topic is associated with a Outline, ...
395   bool m_hasList[3];
396   //! a list of id to retrieve the attachment
397   int m_attachList[3];
398   //! the number of pages in the sub list
399   int m_numPageBreak;
400   //! true if we start a new slide
401   bool m_isStartSlide;
402   //! extra data
403   std::string m_extra;
404 };
405 
406 ////////////////////////////////////////
407 //! Internal: the state of a MoreText
408 struct State {
409   //! constructor
StateMoreTextInternal::State410   State()
411     : m_version(-1)
412     , m_topicList()
413     , m_commentList()
414     , m_speakerList()
415     , m_outlineList()
416     , m_actualComment(0)
417     , m_actualSpeaker(0)
418     , m_actualOutline(0)
419     , m_numPages(-1)
420     , m_actualPage(1)
421   {
422   }
423   //! the file version
424   mutable int m_version;
425   //! the topic list
426   std::vector<Topic> m_topicList;
427   //! the header/footer/comment list
428   std::vector<Comment> m_commentList;
429   //! the speaker note list
430   std::vector<MWAWEntry> m_speakerList;
431   //! the outline list
432   std::vector<Outline> m_outlineList;
433   //! the actual comment
434   int m_actualComment;
435   //! the actual speaker note
436   int m_actualSpeaker;
437   //! the actual outline
438   int m_actualOutline;
439   int m_numPages /* the number of pages */, m_actualPage /* the actual page */;
440 };
441 
442 ////////////////////////////////////////
443 //! Internal: the subdocument of a MoreText
444 class SubDocument final : public MWAWSubDocument
445 {
446 public:
SubDocument(MoreText & pars,MWAWInputStreamPtr const & input,int zId,int what)447   SubDocument(MoreText &pars, MWAWInputStreamPtr const &input, int zId, int what)
448     : MWAWSubDocument(pars.m_mainParser, input, MWAWEntry())
449     , m_textParser(&pars)
450     , m_id(zId)
451     , m_what(what)
452   {
453   }
454 
455   //! destructor
~SubDocument()456   ~SubDocument() final {}
457 
458   //! operator!=
459   bool operator!=(MWAWSubDocument const &doc) const final;
460 
461   //! the parser function
462   void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
463 
464 protected:
465   /** the text parser */
466   MoreText *m_textParser;
467   //! the subdocument id
468   int m_id;
469   //! a int to know what to send 0: header/footer, 1: comment, 2:note
470   int m_what;
471 private:
472   SubDocument(SubDocument const &orig) = delete;
473   SubDocument &operator=(SubDocument const &orig) = delete;
474 };
475 
parse(MWAWListenerPtr & listener,libmwaw::SubDocumentType)476 void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType /*type*/)
477 {
478   if (!listener.get()) {
479     MWAW_DEBUG_MSG(("MoreTextInternal::SubDocument::parse: no listener\n"));
480     return;
481   }
482   if (!m_textParser) {
483     MWAW_DEBUG_MSG(("MoreTextInternal::SubDocument::parse: no parser\n"));
484     return;
485   }
486 
487   long pos = m_input->tell();
488   switch (m_what) {
489   case 0: {
490     std::vector<MWAWParagraph> paraStack;
491     m_textParser->sendTopic(m_id,0,paraStack);
492     break;
493   }
494   case 1:
495     m_textParser->sendComment(m_id);
496     break;
497   case 2:
498     m_textParser->sendSpeakerNote(m_id);
499     break;
500   default:
501     MWAW_DEBUG_MSG(("SubDocument::parse: unknow value in m_what\n"));
502     break;
503   }
504   m_input->seek(pos, librevenge::RVNG_SEEK_SET);
505 }
506 
operator !=(MWAWSubDocument const & doc) const507 bool SubDocument::operator!=(MWAWSubDocument const &doc) const
508 {
509   if (MWAWSubDocument::operator!=(doc)) return true;
510   auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
511   if (!sDoc) return true;
512   if (m_textParser != sDoc->m_textParser) return true;
513   if (m_id != sDoc->m_id) return true;
514   return m_what != sDoc->m_what;
515 }
516 }
517 
518 ////////////////////////////////////////////////////////////
519 // constructor/destructor, ...
520 ////////////////////////////////////////////////////////////
MoreText(MoreParser & parser)521 MoreText::MoreText(MoreParser &parser)
522   : m_parserState(parser.getParserState())
523   , m_state(new MoreTextInternal::State)
524   , m_mainParser(&parser)
525 {
526 }
527 
~MoreText()528 MoreText::~MoreText()
529 {
530 }
531 
version() const532 int MoreText::version() const
533 {
534   if (m_state->m_version < 0)
535     m_state->m_version = m_parserState->m_version;
536   return m_state->m_version;
537 }
538 
numPages() const539 int MoreText::numPages() const
540 {
541   if (m_state->m_numPages >= 0)
542     return m_state->m_numPages;
543 
544   MWAW_DEBUG_MSG(("MoreText::createZones is not called\n"));
545   const_cast<MoreText *>(this)->createZones();
546   return m_state->m_numPages;
547 }
548 
getHeaderFooter(bool header)549 std::shared_ptr<MWAWSubDocument> MoreText::getHeaderFooter(bool header)
550 {
551   std::shared_ptr<MWAWSubDocument> res;
552   size_t id=header ? 1 : 2;
553   if (id >= m_state->m_topicList.size())
554     return res;
555   auto const &topic= m_state->m_topicList[id];
556   // check if the content is empty
557   int comment=topic.m_attachList[MoreTextInternal::Topic::AComment];
558   if (comment < 0 || comment >= int(m_state->m_commentList.size()))
559     return res;
560   if (m_state->m_commentList[size_t(comment)].m_entry.length()<=4)
561     return res;
562   res.reset(new MoreTextInternal::SubDocument(*this, m_parserState->m_input, int(id), 0));
563   return res;
564 }
565 
566 ////////////////////////////////////////////////////////////
567 // Intermediate level
568 ////////////////////////////////////////////////////////////
569 
570 //
571 // find/send the different zones
572 //
createZones()573 bool MoreText::createZones()
574 {
575   if (m_state->m_topicList.empty())
576     return false;
577 
578   // first create the list of cloned topic
579   std::vector<int> clonedList;
580   for (size_t i=0; i < m_state->m_topicList.size(); i++) {
581     auto const &topic=m_state->m_topicList[i];
582     if (topic.m_isCloned)
583       clonedList.push_back(int(i));
584   }
585   // now associated each topic with its outline, its comment, its original topic (if clone)
586   auto numCloned=int(clonedList.size());
587   size_t actAttach[3]= {0,0,0};
588   size_t numAttach[3]= {0,0,0};
589   numAttach[MoreTextInternal::Topic::AOutline]=m_state->m_outlineList.size();
590   numAttach[MoreTextInternal::Topic::AComment]=m_state->m_commentList.size();
591   numAttach[MoreTextInternal::Topic::ASpeakerNote]=m_state->m_speakerList.size();
592   int n=0;
593   for (auto &topic : m_state->m_topicList) {
594     ++n;
595     for (int j=0; j < 3; j++) {
596       if (!topic.m_hasList[j])
597         continue;
598       if (actAttach[j] >= numAttach[j]) {
599         MWAW_DEBUG_MSG(("MoreText::createZones: can not find attach-%d for topic %d\n", j, n-1));
600         continue;
601       }
602       topic.m_attachList[j]=int(actAttach[j]++);
603       switch (j) {
604       case MoreTextInternal::Topic::AOutline:
605         break;
606       case MoreTextInternal::Topic::AComment: // no need to add empty comment
607         if (m_state->m_commentList[size_t(topic.m_attachList[j])].m_entry.length() <= 4)
608           topic.m_attachList[j]=-1;
609         break;
610       case MoreTextInternal::Topic::ASpeakerNote: // no need to add empty speaker note
611         if (m_state->m_speakerList[size_t(topic.m_attachList[j])].length() <= 4)
612           topic.m_attachList[j]=-1;
613         break;
614       default:
615         break;
616       }
617     }
618     int cloneId=topic.m_cloneId;
619     if (cloneId < 0)
620       continue;
621     if (cloneId==0 || cloneId > numCloned) {
622       MWAW_DEBUG_MSG(("MoreText::createZones: can not find original for topic %d\n", n-1));
623       topic.m_cloneId=-1;
624     }
625     else
626       topic.m_cloneId=clonedList[size_t(cloneId-1)];
627   }
628 
629   // now check that we have no loop
630   for (size_t i=0; i < m_state->m_topicList.size(); i++) {
631     auto &topic=m_state->m_topicList[i];
632     if (topic.m_cloneId<0)
633       continue;
634     std::set<size_t> parent;
635     checkTopicList(i, parent);
636   }
637   // now compute the number of page
638   int nPages = 1;
639   for (auto const &topic : m_state->m_topicList) {
640     if (topic.m_numPageBreak >= 0) {
641       nPages+=topic.m_numPageBreak;
642     }
643     int outId=topic.m_attachList[MoreTextInternal::Topic::AOutline];
644     if (outId<0) continue;
645     if (m_state->m_outlineList[size_t(outId)].m_paragraphs[0].m_pageBreak)
646       nPages++;
647   }
648   m_state->m_actualPage = 1;
649   m_state->m_numPages = nPages;
650   return true;
651 }
652 
getLastTopicChildId(int tId) const653 int MoreText::getLastTopicChildId(int tId) const
654 {
655   size_t numTopic=m_state->m_topicList.size();
656   if (tId < 0||tId>= int(numTopic)) {
657     MWAW_DEBUG_MSG(("MoreText::getLastTopicChildId: can not find topic %d\n", tId));
658     return tId;
659   }
660   auto p=size_t(tId);
661   int level=m_state->m_topicList[p].m_level;
662   while (p+1<numTopic && m_state->m_topicList[p].m_level>level)
663     p++;
664   return int(p);
665 }
666 
checkTopicList(size_t tId,std::set<size_t> & parent)667 int MoreText::checkTopicList(size_t tId, std::set<size_t> &parent)
668 {
669   size_t numTopic=m_state->m_topicList.size();
670   if (tId>=numTopic) {
671     MWAW_DEBUG_MSG(("MoreText::checkTopicList: can not find topic %d\n", int(tId)));
672     return 0;
673   }
674   if (parent.find(tId)!=parent.end()) {
675     MWAW_DEBUG_MSG(("MoreText::checkTopicList: repairing fails\n"));
676     throw libmwaw::ParseException();
677   }
678   parent.insert(tId);
679   auto &topic=m_state->m_topicList[tId];
680   int nPages=0;
681   int outId=topic.m_attachList[MoreTextInternal::Topic::AOutline];
682   if (outId>=0) {
683     if (m_state->m_outlineList[size_t(outId)].m_paragraphs[0].m_pageBreak)
684       nPages++;
685   }
686   auto id=int(tId);
687   if (topic.m_cloneId>=0) {
688     if (parent.find(size_t(topic.m_cloneId))!=parent.end()) {
689       MWAW_DEBUG_MSG(("MoreText::checkTopicList: find a loop, remove a clone\n"));
690       topic.m_cloneId=-1;
691       parent.erase(tId);
692       return 0;
693     }
694     id = topic.m_cloneId;
695     parent.insert(size_t(id));
696   }
697   int lastTId=getLastTopicChildId(id);
698   for (int i=id+1; i<=lastTId; i++)
699     nPages+=checkTopicList(size_t(i), parent);
700   topic.m_numPageBreak=nPages;
701   parent.erase(tId);
702   if (id!=int(tId))
703     parent.erase(size_t(tId));
704   return nPages;
705 }
706 
707 // try read to the file text position
readTopic(MWAWEntry const & entry)708 bool MoreText::readTopic(MWAWEntry const &entry)
709 {
710   if (!entry.valid() || (entry.length()%10)) {
711     MWAW_DEBUG_MSG(("MoreText::readTopic: the entry is bad\n"));
712     return false;
713   }
714   // topic 0-2: ?, header,footer, topic 3: title, after document
715   long pos = entry.begin();
716   MWAWInputStreamPtr &input= m_parserState->m_input;
717   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
718   libmwaw::DebugStream f;
719 
720   input->seek(pos, librevenge::RVNG_SEEK_SET);
721   entry.setParsed(true);
722 
723   ascFile.addPos(pos);
724   ascFile.addNote("Entries(Topic)");
725 
726   auto N=int(entry.length()/10);
727   for (int i=0; i < N; i++) {
728     pos=input->tell();
729     MoreTextInternal::Topic topic;
730     f.str("");
731     topic.m_level = static_cast<int>(input->readLong(2));
732     bool isAClone=false;
733     auto flag = static_cast<int>(input->readULong(2)); // some flag ?
734     if ((flag&1)==0) f << "hidden,";
735     if (flag&4) f << "marked,";
736     if (flag&0x10) topic.m_isCloned=true;
737     if (flag&0x20) isAClone=true;
738     if (flag&0x40) {
739       topic.m_hasList[MoreTextInternal::Topic::ASpeakerNote]=true;
740       f << "S" << m_state->m_actualSpeaker++ << ",";
741     }
742     if (flag&0x80) {
743       topic.m_hasList[MoreTextInternal::Topic::AComment]=true;
744       f << "C" << m_state->m_actualComment++ << ",";
745     }
746     if (flag&0x400) f << "showComment,";
747     if (flag&0x2000) topic.m_isStartSlide=true;
748     if (flag&0x8000) {
749       topic.m_hasList[MoreTextInternal::Topic::AOutline]=true;
750       f << "O" << m_state->m_actualOutline++ << ",";
751     }
752     // find only bits: 5208
753     flag &= 0x5B4A;
754     if (flag) f << "fl=" << std::hex << flag << std::dec << ",";
755     long fPos = input->readLong(4);
756     if (isAClone)
757       topic.m_cloneId=static_cast<int>(fPos);
758     else {
759       f << "pos=" << std::hex << fPos << std::dec << ",";
760       topic.m_entry.setBegin(fPos);
761       if (!m_mainParser->checkAndFindSize(topic.m_entry)) {
762         MWAW_DEBUG_MSG(("MoreText::readTopic: can not read a text position\n"));
763         f << "###";
764         topic.m_entry = MWAWEntry();
765       }
766     }
767     auto val = static_cast<int>(input->readLong(2)); // a small number 1 or 2
768     if (val)
769       f << "f1=" << val << ",";
770     topic.m_extra=f.str();
771     m_state->m_topicList.push_back(topic);
772 
773     f.str("");
774     f << "Topic-" << i << ":" << topic;
775     ascFile.addPos(pos);
776     ascFile.addNote(f.str().c_str());
777     input->seek(pos+10, librevenge::RVNG_SEEK_SET);
778   }
779   return true;
780 }
781 
readComment(MWAWEntry const & entry)782 bool MoreText::readComment(MWAWEntry const &entry)
783 {
784   if (!entry.valid() || (entry.length()%8)) {
785     MWAW_DEBUG_MSG(("MoreText::readComment: the entry is bad\n"));
786     return false;
787   }
788   // comment0? comment1: header, comment2: footer
789   long pos = entry.begin();
790   MWAWInputStreamPtr &input= m_parserState->m_input;
791   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
792   libmwaw::DebugStream f;
793 
794   input->seek(pos, librevenge::RVNG_SEEK_SET);
795   entry.setParsed(true);
796 
797   ascFile.addPos(pos);
798   ascFile.addNote("Entries(Comment)");
799 
800   auto N=int(entry.length()/8);
801   for (int i=0; i < N; i++) {
802     pos=input->tell();
803     MoreTextInternal::Comment comment;
804     f.str("");
805     long fPos = input->readLong(4);
806     f << "pos=" << std::hex << fPos << std::dec << ",";
807     comment.m_entry.setBegin(fPos);
808     if (!m_mainParser->checkAndFindSize(comment.m_entry)) {
809       MWAW_DEBUG_MSG(("MoreText::readComment: can not read a file position\n"));
810       f << "###";
811       comment.m_entry.setLength(0);
812     }
813     auto val = static_cast<int>(input->readLong(2)); // always 4 ?
814     if (val != 4)
815       f << "f0=" << val << ",";
816     val = static_cast<int>(input->readULong(2)); // some flag ? find 0x3333 0x200d ...
817     if (val) f << "fl=" << std::hex << val << std::dec << ",";
818 
819     comment.m_extra=f.str();
820     m_state->m_commentList.push_back(comment);
821     f.str("");
822     f << "Comment-C" << i << ":" << comment;
823     ascFile.addPos(pos);
824     ascFile.addNote(f.str().c_str());
825     input->seek(pos+8, librevenge::RVNG_SEEK_SET);
826   }
827   return true;
828 }
829 
readSpeakerNote(MWAWEntry const & entry)830 bool MoreText::readSpeakerNote(MWAWEntry const &entry)
831 {
832   if (!entry.valid() || (entry.length()%4)) {
833     MWAW_DEBUG_MSG(("MoreText::readSpeakerNote: the entry is bad\n"));
834     return false;
835   }
836 
837   long pos = entry.begin();
838   MWAWInputStreamPtr &input= m_parserState->m_input;
839   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
840   libmwaw::DebugStream f;
841 
842   input->seek(pos, librevenge::RVNG_SEEK_SET);
843   entry.setParsed(true);
844 
845   f << "Entries(SpeakerNote):";
846   auto N=int(entry.length()/4);
847   for (int i=0; i < N; i++) {
848     long fPos = input->readLong(4);
849     f << "S" << i << ":pos=" << std::hex << fPos << std::dec << ",";
850     MWAWEntry tEntry;
851     tEntry.setBegin(fPos);
852     if (!m_mainParser->checkAndFindSize(tEntry)) {
853       MWAW_DEBUG_MSG(("MoreText::readSpeakerNote: can not read a file position\n"));
854       f << "###";
855       tEntry.setLength(0);
856     }
857     m_state->m_speakerList.push_back(tEntry);
858   }
859   ascFile.addPos(pos);
860   ascFile.addNote(f.str().c_str());
861   return true;
862 }
863 
864 //
865 // send the text
866 //
sendMainText()867 bool MoreText::sendMainText()
868 {
869   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
870   libmwaw::DebugStream f;
871   std::vector<MWAWParagraph> paraStack;
872   /* skip 0: unknown, 1: header(comment), 2: footer(comment), 3: title?, after text */
873   for (size_t i=4; i < m_state->m_topicList.size(); i++) {
874     auto const &topic = m_state->m_topicList[i];
875     MWAWEntry const &entry=topic.m_entry;
876     if (!entry.valid()) { // a clone ?
877       sendTopic(int(i),0,paraStack);
878       continue;
879     }
880     ascFile.addPos(entry.end());
881     ascFile.addNote("_");
882     if (sendTopic(int(i),0,paraStack))
883       continue;
884     ascFile.addPos(entry.end());
885     ascFile.addNote("_");
886     f.str("");
887     f << "Topic-" << i << "[data]:";
888     ascFile.addPos(entry.begin());
889     ascFile.addNote(f.str().c_str());
890   }
891   return true;
892 }
893 
sendComment(int cId)894 bool MoreText::sendComment(int cId)
895 {
896   MWAWTextListenerPtr listener=m_parserState->m_textListener;
897   if (!listener) {
898     MWAW_DEBUG_MSG(("MoreText::sendComment: can not find a listener!"));
899     return true;
900   }
901   if (cId < 0 || cId >= int(m_state->m_commentList.size())) {
902     MWAW_DEBUG_MSG(("MoreText::sendComment: can not find the comment %d!", cId));
903     return false;
904   }
905   return sendText(m_state->m_commentList[size_t(cId)].m_entry, MWAWFont(3,12));
906 }
907 
sendSpeakerNote(int nId)908 bool MoreText::sendSpeakerNote(int nId)
909 {
910   MWAWTextListenerPtr listener=m_parserState->m_textListener;
911   if (!listener) {
912     MWAW_DEBUG_MSG(("MoreText::sendSpeakerNote: can not find a listener!"));
913     return true;
914   }
915   if (nId < 0 || nId >= int(m_state->m_speakerList.size())) {
916     MWAW_DEBUG_MSG(("MoreText::sendSpeakerNote: can not find the speaker note %d!", nId));
917     return false;
918   }
919   return sendText(m_state->m_speakerList[size_t(nId)], MWAWFont(3,12));
920 }
921 
sendTopic(int tId,int dLevel,std::vector<MWAWParagraph> & paraStack)922 bool MoreText::sendTopic(int tId, int dLevel, std::vector<MWAWParagraph> &paraStack)
923 {
924   MWAWTextListenerPtr listener=m_parserState->m_textListener;
925   if (!listener) {
926     MWAW_DEBUG_MSG(("MoreText::sendTopic: can not find a listener!"));
927     return true;
928   }
929   if (tId < 0 || tId >= int(m_state->m_topicList.size())) {
930     MWAW_DEBUG_MSG(("MoreText::sendTopic: can not find the topic note %d!", tId));
931     return false;
932   }
933   auto const &topic=m_state->m_topicList[size_t(tId)];
934   MWAWEntry entry=topic.m_entry;
935   int level=topic.m_level+dLevel;
936   if (level < 0) {
937     if (tId>=4) {
938       MWAW_DEBUG_MSG(("MoreText::sendTopic: oops level is negatif!\n"));
939     }
940     level=0;
941   }
942   // data to send clone child
943   int actId=tId, lastId = tId, cloneDLevel=0;
944   if (tId==1||tId==2) {
945     // header/footer: the data are in the comment...
946     int comment=topic.m_attachList[MoreTextInternal::Topic::AComment];
947     if (comment<0||comment>=int(m_state->m_commentList.size()))
948       return false;
949     entry=m_state->m_commentList[size_t(comment)].m_entry;
950   }
951   else if (topic.m_cloneId>=0 && topic.m_cloneId < static_cast<int>(m_state->m_topicList.size())) {
952     auto const &cTopic = m_state->m_topicList[size_t(topic.m_cloneId)];
953     entry=cTopic.m_entry;
954     actId = topic.m_cloneId;
955     lastId = getLastTopicChildId(topic.m_cloneId);
956     cloneDLevel = cTopic.m_level-level;;
957   }
958   if (!entry.valid())
959     return false;
960   // retrieve the ouline
961   MWAWFont font(3,12);
962   MoreTextInternal::Paragraph para;
963   int outl=topic.m_attachList[MoreTextInternal::Topic::AOutline];
964   if (outl>=0 && outl < static_cast<int>(m_state->m_outlineList.size())) {
965     auto const &outline=m_state->m_outlineList[size_t(outl)];
966     if (outline.m_paragraphs[0].m_pageBreak)
967       m_mainParser->newPage(++m_state->m_actualPage);
968     para=outline.m_paragraphs[0];
969     font = outline.m_fonts[0];
970   }
971   else if (tId>=4) {
972     /* default: leader is default for a paragraph
973 
974     note: sometimes, some small level are bold by default, I do not understand why ? */
975     para.m_listType=1;
976   }
977   if (tId==1||tId==2) // force no list in header footer
978     para.m_listType=0;
979   if (level >= int(paraStack.size()))
980     paraStack.resize(size_t(level+1));
981   if (level>0)
982     para.updateToFinalState(paraStack[size_t(level-1)], level,
983                             *m_parserState->m_listManager);
984   else
985     para.updateToFinalState(MWAWParagraph(),0, *m_parserState->m_listManager);
986   if (level>=0)
987     paraStack[size_t(level)]=para;
988 
989   listener->setParagraph(para);
990   bool ok=sendText(entry, font);
991   if (tId==1||tId==2)
992     return true;
993   // send potential comment and speakernote
994   int comment=topic.m_attachList[MoreTextInternal::Topic::AComment];
995   if (comment>=0) {
996     MWAWSubDocumentPtr doc(new MoreTextInternal::SubDocument(*this, m_parserState->m_input, comment, 1));
997     listener->insertComment(doc);
998   }
999   int speaker=topic.m_attachList[MoreTextInternal::Topic::ASpeakerNote];
1000   if (speaker>=0) {
1001     MWAWSubDocumentPtr doc(new MoreTextInternal::SubDocument(*this, m_parserState->m_input, speaker, 2));
1002     listener->insertComment(doc);
1003   }
1004 
1005   listener->insertEOL();
1006   for (int i=actId+1; i <= lastId; i++)
1007     sendTopic(i, dLevel+cloneDLevel, paraStack);
1008   return ok;
1009 }
1010 
sendText(MWAWEntry const & entry,MWAWFont const & font)1011 bool MoreText::sendText(MWAWEntry const &entry, MWAWFont const &font)
1012 {
1013   MWAWTextListenerPtr listener=m_parserState->m_textListener;
1014   if (!listener) {
1015     MWAW_DEBUG_MSG(("MoreText::sendText: can not find a listener!"));
1016     return true;
1017   }
1018 
1019   MWAWInputStreamPtr &input= m_parserState->m_input;
1020   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1021   libmwaw::DebugStream f;
1022   long pos = entry.begin();
1023   long endPos = entry.end();
1024   if (entry.length()==4) { // no text, we can stop here
1025     ascFile.addPos(pos);
1026     ascFile.addNote("Entries(Text):");
1027     ascFile.addPos(entry.end());
1028     ascFile.addNote("_");
1029     return true;
1030   }
1031 
1032   if (entry.length()<4) {
1033     MWAW_DEBUG_MSG(("MoreText::sendText: the entry is bad\n"));
1034     return false;
1035   }
1036 
1037   input->seek(pos+4, librevenge::RVNG_SEEK_SET);
1038   listener->setFont(font);
1039   f << "Entries(Text):";
1040   int val;
1041   MWAWFont ft(font);
1042   MWAWColor defCol;
1043   font.getColor(defCol);
1044   uint32_t defFlags=font.flags();
1045   bool defHasUnderline=font.getUnderline().isSet();
1046   listener->setFont(ft);
1047   while (!input->isEnd()) {
1048     long actPos = input->tell();
1049     if (actPos >= endPos)
1050       break;
1051     auto c=static_cast<unsigned char>(input->readULong(1));
1052     if (c!=0x1b) {
1053       listener->insertCharacter(c);
1054       f << c;
1055       continue;
1056     }
1057     if (actPos+1 >= endPos) {
1058       f << "@[#]";
1059       MWAW_DEBUG_MSG(("MoreText::sendText: text end by 0x1b\n"));
1060       continue;
1061     }
1062     auto fld=static_cast<int>(input->readULong(1));
1063     bool sendFont=true;
1064     switch (fld) {
1065     case 0x9:
1066       listener->insertTab();
1067       f << "\t";
1068       break;
1069     case 0xd: // EOL in header/footer
1070       listener->insertEOL();
1071       f << char(0xd);
1072       break;
1073     case 0x2d: // geneva
1074       ft.setId(font.id());
1075       sendFont=false;
1076       f << "@[fId=def]";
1077       break;
1078     case 0x2e: // 12
1079       ft.setSize(font.size());
1080       sendFont=false;
1081       f << "@[fSz=def]";
1082       break;
1083     case 0x2f: // black
1084       ft.setColor(defCol);
1085       sendFont=false;
1086       f << "@[fCol=def]";
1087       break;
1088     case 0x30: // font
1089       if (actPos+4+2 > endPos) {
1090         f << "@[#fId]";
1091         MWAW_DEBUG_MSG(("MoreText::sendText: field font seems too short\n"));
1092         break;
1093       }
1094       val = static_cast<int>(input->readULong(2));
1095       if (!(val&0x8000)) {
1096         MWAW_DEBUG_MSG(("MoreText::sendText: field fId: unexpected id\n"));
1097         f << "@[#fId]";
1098         input->seek(-2, librevenge::RVNG_SEEK_CUR);
1099         break;
1100       }
1101       f << "@[fId=" << (val&0x7FFF) << "]";
1102       ft.setId(val&0x7FFF);
1103       sendFont = false;
1104       val = static_cast<int>(input->readULong(2));
1105       if (val!=0x1b30) {
1106         MWAW_DEBUG_MSG(("MoreText::sendText: field fId: unexpected end field\n"));
1107         f << "###";
1108         input->seek(-2, librevenge::RVNG_SEEK_CUR);
1109         break;
1110       }
1111       break;
1112     case 0x31:
1113       if (actPos+4+2 > endPos) {
1114         f << "@[#fSz]";
1115         MWAW_DEBUG_MSG(("MoreText::sendText: field fSz seems too short\n"));
1116         break;
1117       }
1118       val = static_cast<int>(input->readLong(2));
1119       f << "@[fSz=" << val << "]";
1120       if (val <= 0) {
1121         MWAW_DEBUG_MSG(("MoreText::sendText: field fSz seems bad\n"));
1122         f << "###";
1123       }
1124       else {
1125         ft.setSize(float(val));
1126         sendFont = false;
1127       }
1128       val = static_cast<int>(input->readULong(2));
1129       if (val!=0x1b31) {
1130         MWAW_DEBUG_MSG(("MoreText::sendText: field fSz: unexpected end field\n"));
1131         f << "###";
1132         input->seek(-2, librevenge::RVNG_SEEK_CUR);
1133         break;
1134       }
1135       break;
1136     case 0x38: {
1137       if (actPos+4+10 > endPos) {
1138         f << "@[#fCol]";
1139         MWAW_DEBUG_MSG(("MoreText::sendText: field fCol seems too short\n"));
1140         break;
1141       }
1142       uint16_t values[5];
1143       for (auto &v : values) v=static_cast<uint16_t>(input->readULong(2));
1144       if (values[0]!=0xe || values[4]!=0xe) {
1145         MWAW_DEBUG_MSG(("MoreText::sendText: field fCol: color sep seems bad\n"));
1146         f << "@[fCol###]";
1147       }
1148       else {
1149         MWAWColor col(static_cast<unsigned char>(values[1]>>8),
1150                       static_cast<unsigned char>(values[2]>>8),
1151                       static_cast<unsigned char>(values[3]>>8));
1152         ft.setColor(col);
1153         sendFont = false;
1154         f << "@[fCol=" << col << "]";
1155       }
1156       val = static_cast<int>(input->readULong(2));
1157       if (val!=0x1b38) {
1158         MWAW_DEBUG_MSG(("MoreText::sendText: field fCol: unexpected end field\n"));
1159         f << "###";
1160         input->seek(-2, librevenge::RVNG_SEEK_CUR);
1161         break;
1162       }
1163       break;
1164     }
1165     case 0x41:
1166       ft.set(MWAWFont::Script(33));
1167       sendFont = false;
1168       f << "@[supersc]";
1169       break;
1170     case 0x42: // in fact, (line bold)^bold
1171       if (defFlags&MWAWFont::boldBit)
1172         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::boldBit));
1173       else
1174         ft.setFlags(ft.flags()|MWAWFont::boldBit);
1175       sendFont = false;
1176       f << "@[b]";
1177       break;
1178     case 0x49: // in fact, (line italic)^italic
1179       if (defFlags&MWAWFont::italicBit)
1180         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::italicBit));
1181       else
1182         ft.setFlags(ft.flags()|MWAWFont::italicBit);
1183       sendFont = false;
1184       f << "@[it]";
1185       break;
1186     case 0x4c:
1187       ft.set(MWAWFont::Script(-33));
1188       sendFont = false;
1189       f << "@[subsc]";
1190       break;
1191     case 0x4f:
1192       if (defFlags&MWAWFont::outlineBit)
1193         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::outlineBit));
1194       else
1195         ft.setFlags(ft.flags()|MWAWFont::outlineBit);
1196       sendFont = false;
1197       f << "@[outline]";
1198       break;
1199     case 0x53:
1200       if (defFlags&MWAWFont::shadowBit)
1201         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::shadowBit));
1202       else
1203         ft.setFlags(ft.flags()|MWAWFont::shadowBit);
1204       sendFont = false;
1205       f << "@[shadow]";
1206       break;
1207     case 0x55:
1208       ft.setUnderlineStyle(defHasUnderline ? MWAWFont::Line::None : MWAWFont::Line::Simple);
1209       sendFont = false;
1210       f << "@[underl]";
1211       break;
1212     case 0x61:
1213       ft.set(MWAWFont::Script());
1214       sendFont = false;
1215       f << "@[script=def]";
1216       break;
1217     case 0x62:
1218       if ((defFlags&MWAWFont::boldBit)==0)
1219         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::boldBit));
1220       else
1221         ft.setFlags(ft.flags()|MWAWFont::boldBit);
1222       sendFont = false;
1223       f << "@[/b]";
1224       break;
1225     case 0x69:
1226       if ((defFlags&MWAWFont::italicBit)==0)
1227         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::italicBit));
1228       else
1229         ft.setFlags(ft.flags()|MWAWFont::italicBit);
1230       sendFont = false;
1231       f << "@[/it]";
1232       break;
1233     case 0x6f:
1234       if ((defFlags&MWAWFont::outlineBit)==0)
1235         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::outlineBit));
1236       else
1237         ft.setFlags(ft.flags()|MWAWFont::outlineBit);
1238       sendFont = false;
1239       f << "@[/outline]";
1240       break;
1241     case 0x73:
1242       if ((defFlags&MWAWFont::shadowBit)==0)
1243         ft.setFlags(ft.flags()&uint32_t(~MWAWFont::shadowBit));
1244       else
1245         ft.setFlags(ft.flags()|MWAWFont::shadowBit);
1246       sendFont = false;
1247       f << "@[/shadow]";
1248       break;
1249     case 0x75:
1250       ft.setUnderlineStyle(defHasUnderline ? MWAWFont::Line::Simple : MWAWFont::Line::None);
1251       sendFont = false;
1252       f << "@[/underl]";
1253       break;
1254     case 0xb9: {
1255       if (actPos+4+8 > endPos) {
1256         f << "@[#field]";
1257         MWAW_DEBUG_MSG(("MoreText::sendText: field b9 seems too short\n"));
1258         break;
1259       }
1260       uint16_t values[4];
1261       for (auto &v : values) v=static_cast<uint16_t>(input->readULong(2));
1262       if (values[0]!=0xc || values[3]!=0xc) {
1263         MWAW_DEBUG_MSG(("MoreText::sendText: field the separator seems bad\n"));
1264         f << "@[field###]";
1265       }
1266       else {
1267         switch (values[1]) {
1268         case 1:
1269           listener->insertUnicodeString(librevenge::RVNGString("#Slide#"));
1270           f << "@[slide/title";
1271           if (values[2]) f << ":" << values[2]; // always 0?
1272           f << "]";
1273           break;
1274         case 3:
1275           listener->insertField(MWAWField(MWAWField::Title));
1276           f << "@[title";
1277           if (values[2]) f << ":" << values[2]; // always 0?
1278           f << "]";
1279           break;
1280         case 4:
1281           listener->insertUnicodeString(librevenge::RVNGString("#Folder#"));
1282           f << "@[folder]";
1283           if (values[2]) f << ":" << values[2]; // always 0?
1284           f << "]";
1285           break;
1286         case 5:
1287           listener->insertField(MWAWField(MWAWField::PageNumber));
1288           f << "@[pNumber";
1289           if (values[2]!=0xa) f << ":" << values[2];
1290           f << "]";
1291           break;
1292         case 6: // actual time
1293         case 7: // last modif
1294           listener->insertField(MWAWField(MWAWField::Time));
1295           f << "@[time";
1296           if (values[1]==7) f << "2";
1297           if (values[2]!=1) f << ":" << values[2];
1298           f << "]";
1299           break;
1300         case 8: // actual date
1301         case 9: // last modif
1302           listener->insertField(MWAWField(MWAWField::Date));
1303           f << "@[date";
1304           if (values[1]==7) f << "2";
1305           if (values[2]!=0x200) f << ":" << values[2];
1306           f << "]";
1307           break;
1308         case 0xc:
1309           listener->insertField(MWAWField(MWAWField::PageCount));
1310           f << "@[pNumber";
1311           if (values[2]!=0xa) f << ":" << values[2];
1312           f << "]";
1313           break;
1314         default:
1315           f << "@[field=##" << values[1] << ":" << values[2] << "]";
1316           MWAW_DEBUG_MSG(("MoreText::sendText: unknown field\n"));
1317           break;
1318         }
1319       }
1320       val = static_cast<int>(input->readULong(2));
1321       if (val!=0x1bb9) {
1322         MWAW_DEBUG_MSG(("MoreText::sendText: field b9: unexpected end field\n"));
1323         f << "###";
1324         input->seek(-2, librevenge::RVNG_SEEK_CUR);
1325       }
1326       break;
1327     }
1328     case 0xf9: {
1329       if (actPos+22 > endPos) {
1330         f << "@[#picture]";
1331         MWAW_DEBUG_MSG(("MoreText::sendText: field f9 seems too short\n"));
1332         break;
1333       }
1334       auto sz=long(input->readULong(4));
1335       if (sz<22 || actPos+sz>=endPos) {
1336         MWAW_DEBUG_MSG(("MoreText::sendText: field f9: bad field size\n"));
1337         f << "###";
1338         input->seek(actPos+2, librevenge::RVNG_SEEK_CUR);
1339         break;
1340       }
1341       input->seek(actPos+sz-6, librevenge::RVNG_SEEK_SET);
1342       if (static_cast<int>(input->readULong(4))!=sz &&
1343           static_cast<int>(input->readULong(2))!=int(0x1b00|fld)) {
1344         MWAW_DEBUG_MSG(("MoreText::sendText: find a unknown picture end field\n"));
1345         f << "@[#" << std::hex << fld << std::dec << ":" << sz << "]";
1346         input->seek(actPos+2, librevenge::RVNG_SEEK_SET);
1347         break;
1348       }
1349       input->seek(actPos+6, librevenge::RVNG_SEEK_SET);
1350       f << "[picture:";
1351       val = static_cast<int>(input->readLong(2));
1352       if (val!=0x100)
1353         f << "type=" << std::hex << val << std::dec << ",";
1354       float dim[4];
1355       for (auto &d : dim) d = float(input->readLong(2));
1356       MWAWBox2f bdbox(MWAWVec2f(dim[1],dim[0]), MWAWVec2f(dim[3],dim[2]));
1357       f << "bdbox=" << bdbox << ",";
1358       if (sz>22) {
1359         std::shared_ptr<MWAWPict> pict(MWAWPictData::get(input, static_cast<int>(sz)-22));
1360         MWAWEmbeddedObject picture;
1361         if (pict && pict->getBinary(picture)) {
1362           MWAWPosition pictPos(MWAWVec2f(0,0), bdbox.size(), librevenge::RVNG_POINT);
1363           pictPos.m_anchorTo = MWAWPosition::Char;
1364           listener->insertPicture(pictPos, picture);
1365         }
1366 #ifdef DEBUG_WITH_FILES
1367         if (1) {
1368           librevenge::RVNGBinaryData file;
1369           input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
1370           input->readDataBlock(sz-22, file);
1371           static int volatile pictName = 0;
1372           libmwaw::DebugStream f2;
1373           f2 << "Pict-" << ++pictName << ".pct";
1374           libmwaw::Debug::dumpFile(file, f2.str().c_str());
1375           ascFile.skipZone(actPos+16, actPos+sz-7);
1376         }
1377 #endif
1378       }
1379       input->seek(actPos+sz, librevenge::RVNG_SEEK_SET);
1380       break;
1381     }
1382     default: {
1383       auto sz=static_cast<int>(input->readULong(2));
1384       if (sz>4 && actPos+sz<=endPos) {
1385         input->seek(actPos+sz-4, librevenge::RVNG_SEEK_SET);
1386         if (static_cast<int>(input->readULong(2))==sz &&
1387             static_cast<int>(input->readULong(2))==int(0x1b00|fld)) {
1388           MWAW_DEBUG_MSG(("MoreText::sendText: find a unknown field, but can infer size\n"));
1389           f << "@[#" << std::hex << fld << std::dec << ":" << sz << "]";
1390           break;
1391         }
1392         input->seek(actPos+2, librevenge::RVNG_SEEK_SET);
1393       }
1394       MWAW_DEBUG_MSG(("MoreText::sendText: find a unknown field\n"));
1395       f << "@[#" << std::hex << fld << std::dec << "]";
1396       break;
1397     }
1398     }
1399     if (!sendFont)
1400       listener->setFont(ft);
1401   }
1402   ascFile.addPos(pos);
1403   ascFile.addNote(f.str().c_str());
1404   ascFile.addPos(entry.end());
1405   ascFile.addNote("_");
1406   return true;
1407 }
1408 
1409 //////////////////////////////////////////////
1410 // Fonts
1411 //////////////////////////////////////////////
readFonts(MWAWEntry const & entry)1412 bool MoreText::readFonts(MWAWEntry const &entry)
1413 {
1414   MWAWInputStreamPtr &input= m_parserState->m_input;
1415   if (!entry.valid() || !input || !input->checkPosition(entry.end())) {
1416     MWAW_DEBUG_MSG(("MoreText::readFonts: the entry is bad\n"));
1417     return false;
1418   }
1419 
1420   long pos = entry.begin();
1421   long endPos = entry.end();
1422   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1423   libmwaw::DebugStream f;
1424 
1425   input->seek(pos, librevenge::RVNG_SEEK_SET);
1426   entry.setParsed(true);
1427 
1428   int n=0;
1429   while (1) {
1430     pos=input->tell();
1431     if (pos+1 > endPos) {
1432       MWAW_DEBUG_MSG(("MoreText::readFonts: problem reading a font\n"));
1433       break;
1434     }
1435     auto fSz=int(input->readULong(1));
1436     if (fSz==0)
1437       break;
1438     if (pos+1+fSz+2 > endPos) {
1439       input->seek(-1, librevenge::RVNG_SEEK_CUR);
1440       break;
1441     }
1442     f.str("");
1443     if (n==0)
1444       f << "Entries(Fonts)-" << n++ << ",";
1445     else
1446       f << "Fonts-"  << n++ << ":";
1447     std::string name("");
1448     for (int i=0; i < fSz; i++)
1449       name+=char(input->readULong(1));
1450     if ((fSz&1)==0) input->seek(1, librevenge::RVNG_SEEK_CUR);
1451     auto id=static_cast<int>(input->readULong(2));
1452     f << name << ",id=" << id << ",";
1453     if (name.length())
1454       m_parserState->m_fontConverter->setCorrespondance(id, name);
1455     ascFile.addPos(pos);
1456     ascFile.addNote(f.str().c_str());
1457   }
1458 
1459   pos = input->tell();
1460   if (pos != endPos) {
1461     MWAW_DEBUG_MSG(("MoreText::readFonts: problem reading a font\n"));
1462     ascFile.addPos(pos);
1463     ascFile.addNote("Fonts:###");
1464   }
1465 
1466   return true;
1467 }
1468 
1469 //////////////////////////////////////////////
1470 // outline
1471 //////////////////////////////////////////////
readOutlineList(MWAWEntry const & entry)1472 bool MoreText::readOutlineList(MWAWEntry const &entry)
1473 {
1474   if (!entry.valid() || (entry.length()%4)) {
1475     MWAW_DEBUG_MSG(("MoreText::readOutlineList: the entry is bad\n"));
1476     return false;
1477   }
1478 
1479   long pos = entry.begin();
1480   MWAWInputStreamPtr &input= m_parserState->m_input;
1481   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1482   libmwaw::DebugStream f;
1483 
1484   input->seek(pos, librevenge::RVNG_SEEK_SET);
1485   entry.setParsed(true);
1486 
1487   f << "Entries(Outline):";
1488   auto N=int(entry.length()/4);
1489   std::vector<MWAWEntry> posList;
1490   for (int i=0; i < N; i++) {
1491     MWAWEntry tEntry;
1492     tEntry.setBegin(input->readLong(4));
1493     tEntry.setId(int(i));
1494     if (!m_mainParser->checkAndFindSize(tEntry)) {
1495       MWAW_DEBUG_MSG(("MoreText::readOutlineList: can not read a file position\n"));
1496       f << "###,";
1497     }
1498     else
1499       f << std::hex << tEntry.begin() << "<->" << tEntry.end() << ",";
1500     posList.push_back(tEntry);
1501   }
1502   ascFile.addPos(pos);
1503   ascFile.addNote(f.str().c_str());
1504 
1505   for (auto const &tEntry : posList) {
1506     if (!tEntry.valid())
1507       continue;
1508     MoreTextInternal::Outline outline;
1509     if (readOutline(tEntry, outline)) {
1510       m_state->m_outlineList.push_back(outline);
1511       continue;
1512     }
1513     m_state->m_outlineList.push_back(MoreTextInternal::Outline());
1514     ascFile.addPos(tEntry.begin());
1515     ascFile.addNote("Outline-data:###");
1516     ascFile.addPos(tEntry.end());
1517     ascFile.addNote("_");
1518   }
1519   return true;
1520 }
1521 
readOutline(MWAWEntry const & entry,MoreTextInternal::Outline & outline)1522 bool MoreText::readOutline(MWAWEntry const &entry, MoreTextInternal::Outline &outline)
1523 {
1524   if (!entry.valid() || entry.length()<8) {
1525     MWAW_DEBUG_MSG(("MoreText::readOutline: the entry is bad\n"));
1526     return false;
1527   }
1528   int vers = version();
1529   long pos = entry.begin();
1530   long endPos = entry.end();
1531   MWAWInputStreamPtr &input= m_parserState->m_input;
1532   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
1533   libmwaw::DebugStream f;
1534 
1535   input->seek(pos+4, librevenge::RVNG_SEEK_SET); // skip size
1536 
1537   f << "Outline[O" << entry.id() << "]:";
1538   auto val=static_cast<int>(input->readULong(2));
1539   if (val!=6*(vers-1)) {
1540     MWAW_DEBUG_MSG(("MoreText::readOutline: find unexpected type\n"));
1541     f << "#f0=" << val << ",";
1542   }
1543   auto N=static_cast<int>(input->readULong(2));
1544   f << "N=" << N << ",";
1545   long lastListPos = pos+8+N*16;
1546   if (lastListPos > endPos) {
1547     MWAW_DEBUG_MSG(("MoreText::readOutline: can not read length\n"));
1548     return false;
1549   }
1550   ascFile.addPos(pos);
1551   ascFile.addNote(f.str().c_str());
1552 
1553   std::vector<MoreTextInternal::OutlineMod> outlineModList;
1554   for (int n=0; n<N; n++) {
1555     pos = input->tell();
1556     f.str("");
1557 
1558     MoreTextInternal::OutlineMod outlineMod;
1559     val=int(input->readLong(1));
1560     if (val!=6*(vers-1))
1561       f << "#f0=" << val << ",";
1562 
1563     outlineMod.m_flags=int(input->readULong(1));
1564     for (auto &unkn : outlineMod.m_unknowns) unkn=int(input->readLong(2));
1565     outlineMod.m_type=int(input->readULong(2));
1566     int values[4];
1567     for (auto &v : values) v = static_cast<int>(input->readULong(2));
1568     int const which= outlineMod.getModId();
1569     auto &para = outline.m_paragraphs[which];
1570     MWAWFont &font=outline.m_fonts[which];
1571     uint32_t fFlags=font.flags();
1572     bool haveExtra=false;
1573     switch (outlineMod.m_type) {
1574     case 0x301: // font name
1575     case 0xf07: // left indent+tabs
1576       haveExtra=true;
1577       break;
1578     case 0x402:
1579       // size can be very big, force it to be smallest than 100
1580       if (values[0]>0 && values[0] <= 100) {
1581         font.setSize(float(values[0]));
1582         f << "sz=" << values[0] << ",";
1583       }
1584       else {
1585         MWAW_DEBUG_MSG(("MoreText::readOutline: the font size seems bad\n"));
1586         f << "##sz=" << values[0] << ",";
1587       }
1588       break;
1589     case 0x603: {
1590       uint32_t bit=0;
1591       switch (values[0]) {
1592       case 0:
1593         f << "plain";
1594         if (values[1]==1)
1595           fFlags=0;
1596         break;
1597       case 1:
1598         bit = MWAWFont::boldBit;
1599         f << "b";
1600         break;
1601       case 2:
1602         bit = MWAWFont::italicBit;
1603         f << "it";
1604         break;
1605       case 3:
1606         if (values[1]==1)
1607           font.setUnderlineStyle(MWAWFont::Line::Simple);
1608         f << "underl";
1609         break;
1610       case 4:
1611         bit = MWAWFont::outlineBit;
1612         f << "outline";
1613         break;
1614       case 5:
1615         bit = MWAWFont::shadowBit;
1616         f << "shadow";
1617         break;
1618       default:
1619         f << "##fl=" << std::hex << values[0] << std::dec;
1620         break;
1621       }
1622       if (values[1]==1) {
1623         if (bit) fFlags = fFlags & (~bit);
1624         f << "[of],";
1625       }
1626       else if (values[1]!=0)
1627         f << "=##" << values[1] << ",";
1628       else
1629         fFlags |= bit;
1630       values[1]=0;
1631       break;
1632     }
1633     case 0x804: {
1634       MWAWColor col(static_cast<unsigned char>(static_cast<uint16_t>(values[0])>>8),
1635                     static_cast<unsigned char>(static_cast<uint16_t>(values[1])>>8),
1636                     static_cast<unsigned char>(static_cast<uint16_t>(values[2])>>8));
1637       font.setColor(col);
1638       f << col << ",";
1639       values[1]=values[2]=0;
1640       break;
1641     }
1642     case 0xa05:
1643       if (values[0]&0x8000) {
1644         para.setInterline(double(values[0]&0x7FFF)/20., librevenge::RVNG_POINT, MWAWParagraph::AtLeast);
1645         f << "interline=" << *para.m_spacings[0] << "pt,";
1646       }
1647       else {
1648         para.setInterline(double(values[0])/double(0x1000), librevenge::RVNG_PERCENT);
1649         f << "interline=" << 100* *para.m_spacings[0] << "%,";
1650       }
1651       break;
1652     case 0xc0f: // firstIndent
1653       para.m_margins[0] = double(values[0])/1440.;
1654       f << "indent=" << *para.m_margins[0] << ",";
1655       break;
1656     case 0x1006:
1657       switch (values[0]) {
1658       case 0:
1659         para.m_justify = MWAWParagraph::JustificationLeft;
1660         f << "left,";
1661         break;
1662       case 1:
1663         para.m_justify = MWAWParagraph::JustificationCenter;
1664         f << "center,";
1665         break;
1666       case 2:
1667         para.m_justify = MWAWParagraph::JustificationRight;
1668         f << "right,";
1669         break;
1670       case 3:
1671         para.m_justify = MWAWParagraph::JustificationFull;
1672         f << "full,";
1673         break;
1674       default:
1675         f << "##justify=" << values[0] << ",";
1676         break;
1677       }
1678       break;
1679     case 0x1208:
1680       if (values[0] & 0x8000) {
1681         para.m_spacings[1]=double(values[0]&0x7FFF)/1440.;
1682         f << "bef=" << double(values[0]&0x7FFF)/20. << "pt,";
1683       }
1684       else {
1685         // assume 12pt
1686         para.m_spacings[1]=double(values[0])/double(0x1000)*12./72.;
1687         if (values[0])
1688           f << "bef=" << 100.*double(values[0])/double(0x1000) << "%,";
1689       }
1690 
1691       if (values[1] & 0x8000) {
1692         para.m_spacings[2]=double(values[1]&0x7FFF)/1440.;
1693         f << "aft=" << double(values[1]&0x7FFF)/20. << "pt,";
1694       }
1695       else {
1696         para.m_spacings[2]=double(values[1])/double(0x1000)*12./72.;
1697         if (values[1])
1698           f << "aft=" << 100.*double(values[1])/double(0x1000) << "%,";
1699       }
1700       values[1]=0;
1701       break;
1702     case 0x1409: // lMargin in TWIP
1703       para.setLeftMargin(double(values[0]&0x7FFF)/1440., (values[0]&0x8000)==0);
1704       if (values[0]&0x8000)
1705         f << "indent=" << double(values[0]&0x7FFF)/1440. << ",";
1706       else // checkme
1707         f << "indent=" << double(values[0])/1440. << "[fromParent],";
1708       break;
1709     case 0x160a: // rMargin in TWIP
1710       para.setRightMargin(double(values[0]&0x7FFF)/1440., (values[0]&0x8000)==0);
1711       if (values[0]&0x8000)
1712         f << "indent=" << double(values[0]&0x7FFF)/1440. << ",";
1713       else // checkme
1714         f << "indent=" << double(values[0])/1440. << "[fromParent],";
1715       break;
1716     case 0x1a0c:
1717       para.m_pageBreak=(values[0]==0x100);
1718       if (values[0]==0x100)
1719         f << "pagebreak,";
1720       else if (values[0]==0)
1721         f << "no,";
1722       else
1723         f << "##break=" << std::hex << values[0] << std::dec << ",";
1724       break;
1725     case 0x1c0d:
1726       if (values[0]==0x100) {
1727         para.m_breakStatus = (*para.m_breakStatus)|MWAWParagraph::NoBreakWithNextBit;
1728         f << "together,";
1729       }
1730       else if (values[0]==0) {
1731         para.m_breakStatus = (*para.m_breakStatus)|int(~MWAWParagraph::NoBreakWithNextBit);
1732         f << "no,";
1733       }
1734       else
1735         f << "#keepLine=" << std::hex << values[0] << std::dec << ",";
1736       break;
1737     case 0x1e0e:
1738       para.m_keepOutlineTogether = (values[0]==0x100);
1739       if (values[0]==0x100)
1740         f << "together,";
1741       else if (values[0]==0)
1742         f << "no,";
1743       else
1744         f << "#keepOutline=" << std::hex << values[0] << std::dec << ",";
1745       break;
1746     case 0x190b:
1747       para.m_listType = values[0];
1748       switch (values[0]) {
1749       case 0:
1750         f << "no,";
1751         break;
1752       case 1:
1753         f << "leader,";
1754         break;
1755       case 2:
1756         f << "hardvard,";
1757         break;
1758       case 3:
1759         f << "numeric,";
1760         break;
1761       case 4:
1762         f << "legal,";
1763         break;
1764       case 5:
1765         f << "bullets,";
1766         break;
1767       default:
1768         if (values[0]>=11) {
1769           f << "custom[" << values[0] << "],";
1770           haveExtra=true;
1771           break;
1772         }
1773         f << "##bullet=" << values[0] << ",";
1774         break;
1775       }
1776       break;
1777     default:
1778       if (values[0])
1779         f << "f2=" << std::hex << values[0] << std::dec << ",";
1780       // use heuristic to define extra data
1781       if (values[0]>0x2800)
1782         haveExtra = (values[0] & 0x0100);
1783       else
1784         haveExtra=values[1]==0;
1785       break;
1786     }
1787     font.setFlags(fFlags);
1788     if (values[1]) f << "g0=" << std::hex << values[1] << std::dec << ",";
1789 
1790     if (haveExtra && values[3]>0 &&
1791         lastListPos+values[2]+values[3] <= endPos) {
1792       outlineMod.m_entry.setBegin(lastListPos+values[2]);
1793       outlineMod.m_entry.setLength(values[3]);
1794       outlineMod.m_entry.setId(n);
1795     }
1796     else {
1797       for (int i=2; i < 4; i++) {
1798         if (values[i])
1799           f << "g" << i-1 << "=" << std::hex << values[i] << std::dec << ",";
1800       }
1801     }
1802     outlineMod.m_extra=f.str();
1803     f.str("");
1804     f << "Outline[O" << entry.id() << "-" << n << "]:" << outlineMod;
1805     outlineModList.push_back(outlineMod);
1806     ascFile.addPos(pos);
1807     ascFile.addNote(f.str().c_str());
1808     input->seek(pos+16, librevenge::RVNG_SEEK_SET);
1809   }
1810 
1811   int n=0;
1812   for (auto const &outlineMod : outlineModList) {
1813     n++;
1814     if (!outlineMod.m_entry.valid())
1815       continue;
1816     f.str("");
1817     f << "Outline[O" << entry.id() << "-A" << n-1 << "]:";
1818     bool ok=false;
1819 
1820     auto &para = outline.m_paragraphs[outlineMod.getModId()];
1821     switch (outlineMod.m_type) {
1822     case 0x301: {
1823       std::string fName;
1824       int fId;
1825       ok = readFont(outlineMod.m_entry, fName,fId);
1826       if (!ok) break;
1827       f << "font=[";
1828       f << "name=" << fName;
1829       if (fId>=0) {
1830         f << ":" << fId;
1831         MWAWFont &font=outline.m_fonts[outlineMod.getModId()];
1832         font.setId(fId);
1833       }
1834       f << "],";
1835       break;
1836     }
1837     case 0xf07: {
1838       std::string mess;
1839       ok = readTabs(outlineMod.m_entry, para, mess);
1840       if (!ok) break;
1841       f << "tabs=[" << mess << "],";
1842       break;
1843     }
1844     case 0x190b: {
1845       ok = readCustomListLevel(outlineMod.m_entry, para.m_customListLevel);
1846       if (!ok) break;
1847       f << para.m_customListLevel << ",";
1848       break;
1849     }
1850     default:
1851       break;
1852     }
1853     // can be also pattern or backside or custom header
1854     if (!ok) {
1855       f << "[" << outlineMod << "]";
1856       if (!parseUnknown(outlineMod.m_entry, 0))
1857         f << "###";
1858     }
1859     ascFile.addPos(outlineMod.m_entry.begin());
1860     ascFile.addNote(f.str().c_str());
1861   }
1862   ascFile.addPos(endPos);
1863   ascFile.addNote("_");
1864   return true;
1865 }
1866 
1867 //////////////////////////////////////////////
1868 // small structure
1869 //////////////////////////////////////////////
readFont(MWAWEntry const & entry,std::string & fName,int & fId)1870 bool MoreText::readFont(MWAWEntry const &entry, std::string &fName, int &fId)
1871 {
1872   fName="";
1873   fId=-1;
1874   MWAWInputStreamPtr &input= m_parserState->m_input;
1875   if (entry.length() < 2 || !input->checkPosition(entry.end()))
1876     return false;
1877   long pos = entry.begin();
1878   input->seek(pos, librevenge::RVNG_SEEK_SET);
1879 
1880   auto fSz=static_cast<int>(input->readULong(1));
1881   long remain=entry.length()-long(1+fSz);
1882   if (fSz==0 || remain<0 || remain==1)
1883     return false;
1884   if (remain>=2 && remain!=2+(1-(fSz%2)))
1885     return false;
1886   for (int i=0; i < fSz; i++) {
1887     auto c=char(input->readULong(1));
1888     if (c==0) return false;
1889     fName+=c;
1890   }
1891   if (remain==0) { // let try to retrieve the font id
1892     fId=m_parserState->m_fontConverter->getId(fName);
1893     return true;
1894   }
1895   if ((fSz%2)==0) input->seek(1,librevenge::RVNG_SEEK_CUR);
1896   fId=static_cast<int>(input->readULong(2));
1897   return true;
1898 }
1899 
readCustomListLevel(MWAWEntry const & entry,MWAWListLevel & level)1900 bool MoreText::readCustomListLevel(MWAWEntry const &entry, MWAWListLevel &level)
1901 {
1902   level=MWAWListLevel();
1903   MWAWInputStreamPtr &input= m_parserState->m_input;
1904   if (entry.length()<22 || !input || !input->checkPosition(entry.end()))
1905     return false;
1906 
1907   libmwaw::DebugStream f;
1908   long pos = entry.begin();
1909   input->seek(pos, librevenge::RVNG_SEEK_SET);
1910   MWAWFont font;
1911   auto fId=static_cast<int>(input->readULong(2));
1912   if (fId==0xFFFF) // default
1913     ;
1914   else if (fId&0x8000) {
1915     f << "fId=" << (fId&0x7FFF) << ",";
1916     font.setId(fId&0x7FFF);
1917   }
1918   else
1919     f << "#fId=" << std::hex << fId << std::dec << ",";
1920   auto fSz = static_cast<int>(input->readLong(2));
1921   if (fSz != -1) {
1922     font.setSize(float(fSz));
1923     f << "fSz=" << fSz << ",";
1924   }
1925   auto fFlags=static_cast<int>(input->readULong(1));
1926   uint32_t flags=0;
1927   if (fFlags&1) flags |= MWAWFont::boldBit;
1928   if (fFlags&2) flags |= MWAWFont::italicBit;
1929   if (fFlags&4) font.setUnderlineStyle(MWAWFont::Line::Simple);
1930   if (fFlags&8) flags |= MWAWFont::outlineBit;
1931   if (fFlags&0x10) flags |= MWAWFont::shadowBit;
1932   if (fFlags&0xE0)
1933     f << "#fFlags=" << std::hex << (fFlags&0xE0) << std::dec << ",";
1934   font.setFlags(flags);
1935 
1936   auto fColor = static_cast<int>(input->readLong(1));
1937   if (fColor==1)
1938     input->seek(6, librevenge::RVNG_SEEK_CUR);
1939   else if (fColor==3) {
1940     unsigned char color[3];
1941     for (auto &c : color) c=static_cast<unsigned char>(input->readULong(2)>>8);
1942     font.setColor(MWAWColor(color[0], color[1], color[2]));
1943   }
1944   else {
1945     f << "#fCol=" << fColor << ",";
1946     input->seek(6, librevenge::RVNG_SEEK_CUR);
1947   }
1948 #if defined(DEBUG_WITH_FILES)
1949   f << "font=[" << font.getDebugString(m_parserState->m_fontConverter) << "],";
1950 #endif
1951 
1952   // now 4 bool
1953   bool bVal[4]= {false, false, false, false };
1954   long val;
1955   for (int i=0; i < 4; i++) {
1956     val = input->readLong(1);
1957     if (!val) continue;
1958     if (val!=1) {
1959       f << "#g" << i << "=" << val << ",";
1960       continue;
1961     }
1962     bVal[i]=true;
1963   }
1964   for (int i=0; i<2; i++) { // g1: field left ident modify?
1965     if (bVal[i])
1966       f << "g" << i << "=true,";
1967   }
1968   if (bVal[2]) // or flush left at
1969     f << "flushRight,";
1970   if (!bVal[3])
1971     f << "useFirstlineIdent,";
1972   val = input->readLong(2);
1973   if (val!=0x2d0) // default 0.5"
1974     f << "leftIdent=" << double(val)/1440. << ",";
1975   val = input->readLong(2);
1976   if (val != 0xb)
1977     f << "f6=" << val << ",";
1978   if (fId!=0xFFFF) { // maybe the font name
1979     auto fontSz=static_cast<int>(input->readULong(1));
1980     if (!fontSz || input->tell()+fontSz >= entry.end())
1981       input->seek(-1,librevenge::RVNG_SEEK_CUR);
1982     else {
1983       std::string fName("");
1984       for (int i=0; i<fontSz; i++)
1985         fName+=char(input->readULong(1));
1986       f << "fName=" << fName << ",";
1987       int newId=m_parserState->m_fontConverter->getId(fName);
1988       if (newId > 0)
1989         font.setId(fId=newId);
1990     }
1991   }
1992 
1993   auto labelSz = static_cast<int>(input->readULong(1));
1994   if (input->tell()+labelSz != entry.end())
1995     return false;
1996 
1997   f << "label=";
1998   if (fId == 0xFFFF)
1999     fId=3;
2000   for (int c=0; c < labelSz; c++) {
2001     auto ch=static_cast<unsigned char>(input->readULong(1));
2002     f << ch;
2003     int unicode = m_parserState->m_fontConverter->unicode(fId, static_cast<unsigned char>(ch));
2004     if (unicode!=-1)
2005       libmwaw::appendUnicode(uint32_t(unicode), level.m_label);
2006     else if (ch==0x9 || ch > 0x1f)
2007       libmwaw::appendUnicode(static_cast<uint32_t>(c), level.m_label);
2008     else {
2009       f << "##";
2010       MWAW_DEBUG_MSG(("MoreText::readCustomListLevel: label char seems bad\n"));
2011       libmwaw::appendUnicode('#', level.m_label);
2012     }
2013   }
2014   f << ",";
2015   level.m_type=MWAWListLevel::LABEL;
2016   level.m_extra=f.str();
2017   if (input->tell()!=entry.end())
2018     m_parserState->m_asciiFile.addDelimiter(input->tell(),'|');
2019   return true;
2020 }
2021 
readTabs(MWAWEntry const & entry,MoreTextInternal::Paragraph & para,std::string & mess)2022 bool MoreText::readTabs(MWAWEntry const &entry, MoreTextInternal::Paragraph &para,
2023                         std::string &mess)
2024 {
2025   mess="";
2026   if (entry.length() < 4)
2027     return false;
2028   MWAWInputStreamPtr &input= m_parserState->m_input;
2029   libmwaw::DebugStream f;
2030 
2031   long pos = entry.begin();
2032   input->seek(pos, librevenge::RVNG_SEEK_SET);
2033 
2034   auto nTabs=static_cast<int>(input->readULong(2));
2035   if (entry.length()!=4+4*nTabs)
2036     return false;
2037   auto repeat=static_cast<int>(input->readLong(2));
2038   if (uint16_t(repeat)==0x8000) // special case
2039     f << "def[center,right],";
2040   else
2041     f << "repeat=" << double(repeat)/1440. << ",";
2042   para.m_tabs->resize(0);
2043   for (int i=0; i < nTabs; i++) {
2044     libmwaw::DebugStream f2;
2045     MWAWTabStop tab;
2046     tab.m_position = double(input->readULong(2))/1440.;
2047     auto val=static_cast<int>(input->readULong(1));
2048     switch (val&0xF) {
2049     case 1: // left
2050       break;
2051     case 2:
2052       tab.m_alignment=MWAWTabStop::CENTER;
2053       break;
2054     case 3:
2055       tab.m_alignment=MWAWTabStop::RIGHT;
2056       break;
2057     case 4:
2058       tab.m_alignment=MWAWTabStop::DECIMAL;
2059       break;
2060     default:
2061       f2 << "#align=" << (val&0xF) << ",";
2062       break;
2063     }
2064     switch (val>>4) {
2065     case 0: // none
2066       break;
2067     case 1:
2068       tab.m_leaderCharacter = '_';
2069       break;
2070     case 3: // more large space
2071       f2 << "dot[large],";
2072       MWAW_FALLTHROUGH;
2073     case 2:
2074       tab.m_leaderCharacter = '.';
2075       break;
2076     default:
2077       f2 << "#leader=" << (val>>4) << ",";
2078       break;
2079     }
2080     auto decimalChar = char(input->readULong(1));
2081     if (decimalChar) {
2082       int unicode= m_parserState->m_fontConverter->unicode(3, static_cast<unsigned char>(decimalChar));
2083       if (unicode==-1)
2084         tab.m_decimalCharacter = uint16_t(decimalChar);
2085       else
2086         tab.m_decimalCharacter = uint16_t(unicode);
2087     }
2088     f << "tab" << i << "=[" << tab << "," << f2.str() << "],";
2089     para.m_tabs->push_back(tab);
2090   }
2091   mess=f.str();
2092   return true;
2093 }
2094 
2095 //////////////////////////////////////////////
2096 // unknown structure
2097 //////////////////////////////////////////////
parseUnknown(MWAWEntry const & entry,long fDecal)2098 bool MoreText::parseUnknown(MWAWEntry const &entry, long fDecal)
2099 {
2100   MWAWInputStreamPtr &input= m_parserState->m_input;
2101   libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
2102   libmwaw::DebugStream f;
2103 
2104   MoreStruct::Pattern pattern;
2105   long pos = entry.begin();
2106   input->seek(pos, librevenge::RVNG_SEEK_SET);
2107   if (m_mainParser->readPattern(entry.end(),pattern)) {
2108     f << pattern;
2109     if (input->tell()!=entry.end())
2110       ascFile.addDelimiter(input->tell(),'|');
2111     ascFile.addPos(pos+fDecal);
2112     ascFile.addNote(f.str().c_str());
2113     return true;
2114   }
2115   // can we find a backsidde here
2116   input->seek(pos, librevenge::RVNG_SEEK_SET);
2117   std::string extra("");
2118   if (m_mainParser->readBackside(entry.end(), extra)) {
2119     f << extra;
2120     if (input->tell()!=entry.end())
2121       ascFile.addDelimiter(input->tell(),'|');
2122     ascFile.addPos(pos+fDecal);
2123     ascFile.addNote(f.str().c_str());
2124     return true;
2125   }
2126 
2127   std::string mess;
2128   MoreTextInternal::Paragraph para;
2129   if (readTabs(entry, para, mess)) {
2130     f << "tabs=[" << mess << "],";
2131     ascFile.addPos(pos+fDecal);
2132     ascFile.addNote(f.str().c_str());
2133     return true;
2134   }
2135 
2136   std::string fName;
2137   int fId;
2138   if (readFont(entry, fName,fId)) {
2139     f << "font=[";
2140     f << "name=" << fName;
2141     if (fId>=0) f << ":" << fId;
2142     f << "],";
2143     ascFile.addPos(pos+fDecal);
2144     ascFile.addNote(f.str().c_str());
2145     return true;
2146   }
2147   return false;
2148 }
2149 
2150 // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
2151