15#include <QRegularExpression>
16#include <QRegularExpressionMatch>
19#include "../../util/btassert.h"
20#include "../config/btconfig.h"
21#include "../drivers/cswordmoduleinfo.h"
22#include "../managers/cswordbackend.h"
23#include "../managers/referencemanager.h"
27#pragma GCC diagnostic push
28#pragma GCC diagnostic ignored "-Wextra-semi"
29#pragma GCC diagnostic ignored "-Wsuggest-override"
30#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
33#pragma clang diagnostic push
34#pragma clang diagnostic ignored "-Wsuggest-destructor-override"
41#pragma clang diagnostic pop
44#pragma GCC diagnostic pop
51 setEscapeStringCaseSensitive(
true);
52 setPassThruUnknownEscapeString(
true);
56 setTokenCaseSensitive(
true);
58 addTokenSubstitute(
"/foreign",
"</span>");
60 removeTokenSubstitute(
"note");
61 removeTokenSubstitute(
"/note");
65 const sword::SWModule *module)
67 sword::ThMLHTML::processText(buf, key, module);
80 auto t = QString::fromUtf8(buf.c_str());
82 static QRegularExpression
const tag(
83 QStringLiteral(R
"PCRE(([.,;]?<sync[^>]+(type|value)=)PCRE"
84 R"PCRE("([^"]+)"[^>]+(type|value)=)PCRE"
85 R"PCRE("([^"]+)"([^<]*)>)+)PCRE"));
86 QRegularExpressionMatch match;
87 auto pos = t.indexOf(tag, 0, &match);
91 auto const partLength = pos + match.capturedLength();
92 list.append(t.left(partLength));
93 t.remove(0, partLength);
94 pos = t.indexOf(tag, 0, &match);
100 list.append(std::move(t));
103 static QRegularExpression
const tag(
104 QStringLiteral(R
"PCRE(<sync[^>]+(type|value|class)="([^"]+)"[^>]+)PCRE"
105 R"PCRE((type|value|class)="([^"]+)"[^>]+)PCRE"
106 R"PCRE(((type|value|class)="([^"]+)")*([^<]*)>)PCRE"));
108 for (
auto & e : list) {
111 if (
auto const pos = e.indexOf(tag); pos > 0) {
112 result.append(e.left(pos));
117 bool hasLemmaAttr =
false;
118 bool hasMorphAttr =
false;
120 QRegularExpressionMatch match;
121 auto pos = e.indexOf(tag, 0, &match);
122 bool insertedTag =
false;
125 bool isMorph =
false;
126 bool isStrongs =
false;
132 for (
int i = 1; i < 6; i += 2) {
136 auto const attrName = match.captured(i);
137 auto const attrValue = match.captured(i + 1);
138 if (attrName == QStringLiteral(
"type")) {
139 isMorph = (attrValue == QStringLiteral(
"morph"));
140 isStrongs = (attrValue == QStringLiteral(
"Strongs"));
141 }
else if (attrName == QStringLiteral(
"value")) {
143 }
else if (attrName == QStringLiteral(
"class")) {
144 valueClass = attrValue;
151 if (!valueClass.isEmpty())
152 value = QStringLiteral(
"%1:%2").arg(valueClass, value);
154 if (value.isEmpty()) {
160 e.replace(pos, match.capturedLength(), QStringLiteral(
"</span>"));
163 auto rep = QStringLiteral(
"<span lemma=\"%1\">").arg(value);
165 QChar c = e[startPos];
167 while ((startPos < pos) && (c.isSpace() || c.isPunct())) {
172 hasLemmaAttr = isStrongs;
173 hasMorphAttr = isMorph;
176 e.insert(startPos, std::move(rep));
179 e.remove(pos, match.capturedLength());
181 if ((!isMorph && hasLemmaAttr) || (isMorph && hasMorphAttr)) {
183 auto const & attrRegExp =
186 static QRegularExpression
const re(
187 QStringLiteral(
"morph=\".+?(?=\")"));
190 static QRegularExpression
const re(
191 QStringLiteral(
"lemma=\".+?(?=\")"));
195 QRegularExpressionMatch match;
196 const int foundAttrPos = e.indexOf(attrRegExp, pos, &match);
198 if (foundAttrPos != -1) {
199 e.insert(foundAttrPos + match.capturedLength(),
200 QStringLiteral(
"|%1").arg(value));
201 pos += value.length() + 1;
203 hasLemmaAttr = !isMorph;
204 hasMorphAttr = isMorph;
208 static QRegularExpression
const re(
209 QStringLiteral(
"morph=|lemma="));
210 const int attrPos = e.indexOf(re, 0);
213 hasMorphAttr = isMorph;
214 hasLemmaAttr = !isMorph;
216 auto attr = QStringLiteral(
"%1=\"%2\" ")
218 ? QStringLiteral(
"morph")
219 : QStringLiteral(
"lemma"),
221 pos += attr.length();
222 e.insert(attrPos, std::move(attr));
228 pos = e.indexOf(tag, pos, &match);
231 result.append(std::move(e));
235 buf = result.toUtf8();
242 sword::BasicFilterUserData *userData)
244 if (!substituteToken(buf, token) && !substituteEscapeString(buf, token)) {
245 sword::XMLTag
const tag(token);
246 BT_ASSERT(
dynamic_cast<UserData *
>(userData));
247 UserData *
const myUserData =
static_cast<UserData *
>(userData);
249 sword::SWModule
const *
const myModule =
250 const_cast<sword::SWModule *
>(myUserData->module);
251 char const *
const tagName = tag.getName();
253 return sword::ThMLHTML::handleToken(buf, token, userData);
254 if (!sword::stricmp(tagName,
"foreign")) {
257 if (
const char *
const tagLang = tag.getAttribute(
"lang"))
258 buf.append(
"<span class=\"foreign\" lang=\"")
261 }
else if (!sword::stricmp(tagName,
"sync")) {
263 if (
const char *
const tagType = tag.getAttribute(
"type"))
264 if (!sword::stricmp(tagType,
"morph")
265 || !sword::stricmp(tagType,
"Strongs")
266 || !sword::stricmp(tagType,
"lemma"))
267 buf.append(
'<').append(token).append(
'>');
268 }
else if (!sword::stricmp(tagName,
"note")) {
269 if (!tag.isEmpty()) {
270 if (!tag.isEndTag()) {
271 buf.append(
" <span class=\"footnote\" note=\"")
272 .append(myModule->getName())
274 .append(myUserData->key->getShortText())
276 .append(QString::number(myUserData->swordFootnote).toUtf8().constData())
277 .append(
"\">*</span> ");
279 myUserData->swordFootnote++;
280 myUserData->suspendTextPassThru =
true;
281 myUserData->inFootnoteTag =
true;
282 }
else if (tag.isEndTag()) {
284 myUserData->suspendTextPassThru =
false;
285 myUserData->inFootnoteTag =
false;
288 }
else if (!sword::stricmp(tagName,
"scripRef")) {
291 if (!myUserData->inFootnoteTag) {
292 if (tag.isEndTag()) {
293 if (myUserData->inscriptRef) {
294 buf.append(
"</a></span>");
296 myUserData->inscriptRef =
false;
297 myUserData->suspendTextPassThru =
false;
300 btConfig().getDefaultSwordModuleByType(
306 QString::fromUtf8(myUserData->key->getText()),
307 myModule->getLanguage()};
310 bool insertSemicolon =
false;
311 buf.append(
"<span class=\"crossreference\">");
312 QStringList
const refs(
314 myUserData->lastTextNode.c_str()).split(
317 for (
auto const & ref : refs) {
318 if (!oldRef.isEmpty())
319 options.refBase = oldRef;
330 buf.append(
"<a href=\"")
335 ).toUtf8().constData()
337 .append(
"\" crossrefs=\"")
338 .append(oldRef.toUtf8().constData())
340 .append(ref.toUtf8().constData())
342 insertSemicolon =
true;
344 buf.append(
"</span>");
346 myUserData->suspendTextPassThru =
false;
348 }
else if (tag.getAttribute(
"passage") ) {
350 myUserData->inscriptRef =
true;
351 myUserData->suspendTextPassThru =
false;
354 QStringLiteral(
"standardBible"));
360 QString
const completeRef(
363 tag.getAttribute(
"passage")),
367 myUserData->key->getText()),
368 myModule->getLanguage()}));
369 buf.append(
"<span class=\"crossreference\">")
370 .append(
"<a href=\"")
375 ).toUtf8().constData()
377 .append(
"\" crossrefs=\"")
378 .append(completeRef.toUtf8().constData())
381 buf.append(
"<span><a>");
384 myUserData->inscriptRef =
false;
387 myUserData->suspendTextPassThru =
true;
390 }
else if (!sword::stricmp(tagName,
"div")) {
391 if (tag.isEndTag()) {
392 buf.append(
"</div>");
393 }
else if (
char const *
const tagClass = tag.getAttribute(
"class")){
394 if (!sword::stricmp(tagClass,
"sechead") ) {
395 buf.append(
"<div class=\"sectiontitle\">");
396 }
else if (!sword::stricmp(tagClass,
"title")) {
397 buf.append(
"<div class=\"booktitle\">");
400 }
else if (!sword::stricmp(tagName,
"img") && tag.getAttribute(
"src")) {
401 const char * value = tag.getAttribute(
"src");
406 if (!myUserData->absolutePath.has_value()) {
407 auto const *
const absoluteDataPath =
408 myUserData->module->getConfigEntry(
"AbsoluteDataPath");
409 myUserData->absolutePath.emplace(
410 myUserData->module->isUnicode()
411 ? QString::fromUtf8(absoluteDataPath)
412 : QString::fromLatin1(absoluteDataPath));
415 buf.append(
"<img src=\"")
418 QStringLiteral(
"%1/%2").arg(
419 *myUserData->absolutePath,
420 QString::fromUtf8(value))
421 ).toString().toUtf8().constData())
424 return sword::ThMLHTML::handleToken(buf, token, userData);
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
CSwordModuleInfo * getDefaultSwordModuleByType(const QString &moduleType)
Returns default sword module info class for a given module type.
CSwordModuleInfo * findFirstAvailableModule(CSwordModuleInfo::ModuleType type)
static CSwordBackend & instance() noexcept
static FilterOption const strongNumbers
static FilterOption const lemmas
bool handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) override
char processText(sword::SWBuf &buf, const sword::SWKey *key, const sword::SWModule *module=nullptr) override
QString parseVerseReference(QString const &ref, ParseOptions const &options)
QString encodeHyperlink(CSwordModuleInfo const &module, QString const &key)