fb2save.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. #include <QtGui>
  2. #include <QtDebug>
  3. #include "fb2save.hpp"
  4. #include "fb2view.hpp"
  5. #include "fb2utils.h"
  6. #include <QAbstractNetworkCache>
  7. #include <QNetworkReply>
  8. #include <QNetworkRequest>
  9. #include <QScopedPointer>
  10. #include <QWebFrame>
  11. #include <QWebPage>
  12. //---------------------------------------------------------------------------
  13. // Fb2HtmlHandler
  14. //---------------------------------------------------------------------------
  15. QString Fb2HtmlHandler::local(const QString &name)
  16. {
  17. return name.mid(name.lastIndexOf(":"));
  18. }
  19. void Fb2HtmlHandler::onAttr(const QString &name, const QString &value)
  20. {
  21. m_atts.append(name, "", local(name), value);
  22. }
  23. void Fb2HtmlHandler::onNew(const QString &name)
  24. {
  25. startElement("", local(name), name, m_atts);
  26. m_atts.clear();
  27. }
  28. void Fb2HtmlHandler::onTxt(const QString &text)
  29. {
  30. characters(text);
  31. }
  32. void Fb2HtmlHandler::onEnd(const QString &name)
  33. {
  34. endElement("", local(name), name);
  35. }
  36. //---------------------------------------------------------------------------
  37. // Fb2SaveWriter
  38. //---------------------------------------------------------------------------
  39. Fb2SaveWriter::Fb2SaveWriter(Fb2WebView &view, QByteArray *array, QList<int> *folds)
  40. : QXmlStreamWriter(array)
  41. , m_folds(folds)
  42. , m_view(view)
  43. , m_line(0)
  44. {
  45. Init();
  46. }
  47. Fb2SaveWriter::Fb2SaveWriter(Fb2WebView &view, QIODevice *device, QList<int> *folds)
  48. : QXmlStreamWriter(device)
  49. , m_folds(folds)
  50. , m_view(view)
  51. , m_line(0)
  52. {
  53. Init();
  54. }
  55. Fb2SaveWriter::Fb2SaveWriter(Fb2WebView &view, QString *string, QList<int> *folds)
  56. : QXmlStreamWriter(string)
  57. , m_folds(folds)
  58. , m_view(view)
  59. , m_line(0)
  60. {
  61. Init();
  62. }
  63. void Fb2SaveWriter::Init()
  64. {
  65. writeStartDocument();
  66. }
  67. Fb2SaveWriter::~Fb2SaveWriter()
  68. {
  69. writeEndDocument();
  70. }
  71. void Fb2SaveWriter::writeStartElement(const QString &name, int level)
  72. {
  73. if (level) writeLineEnd();
  74. for (int i = 1; i < level; i++) writeCharacters(" ");
  75. QXmlStreamWriter::writeStartElement(name);
  76. }
  77. void Fb2SaveWriter::writeEndElement(int level)
  78. {
  79. if (level) writeLineEnd();
  80. for (int i = 1; i < level; i++) writeCharacters(" ");
  81. QXmlStreamWriter::writeEndElement();
  82. }
  83. void Fb2SaveWriter::writeLineEnd()
  84. {
  85. writeCharacters("\n");
  86. m_line++;
  87. }
  88. QByteArray Fb2SaveWriter::downloadFile(const QUrl &url)
  89. {
  90. QNetworkRequest request(url);
  91. QNetworkAccessManager * network = m_view.page()->networkAccessManager();
  92. QScopedPointer<QNetworkReply> reply(network->get(request));
  93. if (reply.isNull()) return QByteArray();
  94. QEventLoop loop;
  95. QObject::connect(reply.data(), SIGNAL(finished()), &loop, SLOT(quit()));
  96. loop.exec();
  97. return reply->readAll();
  98. }
  99. QString Fb2SaveWriter::newFileName(const QString &path)
  100. {
  101. QFileInfo info(path);
  102. QString name = info.fileName();
  103. if (!m_view.files().exists(name) && !m_files.exists(name)) return name;
  104. QString base = info.baseName();
  105. QString suff = info.suffix();
  106. for (int i = 1; ; i++) {
  107. QString name = QString("%1(%2).%3").arg(base).arg(i).arg(suff);
  108. if (m_view.files().exists(name)) continue;
  109. if (m_files.exists(name)) continue;
  110. return name;
  111. }
  112. }
  113. QString Fb2SaveWriter::getFileName(const QString &path)
  114. {
  115. QUrl url = path;
  116. if (url.scheme() == "fb2") {
  117. QString name = url.path();
  118. if (m_view.files().exists(name)) {
  119. m_names.append(name);
  120. return name;
  121. }
  122. return QString();
  123. } else {
  124. QByteArray data = downloadFile(url);
  125. if (data.size() == 0) return QString();
  126. QString hash = Fb2TemporaryFile::md5(data);
  127. QString name = m_view.files().name(hash);
  128. if (name.isEmpty()) m_files.name(hash);
  129. if (name.isEmpty()) {
  130. name = newFileName(url.path());
  131. m_files.set(name, data, hash);
  132. m_names.append(name);
  133. }
  134. return name;
  135. }
  136. }
  137. QByteArray Fb2SaveWriter::getFileData(const QString &name)
  138. {
  139. QByteArray data = m_view.files().data(name);
  140. if (data.size() == 0) data = m_files.data(name);
  141. return data;
  142. }
  143. void Fb2SaveWriter::writeFiles()
  144. {
  145. QStringListIterator it(m_names);
  146. while (it.hasNext()) {
  147. QString name = it.next();
  148. if (name.isEmpty()) continue;
  149. QString data = getFileData(name).toBase64();
  150. if (data.isEmpty()) continue;
  151. writeStartElement("binary", 2);
  152. if (m_folds) m_folds->append(m_line);
  153. writeAttribute("id", name);
  154. writeLineEnd();
  155. int pos = 0;
  156. while (true) {
  157. QString text = data.mid(pos, 76);
  158. if (text.isEmpty()) break;
  159. writeCharacters(text);
  160. writeLineEnd();
  161. pos += 76;
  162. }
  163. writeCharacters(" ");
  164. QXmlStreamWriter::writeEndElement();
  165. }
  166. }
  167. //---------------------------------------------------------------------------
  168. // Fb2SaveHandler::BodyHandler
  169. //---------------------------------------------------------------------------
  170. FB2_BEGIN_KEYHASH(Fb2SaveHandler::BodyHandler)
  171. FB2_KEY( Section , "div" );
  172. FB2_KEY( Anchor , "a" );
  173. FB2_KEY( Image , "img" );
  174. FB2_KEY( Table , "table" );
  175. FB2_KEY( Parag , "p" );
  176. FB2_KEY( Strong , "b" );
  177. FB2_KEY( Emphas , "i" );
  178. FB2_KEY( Strike , "strike" );
  179. FB2_KEY( Sub , "sub" );
  180. FB2_KEY( Sup , "sup" );
  181. FB2_KEY( Code , "tt" );
  182. FB2_END_KEYHASH
  183. Fb2SaveHandler::BodyHandler::BodyHandler(Fb2SaveWriter &writer, const QString &name, const QXmlAttributes &atts, const QString &tag)
  184. : NodeHandler(name)
  185. , m_writer(writer)
  186. , m_tag(tag)
  187. , m_level(1)
  188. , m_hasChild(false)
  189. {
  190. Init(atts);
  191. }
  192. Fb2SaveHandler::BodyHandler::BodyHandler(BodyHandler *parent, const QString &name, const QXmlAttributes &atts, const QString &tag)
  193. : NodeHandler(name)
  194. , m_writer(parent->m_writer)
  195. , m_tag(tag)
  196. , m_level(parent->nextLevel())
  197. , m_hasChild(false)
  198. {
  199. Init(atts);
  200. }
  201. void Fb2SaveHandler::BodyHandler::Init(const QXmlAttributes &atts)
  202. {
  203. if (m_tag.isEmpty()) return;
  204. m_writer.writeStartElement(m_tag, m_level);
  205. int count = atts.count();
  206. for (int i = 0; i < count; i++) {
  207. QString name = atts.qName(i);
  208. if (name == "id") {
  209. m_writer.writeAttribute(name, atts.value(i));
  210. } else if (name == "name") {
  211. m_writer.writeAttribute(name, atts.value(i));
  212. } else if (name.left(4) == "fb2:") {
  213. m_writer.writeAttribute(name.mid(4), atts.value(i));
  214. }
  215. }
  216. }
  217. Fb2XmlHandler::NodeHandler * Fb2SaveHandler::BodyHandler::NewTag(const QString &name, const QXmlAttributes &atts)
  218. {
  219. m_hasChild = true;
  220. QString tag = QString();
  221. switch (toKeyword(name)) {
  222. case Section : tag = atts.value("class") ; break;
  223. case Anchor : return new AnchorHandler(this, name, atts);
  224. case Image : return new ImageHandler(this, name, atts);
  225. case Parag : return new ParagHandler(this, name, atts);
  226. case Strong : tag = "strong" ; break;
  227. case Emphas : tag = "emphasis" ; break;
  228. case Strike : tag = "strikethrough" ; break;
  229. case Code : tag = "code" ; break;
  230. case Sub : tag = "sub" ; break;
  231. case Sup : tag = "sup" ; break;
  232. default: ;
  233. }
  234. return new BodyHandler(this, name, atts, tag);
  235. }
  236. void Fb2SaveHandler::BodyHandler::TxtTag(const QString &text)
  237. {
  238. m_writer.writeCharacters(text);
  239. }
  240. void Fb2SaveHandler::BodyHandler::EndTag(const QString &name)
  241. {
  242. Q_UNUSED(name);
  243. if (m_tag.isEmpty()) return;
  244. m_writer.writeEndElement(m_hasChild ? m_level : 0);
  245. }
  246. int Fb2SaveHandler::BodyHandler::nextLevel() const
  247. {
  248. return m_level ? m_level + 1 : 0;
  249. }
  250. //---------------------------------------------------------------------------
  251. // Fb2SaveHandler::RootHandler
  252. //---------------------------------------------------------------------------
  253. Fb2SaveHandler::RootHandler::RootHandler(Fb2SaveWriter &writer, const QString &name, const QXmlAttributes &atts)
  254. : BodyHandler(writer, name, atts, "FictionBook")
  255. {
  256. m_writer.writeAttribute("xmlns", "http://www.gribuser.ru/xml/fictionbook/2.0");
  257. m_writer.writeAttribute("xmlns:l", "http://www.w3.org/1999/xlink");
  258. }
  259. void Fb2SaveHandler::RootHandler::EndTag(const QString &name)
  260. {
  261. m_writer.writeFiles();
  262. BodyHandler::EndTag(name);
  263. }
  264. //---------------------------------------------------------------------------
  265. // Fb2SaveHandler::AnchorHandler
  266. //---------------------------------------------------------------------------
  267. Fb2SaveHandler::AnchorHandler::AnchorHandler(BodyHandler *parent, const QString &name, const QXmlAttributes &atts)
  268. : BodyHandler(parent, name, atts, "a")
  269. {
  270. QString href = Value(atts, "href");
  271. m_writer.writeAttribute("l:href", href);
  272. }
  273. //---------------------------------------------------------------------------
  274. // Fb2SaveHandler::ImageHandler
  275. //---------------------------------------------------------------------------
  276. Fb2SaveHandler::ImageHandler::ImageHandler(BodyHandler *parent, const QString &name, const QXmlAttributes &atts)
  277. : BodyHandler(parent, name, atts, "image")
  278. {
  279. QString src = Value(atts, "src");
  280. QString file = m_writer.getFileName(src);
  281. file.prepend('#');
  282. m_writer.writeAttribute("l:href", file);
  283. m_writer.writeEndElement(0);
  284. }
  285. //---------------------------------------------------------------------------
  286. // Fb2SaveHandler::ParagHandler
  287. //---------------------------------------------------------------------------
  288. Fb2SaveHandler::ParagHandler::ParagHandler(BodyHandler *parent, const QString &name, const QXmlAttributes &atts)
  289. : BodyHandler(parent, name, atts, "")
  290. , m_parent(parent->tag())
  291. , m_empty(true)
  292. {
  293. }
  294. Fb2XmlHandler::NodeHandler * Fb2SaveHandler::ParagHandler::NewTag(const QString &name, const QXmlAttributes &atts)
  295. {
  296. if (m_empty) start();
  297. return BodyHandler::NewTag(name, atts);
  298. }
  299. void Fb2SaveHandler::ParagHandler::TxtTag(const QString &text)
  300. {
  301. if (m_empty) {
  302. if (isWhiteSpace(text)) return;
  303. start();
  304. }
  305. BodyHandler::TxtTag(text);
  306. }
  307. void Fb2SaveHandler::ParagHandler::EndTag(const QString &name)
  308. {
  309. Q_UNUSED(name);
  310. if (m_empty) m_writer.writeStartElement("empty-line", m_level);
  311. m_writer.writeEndElement(0);
  312. }
  313. void Fb2SaveHandler::ParagHandler::start()
  314. {
  315. if (!m_empty) return;
  316. QString tag = m_parent == "stanza" ? "v" : "p";
  317. m_writer.writeStartElement(tag, m_level);
  318. m_empty = false;
  319. }
  320. //---------------------------------------------------------------------------
  321. // Fb2SaveHandler
  322. //---------------------------------------------------------------------------
  323. Fb2SaveHandler::Fb2SaveHandler(Fb2WebView &view, QIODevice *device, QList<int> *folds)
  324. : Fb2HtmlHandler()
  325. , m_writer(view, device, folds)
  326. , m_view(view)
  327. {
  328. }
  329. Fb2SaveHandler::Fb2SaveHandler(Fb2WebView &view, QByteArray *array, QList<int> *folds)
  330. : Fb2HtmlHandler()
  331. , m_writer(view, array, folds)
  332. , m_view(view)
  333. {
  334. }
  335. Fb2XmlHandler::NodeHandler * Fb2SaveHandler::CreateRoot(const QString &name, const QXmlAttributes &atts)
  336. {
  337. if (name == "body") return new RootHandler(m_writer, name, atts);
  338. m_error = QObject::tr("The tag <body> was not found.");
  339. return 0;
  340. }
  341. bool Fb2SaveHandler::save()
  342. {
  343. static const QString javascript = FB2::read(":/js/export.js");
  344. m_view.page()->mainFrame()->addToJavaScriptWindowObject("handler", this);
  345. m_view.page()->mainFrame()->evaluateJavaScript(javascript);
  346. return true;
  347. }