fb2save.cpp 13 KB

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