1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * This file is part of the libetonyek project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include "IWAText.h"
11 
12 #include <memory>
13 
14 #include "IWORKLanguageManager.h"
15 #include "IWORKProperties.h"
16 #include "IWORKText.h"
17 
18 namespace libetonyek
19 {
20 
21 using boost::none;
22 
23 using std::make_shared;
24 using std::map;
25 using std::string;
26 
27 namespace
28 {
29 
flushText(string & text,IWORKText & collector)30 void flushText(string &text, IWORKText &collector)
31 {
32   if (!text.empty())
33   {
34     collector.insertText(text);
35     text.clear();
36   }
37 }
38 
39 }
40 
IWAText(const std::string & text,IWORKLanguageManager & langManager)41 IWAText::IWAText(const std::string &text, IWORKLanguageManager &langManager)
42   : m_text(text.c_str())
43   , m_langManager(langManager)
44   , m_pageMasters()
45   , m_sections()
46   , m_paras()
47   , m_spans()
48 
49   , m_langs()
50   , m_links()
51   , m_lists()
52   , m_listLevels()
53 
54   , m_attachments()
55 {
56 }
57 
setPageMasters(const std::map<unsigned,IWORKStylePtr_t> & pageMasters)58 void IWAText::setPageMasters(const std::map<unsigned, IWORKStylePtr_t> &pageMasters)
59 {
60   m_pageMasters = pageMasters;
61 }
62 
setSections(const std::map<unsigned,IWORKStylePtr_t> & sections)63 void IWAText::setSections(const std::map<unsigned, IWORKStylePtr_t> &sections)
64 {
65   m_sections = sections;
66 }
67 
setParagraphs(const std::map<unsigned,IWORKStylePtr_t> & paras)68 void IWAText::setParagraphs(const std::map<unsigned, IWORKStylePtr_t> &paras)
69 {
70   m_paras = paras;
71 }
72 
setSpans(const std::map<unsigned,IWORKStylePtr_t> & spans)73 void IWAText::setSpans(const std::map<unsigned, IWORKStylePtr_t> &spans)
74 {
75   m_spans = spans;
76 }
77 
setLanguages(const std::map<unsigned,std::string> & langs)78 void IWAText::setLanguages(const std::map<unsigned, std::string> &langs)
79 {
80   m_langs = langs;
81 }
82 
setLinks(const std::map<unsigned,std::string> & links)83 void IWAText::setLinks(const std::map<unsigned, std::string> &links)
84 {
85   m_links = links;
86 }
87 
setListLevels(const std::map<unsigned,unsigned> & levels)88 void IWAText::setListLevels(const std::map<unsigned, unsigned> &levels)
89 {
90   m_listLevels = levels;
91 }
92 
setLists(const std::map<unsigned,IWORKStylePtr_t> & lists)93 void IWAText::setLists(const std::map<unsigned, IWORKStylePtr_t> &lists)
94 {
95   m_lists = lists;
96 }
97 
setAttachments(const std::multimap<unsigned,std::function<void (unsigned,bool &)>> & attachments)98 void IWAText::setAttachments(const std::multimap<unsigned, std::function<void(unsigned, bool &)> > &attachments)
99 {
100   m_attachments = attachments;
101 }
102 
parse(IWORKText & collector,const std::function<void (unsigned,IWORKStylePtr_t)> & openPageSpan)103 void IWAText::parse(IWORKText &collector, const std::function<void(unsigned, IWORKStylePtr_t)> &openPageSpan)
104 {
105   auto pageMasterIt = m_pageMasters.begin();
106   auto sectionIt = m_sections.begin();
107   map<unsigned, IWORKStylePtr_t>::const_iterator paraIt = m_paras.begin();
108   map<unsigned, IWORKStylePtr_t>::const_iterator spanIt = m_spans.begin();
109   map<unsigned, string>::const_iterator langIt = m_langs.begin();
110   map<unsigned, string>::const_iterator linkIt = m_links.begin();
111   map<unsigned, IWORKStylePtr_t>::const_iterator listIt = m_lists.begin();
112   map<unsigned, unsigned>::const_iterator listLevelIt = m_listLevels.begin();
113   auto attachmentIt = m_attachments.begin();
114   bool wasSpace = false;
115   IWORKStylePtr_t currentListStyle;
116   bool isLink = false;
117   string curText;
118 
119   std::size_t pos = 0;
120   librevenge::RVNGString::Iter iter(m_text);
121   iter.rewind();
122   for (; iter.next(); ++pos)
123   {
124     // first the page master change
125     if ((pageMasterIt != m_pageMasters.end()) && (pageMasterIt->first == pos))
126     {
127       if (openPageSpan) openPageSpan(unsigned(pos), pageMasterIt->second);
128       ++pageMasterIt;
129     }
130     // first handle section change
131     if ((sectionIt != m_sections.end()) && (sectionIt->first == pos))
132     {
133       collector.flushLayout();
134       collector.setLayoutStyle(sectionIt->second);
135       ++sectionIt;
136     }
137 
138     // handle span style change
139     IWORKStylePtr_t spanStyle;
140     IWORKStylePtr_t langStyle;
141     bool spanChanged = false;
142     bool langChanged = false;
143     if ((spanIt != m_spans.end()) && (spanIt->first == pos))
144     {
145       spanStyle = spanIt->second;
146       spanChanged = true;
147       ++spanIt;
148     }
149     if ((langIt != m_langs.end()) && (langIt->first == pos))
150     {
151       IWORKPropertyMap props;
152       if (!langIt->second.empty())
153       {
154         const string &tag = m_langManager.addTag(langIt->second);
155         if (tag.empty())
156           props.clear<property::Language>();
157         else
158           props.put<property::Language>(tag);
159       }
160       else
161       {
162         props.clear<property::Language>();
163       }
164       langStyle = make_shared<IWORKStyle>(props, none, none);
165       langChanged = true;
166       ++langIt;
167     }
168     if (spanChanged || langChanged)
169     {
170       flushText(curText, collector);
171       if (pos != 0)
172         collector.flushSpan();
173       if (spanChanged)
174         collector.setSpanStyle(spanStyle);
175       if (langChanged)
176         collector.setLanguage(langStyle);
177     }
178     // handle start/end of a link
179     if ((linkIt != m_links.end()) && (linkIt->first == pos))
180     {
181       flushText(curText, collector);
182       if (isLink)
183       {
184         collector.closeLink();
185         isLink = false;
186       }
187       if (!linkIt->second.empty())
188       {
189         collector.openLink(linkIt->second);
190         isLink = true;
191       }
192       ++linkIt;
193     }
194 
195     // handle paragraph style change
196     if ((paraIt != m_paras.end()) && (paraIt->first == pos))
197     {
198       collector.setParagraphStyle(paraIt->second);
199       ++paraIt;
200     }
201 
202     // handle list style change
203     if ((listIt != m_lists.end()) && (listIt->first == pos))
204     {
205       currentListStyle = listIt->second;
206       collector.setListStyle(currentListStyle);
207       ++listIt;
208     }
209 
210     // handle list level change
211     if ((listLevelIt != m_listLevels.end()) && (listLevelIt->first == pos))
212     {
213       collector.setListLevel(listLevelIt->second + 1);
214       ++listLevelIt;
215     }
216 
217     bool ignoreCharacter=false;
218     while (attachmentIt != m_attachments.end() && attachmentIt->first == pos)
219     {
220       flushText(curText, collector);
221       attachmentIt->second(unsigned(pos), ignoreCharacter);
222       ++attachmentIt;
223     }
224     if (ignoreCharacter)
225       continue;
226     // handle text
227     const char *const u8Char = iter();
228     switch (u8Char[0])
229     {
230     case char(4): // new section(ok)
231     case char(14): // footnote: normally already ignored
232       break;
233     case char(5):
234     {
235       flushText(curText, collector);
236       collector.flushParagraph();
237       collector.insertPageBreak();
238       break;
239     }
240     case char(12): // column break
241       if (m_sections.empty()) break;
242       flushText(curText, collector);
243       collector.flushParagraph();
244       collector.insertColumnBreak();
245       break;
246     case '\t' :
247       flushText(curText, collector);
248       collector.insertTab();
249       break;
250     case '\r' :
251       flushText(curText, collector);
252       collector.insertLineBreak();
253       break;
254     case '\n' :
255       flushText(curText, collector);
256       collector.flushParagraph();
257       break;
258     case ' ' :
259       if (wasSpace)
260       {
261         flushText(curText, collector);
262         collector.insertSpace();
263       }
264       else
265         curText.push_back(' ');
266       break;
267     default:
268       if (unsigned(u8Char[0])<=0x1f)
269       {
270         ETONYEK_DEBUG_MSG(("IWAText::parse: find bad character %d\n", (int) unsigned(u8Char[0])));
271         break;
272       }
273       curText.append(u8Char);
274       break;
275     }
276     wasSpace=u8Char[0]==' ';
277   }
278   flushText(curText, collector);
279   collector.flushParagraph();
280   collector.setListLevel(0);
281   collector.flushList();
282   collector.flushLayout();
283 }
284 
285 }
286 
287 /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
288