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> §ions)
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> ¶s)
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