fb2read.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. #include <QtGui>
  2. #include <QTextEdit>
  3. #include <QtDebug>
  4. #include "fb2read.h"
  5. //---------------------------------------------------------------------------
  6. // Fb2ReadThread
  7. //---------------------------------------------------------------------------
  8. Fb2ReadThread::Fb2ReadThread(QObject *parent, const QString &filename)
  9. : QThread(parent)
  10. , m_filename(filename)
  11. , m_abort(false)
  12. {
  13. }
  14. Fb2ReadThread::~Fb2ReadThread()
  15. {
  16. stop();
  17. wait();
  18. }
  19. QString Fb2ReadThread::file()
  20. {
  21. QMutexLocker locker(&mutex);
  22. Q_UNUSED(locker);
  23. return m_filename;
  24. }
  25. QString Fb2ReadThread::html()
  26. {
  27. QMutexLocker locker(&mutex);
  28. Q_UNUSED(locker);
  29. return m_html;
  30. }
  31. void Fb2ReadThread::stop()
  32. {
  33. QMutexLocker locker(&mutex);
  34. Q_UNUSED(locker);
  35. m_abort = true;
  36. }
  37. void Fb2ReadThread::run()
  38. {
  39. if (Fb2Handler::toHTML(m_filename, m_html)) {
  40. emit sendDocument();
  41. }
  42. }
  43. //---------------------------------------------------------------------------
  44. // Fb2Handler::BaseHandler
  45. //---------------------------------------------------------------------------
  46. #define FB2_BEGIN_KEYHASH(x) \
  47. Fb2Handler::x::Keyword Fb2Handler::x::toKeyword(const QString &name) \
  48. { \
  49. static const KeywordHash map; \
  50. KeywordHash::const_iterator i = map.find(name); \
  51. return i == map.end() ? None : i.value(); \
  52. } \
  53. Fb2Handler::x::KeywordHash::KeywordHash() {
  54. #define FB2_END_KEYHASH }
  55. #define FB2_KEY(key,str) insert(str,key);
  56. static QString Value(const QXmlAttributes &attributes, const QString &name)
  57. {
  58. int count = attributes.count();
  59. for (int i = 0; i < count; i++ ) {
  60. if (attributes.localName(i).compare(name, Qt::CaseInsensitive) == 0) {
  61. return attributes.value(i);
  62. }
  63. }
  64. return QString();
  65. }
  66. Fb2Handler::BaseHandler::~BaseHandler()
  67. {
  68. if (m_handler) delete m_handler;
  69. }
  70. bool Fb2Handler::BaseHandler::doStart(const QString &name, const QXmlAttributes &attributes)
  71. {
  72. if (m_handler) return m_handler->doStart(name, attributes);
  73. m_handler = NewTag(name, attributes); if (m_handler) return true;
  74. // qCritical() << QObject::tr("Unknown XML child tag: <%1> <%2>").arg(m_name).arg(name);
  75. m_handler = new BaseHandler(name);
  76. return true;
  77. }
  78. bool Fb2Handler::BaseHandler::doText(const QString &text)
  79. {
  80. if (m_handler) m_handler->doText(text); else TxtTag(text);
  81. return true;
  82. }
  83. bool Fb2Handler::BaseHandler::doEnd(const QString &name, bool & exists)
  84. {
  85. if (m_handler) {
  86. bool found = exists || name == m_name;
  87. m_handler->doEnd(name, found);
  88. if (m_handler->m_closed) { delete m_handler; m_handler = NULL; }
  89. if (found) { exists = true; return true; }
  90. }
  91. bool found = name == m_name;
  92. if (!found) qCritical() << QObject::tr("Conglict XML tags: <%1> - </%2>").arg(m_name).arg(name);
  93. m_closed = found || exists;
  94. if (m_closed) EndTag(m_name);
  95. exists = found;
  96. return true;
  97. }
  98. //---------------------------------------------------------------------------
  99. // Fb2Handler::RootHandler
  100. //---------------------------------------------------------------------------
  101. FB2_BEGIN_KEYHASH(RootHandler)
  102. insert("stylesheet", Style);
  103. insert("description", Descr);
  104. insert("body", Body);
  105. insert("binary", Binary);
  106. FB2_END_KEYHASH
  107. Fb2Handler::RootHandler::RootHandler(QXmlStreamWriter &writer, const QString &name)
  108. : BaseHandler(name)
  109. , m_writer(writer)
  110. {
  111. m_writer.writeStartElement("html");
  112. m_writer.writeStartElement("body");
  113. }
  114. Fb2Handler::RootHandler::~RootHandler()
  115. {
  116. m_writer.writeEndElement();
  117. m_writer.writeEndElement();
  118. }
  119. Fb2Handler::BaseHandler * Fb2Handler::RootHandler::NewTag(const QString &name, const QXmlAttributes &attributes)
  120. {
  121. switch (toKeyword(name)) {
  122. case Body : return new BodyHandler(m_writer, name, "div", name);
  123. case Descr : return new DescrHandler(m_writer, name);
  124. case Binary : return new BinaryHandler(name, attributes);
  125. default: return NULL;
  126. }
  127. }
  128. //---------------------------------------------------------------------------
  129. // Fb2Handler::DescrHandler
  130. //---------------------------------------------------------------------------
  131. FB2_BEGIN_KEYHASH(DescrHandler)
  132. insert( "title-info" , Title );
  133. insert( "publish-info" , Publish );
  134. FB2_END_KEYHASH
  135. Fb2Handler::BaseHandler * Fb2Handler::DescrHandler::NewTag(const QString &name, const QXmlAttributes &attributes)
  136. {
  137. Q_UNUSED(attributes);
  138. switch (toKeyword(name)) {
  139. case Title : return new HeaderHandler(m_writer, name);
  140. case Publish : return new BaseHandler(name);
  141. default: return NULL;
  142. }
  143. }
  144. //---------------------------------------------------------------------------
  145. // Fb2Handler::HeaderHandler
  146. //---------------------------------------------------------------------------
  147. FB2_BEGIN_KEYHASH(HeaderHandler)
  148. insert( "book-title" , Title );
  149. insert( "author" , Author );
  150. insert( "sequence" , Sequence );
  151. insert( "genre" , Genre );
  152. insert( "lang" , Lang );
  153. insert( "annotation" , Annot );
  154. insert( "coverpage" , Cover );
  155. FB2_END_KEYHASH
  156. Fb2Handler::BaseHandler * Fb2Handler::HeaderHandler::NewTag(const QString &name, const QXmlAttributes &attributes)
  157. {
  158. Q_UNUSED(attributes);
  159. switch (toKeyword(name)) {
  160. case Title : return new BaseHandler(name);
  161. case Annot : return new BaseHandler(name);
  162. default: return NULL;
  163. }
  164. }
  165. //---------------------------------------------------------------------------
  166. // Fb2Handler::BodyHandler
  167. //---------------------------------------------------------------------------
  168. FB2_BEGIN_KEYHASH(BodyHandler)
  169. FB2_KEY( Section, "annotation" );
  170. FB2_KEY( Section, "author" );
  171. FB2_KEY( Section, "cite" );
  172. FB2_KEY( Section, "date" );
  173. FB2_KEY( Section, "epigraph" );
  174. FB2_KEY( Section, "poem" );
  175. FB2_KEY( Section, "section" );
  176. FB2_KEY( Section, "stanza" );
  177. FB2_KEY( Section, "subtitle" );
  178. FB2_KEY( Section, "title" );
  179. FB2_KEY( Anchor, "a" );
  180. FB2_KEY( Table, "table" );
  181. FB2_KEY( Image, "image" );
  182. FB2_KEY( Parag, "empty-line" );
  183. FB2_KEY( Parag, "p" );
  184. FB2_KEY( Parag, "v" );
  185. FB2_KEY( Style, "style" );
  186. FB2_KEY( Strong, "strong" );
  187. FB2_KEY( Emphas, "emphasis" );
  188. FB2_KEY( Strike, "strikethrough" );
  189. FB2_KEY( Sub, "sub" );
  190. FB2_KEY( Sup, "sup" );
  191. FB2_KEY( Code, "code" );
  192. FB2_END_KEYHASH
  193. Fb2Handler::BodyHandler::BodyHandler(QXmlStreamWriter &writer, const QString &name, const QString &tag, const QString &style)
  194. : BaseHandler(name)
  195. , m_writer(writer)
  196. {
  197. m_writer.writeStartElement(tag);
  198. if (!style.isEmpty()) m_writer.writeAttribute("style", style);
  199. }
  200. Fb2Handler::BodyHandler::~BodyHandler()
  201. {
  202. m_writer.writeEndElement();
  203. }
  204. Fb2Handler::BaseHandler * Fb2Handler::BodyHandler::NewTag(const QString &name, const QXmlAttributes &attributes)
  205. {
  206. switch (toKeyword(name)) {
  207. case Section : return new BodyHandler(m_writer, name, "div", name);
  208. case Parag : return new BodyHandler(m_writer, name, "p");
  209. case Strong : return new BodyHandler(m_writer, name, "b");
  210. case Emphas : return new BodyHandler(m_writer, name, "i");
  211. case Strike : return new BodyHandler(m_writer, name, "s");
  212. case Code : return new BodyHandler(m_writer, name, "tt");
  213. case Sub : return new BodyHandler(m_writer, name, "sub");
  214. case Sup : return new BodyHandler(m_writer, name, "sup");
  215. case Image : return new ImageHandler(m_writer, name, attributes);
  216. default: return NULL;
  217. }
  218. }
  219. void Fb2Handler::BodyHandler::TxtTag(const QString &text)
  220. {
  221. m_writer.writeCharacters(text);
  222. }
  223. //---------------------------------------------------------------------------
  224. // Fb2Handler::BlockHandler
  225. //---------------------------------------------------------------------------
  226. /*
  227. Fb2Handler::BaseHandler * Fb2Handler::BlockHandler::NewTag(const QString &name, const QXmlAttributes &attributes)
  228. {
  229. Q_UNUSED(attributes);
  230. QTextCharFormat format;
  231. switch (toKeyword(name)) {
  232. case Image : return new ImageHandler(*this, name, attributes);
  233. case Strong : format.setFontWeight(QFont::Bold); break;
  234. case Emphasis : format.setFontItalic(true); break;
  235. case Strike : format.setFontStrikeOut(true); break;
  236. case Sub : format.setVerticalAlignment(QTextCharFormat::AlignSubScript); break;
  237. case Sup : format.setVerticalAlignment(QTextCharFormat::AlignSuperScript); break;
  238. case Anchor : {
  239. QString href = Value(attributes, "href");
  240. if (!href.isEmpty()) {
  241. format.setAnchorHref(href);
  242. format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
  243. }
  244. } break;
  245. default:
  246. return new UnknowHandler(*this, name); break;
  247. }
  248. return new BlockHandler(*this, name, attributes, &format);
  249. }
  250. */
  251. //---------------------------------------------------------------------------
  252. // Fb2Handler::ImageHandler
  253. //---------------------------------------------------------------------------
  254. Fb2Handler::ImageHandler::ImageHandler(QXmlStreamWriter &writer, const QString &name, const QXmlAttributes &attributes)
  255. : BaseHandler(name)
  256. {
  257. QString image = Value(attributes, "href");
  258. writer.writeStartElement("img");
  259. writer.writeAttribute("href", image);
  260. writer.writeEndElement();
  261. }
  262. //---------------------------------------------------------------------------
  263. // Fb2Handler::BinaryHandler
  264. //---------------------------------------------------------------------------
  265. Fb2Handler::BinaryHandler::BinaryHandler(const QString &name, const QXmlAttributes &attributes)
  266. : BaseHandler(name)
  267. , m_file(Value(attributes, "id"))
  268. {
  269. }
  270. void Fb2Handler::BinaryHandler::TxtTag(const QString &text)
  271. {
  272. m_text += text;
  273. }
  274. void Fb2Handler::BinaryHandler::EndTag(const QString &name)
  275. {
  276. Q_UNUSED(name);
  277. QByteArray in; in.append(m_text);
  278. QImage img = QImage::fromData(QByteArray::fromBase64(in));
  279. // if (!m_file.isEmpty()) m_document.addResource(QTextDocument::ImageResource, QUrl(m_file), img);
  280. }
  281. //---------------------------------------------------------------------------
  282. // Fb2Handler
  283. //---------------------------------------------------------------------------
  284. Fb2Handler::Fb2Handler(QString &string)
  285. : QXmlDefaultHandler()
  286. , m_writer(&string)
  287. , m_handler(NULL)
  288. {
  289. }
  290. Fb2Handler::~Fb2Handler()
  291. {
  292. if (m_handler) delete m_handler;
  293. }
  294. bool Fb2Handler::startElement(const QString & namespaceURI, const QString & localName, const QString &qName, const QXmlAttributes &attributes)
  295. {
  296. Q_UNUSED(namespaceURI);
  297. Q_UNUSED(localName);
  298. const QString name = qName.toLower();
  299. if (m_handler) return m_handler->doStart(name, attributes);
  300. qCritical() << name;
  301. if (name == "fictionbook") {
  302. m_handler = new RootHandler(m_writer, name);
  303. return true;
  304. } else {
  305. m_error = QObject::tr("The file is not an FB2 file.");
  306. return false;
  307. }
  308. }
  309. static bool isWhiteSpace(const QString &str)
  310. {
  311. return str.simplified().isEmpty();
  312. }
  313. bool Fb2Handler::characters(const QString &str)
  314. {
  315. QString s = str.simplified();
  316. if (s.isEmpty()) return true;
  317. if (isWhiteSpace(str.left(1))) s.prepend(" ");
  318. if (isWhiteSpace(str.right(1))) s.append(" ");
  319. return m_handler && m_handler->doText(s);
  320. }
  321. bool Fb2Handler::endElement(const QString & namespaceURI, const QString & localName, const QString &qName)
  322. {
  323. Q_UNUSED(namespaceURI);
  324. Q_UNUSED(localName);
  325. bool found = false;
  326. return m_handler && m_handler->doEnd(qName.toLower(), found);
  327. }
  328. bool Fb2Handler::fatalError(const QXmlParseException &exception)
  329. {
  330. qCritical() << QObject::tr("Parse error at line %1, column %2:\n%3")
  331. .arg(exception.lineNumber())
  332. .arg(exception.columnNumber())
  333. .arg(exception.message());
  334. return false;
  335. }
  336. QString Fb2Handler::errorString() const
  337. {
  338. return m_error;
  339. }
  340. bool Fb2Handler::toHTML(const QString &filename, QString &html)
  341. {
  342. QFile file(filename);
  343. if (!file.open(QFile::ReadOnly | QFile::Text)) {
  344. qCritical() << QObject::tr("Cannot read file %1:\n%2.").arg(filename).arg(file.errorString());
  345. return false;
  346. }
  347. Fb2Handler handler(html);
  348. QXmlSimpleReader reader;
  349. reader.setContentHandler(&handler);
  350. reader.setErrorHandler(&handler);
  351. QXmlInputSource source(&file);
  352. return reader.parse(source);
  353. }
  354. #undef FB2_BEGIN_KEYHASH
  355. #undef FB2_END_KEYHASH
  356. #undef FB2_KEY