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> ¶Stack)
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 ¶ = 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 ¶ = 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 ¶,
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