fb2save.cpp 11 KB

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