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\">&#8212;</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