fb2save.cpp 10 KB

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