1 /*********
2 *
3 * This file is part of BibleTime's source code, http://www.bibletime.info/.
4 *
5 * Copyright 1999-2016 by the BibleTime developers.
6 * The BibleTime source code is licensed under the GNU General Public License version 2.0.
7 *
8 **********/
9
10 #include "chtmlexportrendering.h"
11
12 #include <memory>
13 #include "../../util/btassert.h"
14 #include "../drivers/cswordmoduleinfo.h"
15 #include "../keys/cswordkey.h"
16 #include "../keys/cswordversekey.h"
17 #include "../managers/cdisplaytemplatemgr.h"
18 #include "../managers/clanguagemgr.h"
19
20
21 namespace Rendering {
22
CHTMLExportRendering(bool addText,DisplayOptions const & displayOptions,FilterOptions const & filterOptions)23 CHTMLExportRendering::CHTMLExportRendering(
24 bool addText,
25 DisplayOptions const & displayOptions,
26 FilterOptions const & filterOptions)
27 : m_displayOptions(displayOptions)
28 , m_filterOptions(filterOptions)
29 , m_addText(addText)
30 {}
31
renderEntry(KeyTreeItem const & i,CSwordKey * k)32 QString CHTMLExportRendering::renderEntry(KeyTreeItem const & i, CSwordKey * k)
33 {
34 if (i.hasAlternativeContent()) {
35 QString ret = i.settings().highlight
36 ? "<div class=\"currententry\">"
37 : "<div class=\"entry\">";
38 ret.append(i.getAlternativeContent());
39
40 if (!i.childList()->isEmpty()) {
41 KeyTree const & tree = *i.childList();
42
43 BtConstModuleList const modules(collectModules(tree));
44
45 if (modules.count() == 1)
46 // insert the direction into the surrounding div:
47 ret.insert(5,
48 QString("dir=\"%1\" ")
49 .arg(modules.first()->textDirectionAsHtml()));
50
51 Q_FOREACH (KeyTreeItem const * const item, tree)
52 ret.append(renderEntry(*item));
53 }
54
55 ret.append("</div>");
56 return ret; // WARNING: Return already here!
57 }
58
59
60 BtConstModuleList const & modules(i.modules());
61 if (modules.isEmpty())
62 return ""; // no module present for rendering
63
64 std::unique_ptr<CSwordKey> scoped_key(
65 !k ? CSwordKey::createInstance(modules.first()) : nullptr);
66 CSwordKey * const key = k ? k : scoped_key.get();
67 BT_ASSERT(key);
68
69 CSwordVerseKey * const myVK = dynamic_cast<CSwordVerseKey *>(key);
70 if (myVK)
71 myVK->setIntros(true);
72
73 QString renderedText((modules.count() > 1) ? "\n\t\t<tr>\n" : "\n");
74 // Only insert the table stuff if we are displaying parallel.
75
76 //declarations out of the loop for optimization
77 QString entry;
78 bool isRTL;
79 QString preverseHeading;
80 QString langAttr;
81 QString key_renderedText;
82
83 BtConstModuleList::const_iterator end_modItr = modules.end();
84
85 for (BtConstModuleList::const_iterator mod_Itr(modules.begin()); mod_Itr != end_modItr; ++mod_Itr) {
86 if (myVK) {
87 key->setModule(*modules.begin());
88 key->setKey(i.key());
89
90 // this would change key position due to v11n translation
91 key->setModule(*mod_Itr);
92 } else {
93 key->setModule(*mod_Itr);
94 key->setKey(i.key());
95 }
96
97 // indicate that key was changed
98 i.setMappedKey(key->key() != i.key() ? key : nullptr);
99
100
101 isRTL = ((*mod_Itr)->textDirection() == CSwordModuleInfo::RightToLeft);
102 entry = QString::null;
103
104 auto & swModule = (*mod_Itr)->module();
105 if ((*mod_Itr)->language()->isValid()) {
106 langAttr = QString("xml:lang=\"")
107 .append((*mod_Itr)->language()->abbrev())
108 .append("\" lang=\"")
109 .append((*mod_Itr)->language()->abbrev())
110 .append("\"");
111 } else {
112 langAttr = QString("xml:lang=\"")
113 .append(swModule.getLanguage())
114 .append("\" lang=\"")
115 .append(swModule.getLanguage())
116 .append("\"");
117 }
118
119 if (key->isValid() && i.key() == key->key()) {
120 key_renderedText = key->renderedText();
121
122 // if key was expanded
123 if (CSwordVerseKey const * const vk =
124 dynamic_cast<CSwordVerseKey *>(key))
125 {
126 if (vk->isBoundSet()) {
127 CSwordVerseKey pk(*vk);
128 for (int i = vk->getLowerBound().getIndex() + 1;
129 i <= vk->getUpperBound().getIndex();
130 ++i)
131 {
132 key_renderedText += " ";
133 pk.setIndex(i);
134 key_renderedText += pk.renderedText();
135 }
136 }
137 }
138 } else {
139 key_renderedText = "<span class=\"inactive\">—</span>";
140 }
141
142 if (m_filterOptions.headings && key->isValid() && i.key() == key->key()) {
143
144 // only process EntryAttributes, do not render, this might destroy the EntryAttributes again
145 swModule.renderText(nullptr, -1, 0);
146
147 sword::AttributeValue::const_iterator it =
148 swModule.getEntryAttributes()["Heading"]["Preverse"].begin();
149 sword::AttributeValue::const_iterator const end =
150 swModule.getEntryAttributes()["Heading"]["Preverse"].end();
151
152 for (; it != end; ++it) {
153 QString unfiltered(QString::fromUtf8(it->second.c_str()));
154
155 /// \todo This is only a preliminary workaround to strip the tags:
156 {
157 static QRegExp const staticFilter(
158 "(.*)<title[^>]*>(.*)</title>(.*)");
159 QRegExp filter(staticFilter);
160 while (filter.indexIn(unfiltered) >= 0)
161 unfiltered = filter.cap(1) + filter.cap(2) + filter.cap(3);
162 }
163
164 // Filter out offending self-closing div tags, which are bad HTML
165 {
166 static QRegExp const staticFilter("(.*)<div[^>]*/>(.*)");
167 QRegExp filter(staticFilter);
168 while (filter.indexIn(unfiltered) >= 0)
169 unfiltered = filter.cap(1) + filter.cap(2);
170 }
171
172 preverseHeading = unfiltered;
173
174 /// \todo Take care of the heading type!
175 if (!preverseHeading.isEmpty()) {
176 entry.append("<div ")
177 .append(langAttr)
178 .append(" class=\"sectiontitle\">")
179 .append(preverseHeading)
180 .append("</div>");
181 }
182 }
183 }
184
185 entry.append(m_displayOptions.lineBreaks ? "<div class=\"" : "<div class=\"inline ");
186
187 if (modules.count() == 1) //insert only the class if we're not in a td
188 entry.append( i.settings().highlight ? "currententry " : "entry " );
189 entry.append("\"");
190 entry.append(langAttr).append(isRTL ? " dir=\"rtl\">" : " dir=\"ltr\">");
191
192 //keys should normally be left-to-right, but this doesn't apply in all cases
193 if(key->isValid() && i.key() == key->key())
194 entry.append("<span class=\"entryname\" dir=\"ltr\">").append(entryLink(i, *mod_Itr)).append("</span>");
195
196 if (m_addText)
197 entry.append(key_renderedText);
198
199 if (!i.childList()->isEmpty())
200 Q_FOREACH (KeyTreeItem const * const c, *(i.childList()))
201 entry.append(renderEntry(*c));
202
203 entry.append("</div>");
204
205 if (modules.count() == 1) {
206 renderedText.append("\t\t").append(entry).append("\n");
207 } else {
208 renderedText.append("\t\t<td class=\"")
209 .append(i.settings().highlight ? "currententry" : "entry")
210 .append("\" ")
211 .append(langAttr)
212 .append(" dir=\"")
213 .append(isRTL ? "rtl" : "ltr")
214 .append("\">\n")
215 .append( "\t\t\t" ).append( entry ).append("\n")
216 .append("\t\t</td>\n");
217 }
218 }
219
220 if (modules.count() > 1)
221 renderedText.append("\t\t</tr>\n");
222
223 // qDebug("CHTMLExportRendering: %s", renderedText.latin1());
224 return renderedText;
225 }
226
initRendering()227 void CHTMLExportRendering::initRendering() {
228 //CSwordBackend::instance()()->setDisplayOptions( m_displayOptions );
229 CSwordBackend::instance()->setFilterOptions(m_filterOptions);
230 }
231
finishText(QString const & text,KeyTree const & tree)232 QString CHTMLExportRendering::finishText(QString const & text,
233 KeyTree const & tree)
234 {
235 CDisplayTemplateMgr::Settings settings;
236 settings.modules = collectModules(tree);
237 if (settings.modules.count() == 1) {
238 CSwordModuleInfo const * const firstModule = settings.modules.first();
239 CLanguageMgr::Language const * const lang = firstModule->language();
240 settings.langAbbrev = lang->isValid() ? lang->abbrev() : "unknown";
241 settings.textDirection = firstModule->textDirection();
242 } else {
243 settings.langAbbrev = "unknown";
244 }
245
246 return CDisplayTemplateMgr::instance()->fillTemplate(
247 CDisplayTemplateMgr::activeTemplateName(),
248 text,
249 settings);
250 }
251
252 /*!
253 \fn CHTMLExportRendering::entryLink( KeyTreeItem& item )
254 */
entryLink(KeyTreeItem const & item,CSwordModuleInfo const *)255 QString CHTMLExportRendering::entryLink(KeyTreeItem const & item,
256 CSwordModuleInfo const *)
257 { return item.key(); }
258
259 }//end of namespace "Rendering"
260