vwebutils.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. #include "vwebutils.h"
  2. #include <QFileInfo>
  3. #include <QObject>
  4. #include <QDebug>
  5. #include "vpalette.h"
  6. #include "vconfigmanager.h"
  7. extern VPalette *g_palette;
  8. extern VConfigManager *g_config;
  9. VWebUtils::VWebUtils()
  10. {
  11. }
  12. void VWebUtils::init()
  13. {
  14. m_stylesToRemoveWhenCopied = g_config->getStylesToRemoveWhenCopied();
  15. m_styleOfSpanForMark = g_config->getStyleOfSpanForMark();
  16. m_tagReg = QRegExp("<([^>/\\s]+)([^>]*)>");
  17. m_styleTagReg = QRegExp("<([^>\\s]+)([^>]*\\s)style=\"([^\">]+)\"([^>]*)>");
  18. m_imgTagReg = QRegExp("<img src=\"([^\"]+)\"[^>]*>");
  19. initCopyTargets(g_config->getCopyTargets());
  20. }
  21. void VWebUtils::initCopyTargets(const QStringList &p_str)
  22. {
  23. Q_ASSERT(m_copyTargets.isEmpty());
  24. // cap(1): action;
  25. // cap(3): arguments;
  26. QRegExp actReg("([0-9a-zA-Z])(\\(([^\\)]*)\\))?");
  27. for (auto const & str : p_str) {
  28. auto vals = str.split('$');
  29. if (vals.size() != 2) {
  30. continue;
  31. }
  32. CopyTarget tar;
  33. tar.m_name = vals[0];
  34. if (tar.m_name.isEmpty()) {
  35. continue;
  36. }
  37. auto acts = vals[1].split(':');
  38. for (auto const & it : acts) {
  39. if (it.isEmpty()) {
  40. continue;
  41. }
  42. if (!actReg.exactMatch(it)) {
  43. continue;
  44. }
  45. if (actReg.cap(1).size() != 1) {
  46. continue;
  47. }
  48. CopyTargetAction act;
  49. act.m_act = actReg.cap(1)[0];
  50. if (!actReg.cap(3).isEmpty()) {
  51. act.m_args = actReg.cap(3).toLower().split('|');
  52. }
  53. tar.m_actions.append(act);
  54. }
  55. m_copyTargets.append(tar);
  56. }
  57. qDebug() << "init" << m_copyTargets.size() << "copy targets";
  58. }
  59. bool VWebUtils::fixImageSrc(const QUrl &p_baseUrl, QString &p_html)
  60. {
  61. bool changed = false;
  62. #if defined(Q_OS_WIN)
  63. QUrl::ComponentFormattingOption strOpt = QUrl::EncodeSpaces;
  64. #else
  65. QUrl::ComponentFormattingOption strOpt = QUrl::FullyEncoded;
  66. #endif
  67. QRegExp reg("<img src=\"([^\"]+)\"");
  68. int pos = 0;
  69. while (pos < p_html.size()) {
  70. int idx = p_html.indexOf(reg, pos);
  71. if (idx == -1) {
  72. break;
  73. }
  74. QString urlStr = reg.cap(1);
  75. QUrl imgUrl(urlStr);
  76. QString fixedStr;
  77. if (imgUrl.isRelative()) {
  78. fixedStr = p_baseUrl.resolved(imgUrl).toString(strOpt);
  79. } else if (imgUrl.isLocalFile()) {
  80. fixedStr = imgUrl.toString(strOpt);
  81. } else if (imgUrl.scheme() != "https" && imgUrl.scheme() != "http") {
  82. QString tmp = imgUrl.toString();
  83. if (QFileInfo::exists(tmp)) {
  84. fixedStr = QUrl::fromLocalFile(tmp).toString(strOpt);
  85. }
  86. }
  87. pos = idx + reg.matchedLength();
  88. if (!fixedStr.isEmpty() && urlStr != fixedStr) {
  89. qDebug() << "fix img url" << urlStr << fixedStr;
  90. pos = pos + fixedStr.size() + 1 - urlStr.size();
  91. p_html.replace(idx,
  92. reg.matchedLength(),
  93. QString("<img src=\"%1\"").arg(fixedStr));
  94. changed = true;
  95. }
  96. }
  97. return changed;
  98. }
  99. QStringList VWebUtils::getCopyTargetsName() const
  100. {
  101. QStringList names;
  102. for (auto const & it : m_copyTargets) {
  103. names << it.m_name;
  104. }
  105. return names;
  106. }
  107. bool VWebUtils::alterHtmlAsTarget(const QUrl &p_baseUrl, QString &p_html, const QString &p_target) const
  108. {
  109. int idx = targetIndex(p_target);
  110. if (idx == -1) {
  111. return false;
  112. }
  113. bool altered = false;
  114. for (auto const & act : m_copyTargets[idx].m_actions) {
  115. if (const_cast<VWebUtils *>(this)->alterHtmlByTargetAction(p_baseUrl, p_html, act)) {
  116. altered = true;
  117. }
  118. }
  119. if (altered) {
  120. qDebug() << "==html==";
  121. qDebug() << p_html;
  122. }
  123. return altered;
  124. }
  125. int VWebUtils::targetIndex(const QString &p_target) const
  126. {
  127. if (p_target.isEmpty()) {
  128. return -1;
  129. }
  130. for (int i = 0; i < m_copyTargets.size(); ++i) {
  131. if (m_copyTargets[i].m_name == p_target) {
  132. return i;
  133. }
  134. }
  135. return -1;
  136. }
  137. bool VWebUtils::alterHtmlByTargetAction(const QUrl &p_baseUrl, QString &p_html, const CopyTargetAction &p_action)
  138. {
  139. bool altered = false;
  140. switch (p_action.m_act.toLatin1()) {
  141. case 's':
  142. if (!p_html.startsWith("<html>")) {
  143. p_html = "<html><body>" + p_html + "</body></html>";
  144. altered = true;
  145. }
  146. break;
  147. case 'e':
  148. if (!p_html.startsWith("<html>")) {
  149. p_html = "<html><body><!--StartFragment-->" + p_html + "<!--EndFragment--></body></html>";
  150. altered = true;
  151. }
  152. break;
  153. case 'b':
  154. altered = removeBackgroundColor(p_html, p_action.m_args);
  155. break;
  156. case 'c':
  157. altered = translateColors(p_html, p_action.m_args);
  158. break;
  159. case 'i':
  160. altered = fixImageSrc(p_baseUrl, p_html);
  161. break;
  162. case 'm':
  163. altered = removeMarginPadding(p_html, p_action.m_args);
  164. break;
  165. case 'x':
  166. altered = removeStylesToRemoveWhenCopied(p_html, p_action.m_args);
  167. break;
  168. case 'r':
  169. altered = removeAllStyles(p_html, p_action.m_args);
  170. break;
  171. case 'a':
  172. altered = transformMarkToSpan(p_html);
  173. break;
  174. case 'p':
  175. altered = replacePreBackgroundColorWithCode(p_html);
  176. break;
  177. case 'n':
  178. altered = replaceNewLineWithBR(p_html);
  179. break;
  180. case 'g':
  181. altered = replaceLocalImgWithWarningLabel(p_html);
  182. break;
  183. case 'd':
  184. altered = addSpanInsideCode(p_html);
  185. break;
  186. case 'f':
  187. altered = replaceQuoteInFontFamily(p_html);
  188. break;
  189. case 'h':
  190. altered = replaceHeadingWithSpan(p_html);
  191. break;
  192. case 'j':
  193. altered = fixXHtmlTags(p_html);
  194. break;
  195. default:
  196. break;
  197. }
  198. return altered;
  199. }
  200. static int skipToTagEnd(const QString &p_html,
  201. int p_pos,
  202. const QString &p_tag,
  203. int *p_endTagIdx = NULL)
  204. {
  205. QRegExp beginReg(QString("<%1 ").arg(p_tag));
  206. QRegExp endReg(QString("</%1>").arg(p_tag));
  207. int pos = p_pos;
  208. int nBegin = p_html.indexOf(beginReg, pos);
  209. int nEnd = p_html.indexOf(endReg, pos);
  210. if (nBegin > -1 && nBegin < nEnd) {
  211. // Nested tag.
  212. pos = skipToTagEnd(p_html, nBegin + beginReg.matchedLength(), p_tag);
  213. nEnd = p_html.indexOf(endReg, pos);
  214. }
  215. if (nEnd > -1) {
  216. if (p_endTagIdx) {
  217. *p_endTagIdx = nEnd;
  218. }
  219. pos = nEnd + endReg.matchedLength();
  220. } else if (p_endTagIdx) {
  221. *p_endTagIdx = -1;
  222. }
  223. return pos;
  224. }
  225. // @p_html is the style string.
  226. static bool removeStylesInStyleString(QString &p_html, const QStringList &p_styles)
  227. {
  228. if (p_styles.isEmpty()) {
  229. return false;
  230. }
  231. int size = p_html.size();
  232. QRegExp reg(QString("(\\s|^)(%1):[^:]+;").arg(p_styles.join('|')));
  233. p_html.remove(reg);
  234. return size != p_html.size();
  235. }
  236. bool VWebUtils::removeBackgroundColor(QString &p_html, const QStringList &p_skipTags)
  237. {
  238. QStringList styles({"background", "background-color"});
  239. return removeStyles(p_html, p_skipTags, styles);
  240. }
  241. bool VWebUtils::translateColors(QString &p_html, const QStringList &p_skipTags)
  242. {
  243. bool changed = false;
  244. const QHash<QString, QString> &mapping = g_palette->getColorMapping();
  245. if (mapping.isEmpty()) {
  246. return changed;
  247. }
  248. // Won't mixed up with background-color.
  249. QRegExp colorReg("(\\s|^)color:([^;]+);");
  250. int pos = 0;
  251. while (pos < p_html.size()) {
  252. int tagIdx = p_html.indexOf(m_tagReg, pos);
  253. if (tagIdx == -1) {
  254. break;
  255. }
  256. QString tagName = m_tagReg.cap(1);
  257. if (p_skipTags.contains(tagName.toLower())) {
  258. // Skip this tag.
  259. pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
  260. continue;
  261. }
  262. pos = tagIdx;
  263. int idx = p_html.indexOf(m_styleTagReg, pos);
  264. if (idx == -1) {
  265. break;
  266. } else if (idx != tagIdx) {
  267. pos = tagIdx + m_tagReg.matchedLength();
  268. continue;
  269. }
  270. QString styleStr = m_styleTagReg.cap(3);
  271. QString alteredStyleStr = styleStr;
  272. int posb = 0;
  273. while (posb < alteredStyleStr.size()) {
  274. int idxb = alteredStyleStr.indexOf(colorReg, posb);
  275. if (idxb == -1) {
  276. break;
  277. }
  278. QString col = colorReg.cap(2).trimmed().toLower();
  279. auto it = mapping.find(col);
  280. if (it == mapping.end()) {
  281. posb = idxb + colorReg.matchedLength();
  282. continue;
  283. }
  284. // Replace the color.
  285. QString newCol = it.value();
  286. // Should not add extra space before :.
  287. QString newStr = QString("%1color: %2;").arg(colorReg.cap(1)).arg(newCol);
  288. alteredStyleStr.replace(idxb, colorReg.matchedLength(), newStr);
  289. posb = idxb + newStr.size();
  290. changed = true;
  291. }
  292. if (changed) {
  293. QString newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
  294. .arg(m_styleTagReg.cap(2))
  295. .arg(alteredStyleStr)
  296. .arg(m_styleTagReg.cap(4));
  297. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  298. pos = idx + newTag.size();
  299. } else {
  300. pos = idx + m_styleTagReg.matchedLength();
  301. }
  302. }
  303. return changed;
  304. }
  305. bool VWebUtils::removeMarginPadding(QString &p_html, const QStringList &p_skipTags)
  306. {
  307. QStringList styles({"margin", "margin-left", "margin-right",
  308. "padding", "padding-left", "padding-right"});
  309. return removeStyles(p_html, p_skipTags, styles);
  310. }
  311. bool VWebUtils::removeStyles(QString &p_html, const QStringList &p_skipTags, const QStringList &p_styles)
  312. {
  313. if (p_styles.isEmpty()) {
  314. return false;
  315. }
  316. bool altered = false;
  317. int pos = 0;
  318. while (pos < p_html.size()) {
  319. int tagIdx = p_html.indexOf(m_tagReg, pos);
  320. if (tagIdx == -1) {
  321. break;
  322. }
  323. QString tagName = m_tagReg.cap(1);
  324. if (p_skipTags.contains(tagName.toLower())) {
  325. // Skip this tag.
  326. pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
  327. continue;
  328. }
  329. pos = tagIdx;
  330. int idx = p_html.indexOf(m_styleTagReg, pos);
  331. if (idx == -1) {
  332. break;
  333. } else if (idx != tagIdx) {
  334. pos = tagIdx + m_tagReg.matchedLength();
  335. continue;
  336. }
  337. QString styleStr = m_styleTagReg.cap(3);
  338. if (removeStylesInStyleString(styleStr, p_styles)) {
  339. QString newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
  340. .arg(m_styleTagReg.cap(2))
  341. .arg(styleStr)
  342. .arg(m_styleTagReg.cap(4));
  343. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  344. pos = idx + newTag.size();
  345. altered = true;
  346. } else {
  347. pos = idx + m_styleTagReg.matchedLength();
  348. }
  349. }
  350. return altered;
  351. }
  352. bool VWebUtils::removeStylesToRemoveWhenCopied(QString &p_html, const QStringList &p_skipTags)
  353. {
  354. return removeStyles(p_html, p_skipTags, m_stylesToRemoveWhenCopied);
  355. }
  356. bool VWebUtils::removeAllStyles(QString &p_html, const QStringList &p_skipTags)
  357. {
  358. bool altered = false;
  359. int pos = 0;
  360. while (pos < p_html.size()) {
  361. int tagIdx = p_html.indexOf(m_tagReg, pos);
  362. if (tagIdx == -1) {
  363. break;
  364. }
  365. QString tagName = m_tagReg.cap(1);
  366. if (p_skipTags.contains(tagName.toLower())) {
  367. // Skip this tag.
  368. pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
  369. continue;
  370. }
  371. pos = tagIdx;
  372. int idx = p_html.indexOf(m_styleTagReg, pos);
  373. if (idx == -1) {
  374. break;
  375. } else if (idx != tagIdx) {
  376. pos = tagIdx + m_tagReg.matchedLength();
  377. continue;
  378. }
  379. QString newTag = QString("<%1%2%3>").arg(m_styleTagReg.cap(1))
  380. .arg(m_styleTagReg.cap(2))
  381. .arg(m_styleTagReg.cap(4));
  382. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  383. pos = idx + newTag.size();
  384. altered = true;
  385. }
  386. return altered;
  387. }
  388. bool VWebUtils::transformMarkToSpan(QString &p_html)
  389. {
  390. bool altered = false;
  391. int pos = 0;
  392. while (pos < p_html.size()) {
  393. int tagIdx = p_html.indexOf(m_tagReg, pos);
  394. if (tagIdx == -1) {
  395. break;
  396. }
  397. QString tagName = m_tagReg.cap(1);
  398. if (tagName.toLower() != "mark") {
  399. pos = tagIdx + m_tagReg.matchedLength();
  400. continue;
  401. }
  402. pos = tagIdx;
  403. int idx = p_html.indexOf(m_styleTagReg, pos);
  404. if (idx == -1 || idx != tagIdx) {
  405. // <mark> without "style".
  406. QString newTag = QString("<span style=\"%1\" %2>").arg(m_styleOfSpanForMark)
  407. .arg(m_tagReg.cap(2));
  408. p_html.replace(tagIdx, m_tagReg.matchedLength(), newTag);
  409. pos = tagIdx + newTag.size();
  410. altered = true;
  411. continue;
  412. }
  413. QString newTag = QString("<span%1style=\"%2\"%3>").arg(m_styleTagReg.cap(2))
  414. .arg(m_styleTagReg.cap(3) + m_styleOfSpanForMark)
  415. .arg(m_styleTagReg.cap(4));
  416. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  417. pos = idx + newTag.size();
  418. altered = true;
  419. }
  420. if (altered) {
  421. // Replace all </mark> with </span>.
  422. p_html.replace("</mark>", "</span>");
  423. }
  424. return altered;
  425. }
  426. bool VWebUtils::replacePreBackgroundColorWithCode(QString &p_html)
  427. {
  428. if (p_html.isEmpty()) {
  429. return false;
  430. }
  431. bool altered = false;
  432. int pos = 0;
  433. QRegExp bgReg("(\\s|^)(background(-color)?:[^;]+;)");
  434. while (pos < p_html.size()) {
  435. int tagIdx = p_html.indexOf(m_tagReg, pos);
  436. if (tagIdx == -1) {
  437. break;
  438. }
  439. QString tagName = m_tagReg.cap(1);
  440. pos = tagIdx + m_tagReg.matchedLength();
  441. if (tagName.toLower() != "pre") {
  442. continue;
  443. }
  444. int preEnd = skipToTagEnd(p_html, pos, tagName);
  445. HtmlTag nextTag = readNextTag(p_html, pos);
  446. if (nextTag.m_name != "code"
  447. || nextTag.m_start >= preEnd
  448. || nextTag.m_style.isEmpty()) {
  449. continue;
  450. }
  451. // Get the background style of <code>.
  452. int idx = nextTag.m_style.indexOf(bgReg);
  453. if (idx == -1) {
  454. continue;
  455. }
  456. QString bgStyle = bgReg.cap(2);
  457. pos = tagIdx;
  458. idx = p_html.indexOf(m_styleTagReg, pos);
  459. if (idx == -1 || idx != tagIdx) {
  460. // <pre> without "style".
  461. QString newTag = QString("<%1 style=\"%2\" %3>").arg(m_tagReg.cap(1))
  462. .arg(bgStyle)
  463. .arg(m_tagReg.cap(2));
  464. p_html.replace(tagIdx, m_tagReg.matchedLength(), newTag);
  465. pos = tagIdx + newTag.size();
  466. altered = true;
  467. continue;
  468. }
  469. QString newTag;
  470. if (m_styleTagReg.cap(3).indexOf(bgReg) == -1) {
  471. // No background style specified.
  472. newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
  473. .arg(m_styleTagReg.cap(2))
  474. .arg(m_styleTagReg.cap(3) + bgStyle)
  475. .arg(m_styleTagReg.cap(4));
  476. } else {
  477. // Replace background style.
  478. newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
  479. .arg(m_styleTagReg.cap(2))
  480. .arg(m_styleTagReg.cap(3).replace(bgReg, " " + bgStyle))
  481. .arg(m_styleTagReg.cap(4));
  482. }
  483. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  484. pos = idx + newTag.size();
  485. altered = true;
  486. }
  487. return altered;
  488. }
  489. VWebUtils::HtmlTag VWebUtils::readNextTag(const QString &p_html, int p_pos)
  490. {
  491. HtmlTag tag;
  492. int tagIdx = p_html.indexOf(m_tagReg, p_pos);
  493. if (tagIdx == -1) {
  494. return tag;
  495. }
  496. tag.m_name = m_tagReg.cap(1);
  497. tag.m_start = tagIdx;
  498. tag.m_end = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tag.m_name);
  499. int idx = p_html.indexOf(m_styleTagReg, tagIdx);
  500. if (idx == -1 || idx != tagIdx) {
  501. return tag;
  502. }
  503. tag.m_style = m_styleTagReg.cap(3);
  504. return tag;
  505. }
  506. bool VWebUtils::replaceNewLineWithBR(QString &p_html)
  507. {
  508. if (p_html.isEmpty()) {
  509. return false;
  510. }
  511. bool altered = false;
  512. int pos = 0;
  513. const QString brTag("<br/>");
  514. while (pos < p_html.size()) {
  515. int tagIdx = p_html.indexOf(m_tagReg, pos);
  516. if (tagIdx == -1) {
  517. break;
  518. }
  519. QString tagName = m_tagReg.cap(1);
  520. pos = tagIdx + m_tagReg.matchedLength();
  521. if (tagName.toLower() != "pre") {
  522. continue;
  523. }
  524. int preEnd = skipToTagEnd(p_html, pos, tagName);
  525. // Replace '\n' in [pos, preEnd).
  526. while (pos < preEnd) {
  527. int idx = p_html.indexOf('\n', pos);
  528. if (idx == -1 || idx >= preEnd) {
  529. break;
  530. }
  531. p_html.replace(idx, 1, brTag);
  532. pos = idx + brTag.size() - 1;
  533. preEnd = preEnd + brTag.size() - 1;
  534. altered = true;
  535. }
  536. pos = preEnd;
  537. }
  538. return altered;
  539. }
  540. bool VWebUtils::replaceLocalImgWithWarningLabel(QString &p_html)
  541. {
  542. bool altered = false;
  543. QString label = QString("<span style=\"font-weight: bold; color: #FFFFFF; background-color: #EE0000;\">%1</span>")
  544. .arg(QObject::tr("Insert_Image_HERE"));
  545. int pos = 0;
  546. while (pos < p_html.size()) {
  547. int idx = p_html.indexOf(m_imgTagReg, pos);
  548. if (idx == -1) {
  549. break;
  550. }
  551. QString urlStr = m_imgTagReg.cap(1);
  552. QUrl imgUrl(urlStr);
  553. if (imgUrl.scheme() == "https" || imgUrl.scheme() == "http") {
  554. pos = idx + m_imgTagReg.matchedLength();
  555. continue;
  556. }
  557. p_html.replace(idx, m_imgTagReg.matchedLength(), label);
  558. pos = idx + label.size();
  559. altered = true;
  560. }
  561. return altered;
  562. }
  563. bool VWebUtils::addSpanInsideCode(QString &p_html)
  564. {
  565. bool altered = false;
  566. int pos = 0;
  567. while (pos < p_html.size()) {
  568. int tagIdx = p_html.indexOf(m_tagReg, pos);
  569. if (tagIdx == -1) {
  570. break;
  571. }
  572. QString tagName = m_tagReg.cap(1);
  573. QString lowerName = tagName.toLower();
  574. if (lowerName == "pre") {
  575. // Skip <pre>.
  576. pos = skipToTagEnd(p_html, tagIdx + m_tagReg.matchedLength(), tagName);
  577. continue;
  578. }
  579. if (lowerName != "code") {
  580. pos = tagIdx + m_tagReg.matchedLength();
  581. continue;
  582. }
  583. int idx = tagIdx + m_tagReg.matchedLength() - 1;
  584. Q_ASSERT(p_html[idx] == '>');
  585. QString span = QString("><span%1>").arg(m_tagReg.cap(2));
  586. p_html.replace(idx, 1, span);
  587. int codeEnd = skipToTagEnd(p_html, idx + span.size(), tagName, &idx);
  588. Q_ASSERT(idx > -1);
  589. Q_ASSERT(codeEnd - idx == 7);
  590. Q_ASSERT(p_html[idx] == '<');
  591. p_html.replace(idx, 1, "</span><");
  592. pos = codeEnd;
  593. altered = true;
  594. }
  595. return altered;
  596. }
  597. // @p_html is the style string.
  598. static bool replaceQuoteInFontFamilyInStyleString(QString &p_html)
  599. {
  600. QRegExp reg("font-family:((&quot;)|[^;])+;");
  601. int idx = p_html.indexOf(reg);
  602. if (idx == -1) {
  603. return false;
  604. }
  605. QString quote("&quot;");
  606. QString family = reg.cap(0);
  607. if (family.indexOf(quote) == -1) {
  608. return false;
  609. }
  610. QString newFamily = family.replace(quote, "'");
  611. p_html.replace(idx, reg.matchedLength(), newFamily);
  612. return true;
  613. }
  614. bool VWebUtils::replaceQuoteInFontFamily(QString &p_html)
  615. {
  616. bool altered = false;
  617. int pos = 0;
  618. while (pos < p_html.size()) {
  619. int idx = p_html.indexOf(m_styleTagReg, pos);
  620. if (idx == -1) {
  621. break;
  622. }
  623. QString styleStr = m_styleTagReg.cap(3);
  624. if (replaceQuoteInFontFamilyInStyleString(styleStr)) {
  625. QString newTag = QString("<%1%2style=\"%3\"%4>").arg(m_styleTagReg.cap(1))
  626. .arg(m_styleTagReg.cap(2))
  627. .arg(styleStr)
  628. .arg(m_styleTagReg.cap(4));
  629. p_html.replace(idx, m_styleTagReg.matchedLength(), newTag);
  630. pos = idx + newTag.size();
  631. altered = true;
  632. } else {
  633. pos = idx + m_styleTagReg.matchedLength();
  634. }
  635. }
  636. return altered;
  637. }
  638. static bool isHeadingTag(const QString &p_tagName)
  639. {
  640. QString tag = p_tagName.toLower();
  641. if (!tag.startsWith('h') || tag.size() != 2) {
  642. return false;
  643. }
  644. return tag == "h1"
  645. || tag == "h2"
  646. || tag == "h3"
  647. || tag == "h4"
  648. || tag == "h5"
  649. || tag == "h6";
  650. }
  651. bool VWebUtils::replaceHeadingWithSpan(QString &p_html)
  652. {
  653. bool altered = false;
  654. int pos = 0;
  655. QString spanTag("span");
  656. while (pos < p_html.size()) {
  657. int tagIdx = p_html.indexOf(m_tagReg, pos);
  658. if (tagIdx == -1) {
  659. break;
  660. }
  661. QString tagName = m_tagReg.cap(1);
  662. if (!isHeadingTag(tagName)) {
  663. pos = tagIdx + m_tagReg.matchedLength();
  664. continue;
  665. }
  666. p_html.replace(tagIdx + 1, 2, spanTag);
  667. pos = tagIdx + m_tagReg.matchedLength() + spanTag.size() - 2;
  668. pos = skipToTagEnd(p_html, pos, tagName);
  669. Q_ASSERT(pos != -1);
  670. Q_ASSERT(p_html.mid(pos - 3, 2) == tagName);
  671. p_html.replace(pos - 3, 2, spanTag);
  672. pos = pos + spanTag.size() - 2;
  673. altered = true;
  674. }
  675. return altered;
  676. }
  677. bool VWebUtils::fixXHtmlTags(QString &p_html)
  678. {
  679. bool altered = false;
  680. // <img>.
  681. int pos = 0;
  682. const QString legalTag("/>");
  683. while (pos < p_html.size()) {
  684. int idx = p_html.indexOf(m_imgTagReg, pos);
  685. if (idx == -1) {
  686. break;
  687. }
  688. pos = idx + m_imgTagReg.matchedLength();
  689. Q_ASSERT(p_html[pos - 1] == '>');
  690. if (p_html.mid(pos - 2, 2) == legalTag) {
  691. continue;
  692. }
  693. p_html.replace(pos - 1, 1, legalTag);
  694. pos = pos + legalTag.size() - 1;
  695. altered = true;
  696. }
  697. // <br>.
  698. int size = p_html.size();
  699. p_html.replace("<br>", "<br/>");
  700. if (!altered && size != p_html.size()) {
  701. altered = true;
  702. }
  703. return altered;
  704. }