Fb2Parser.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. const XmlParser = require('../xml/XmlParser');
  2. class Fb2Parser extends XmlParser {
  3. get xlinkNS() {
  4. if (!this._xlinkNS) {
  5. const rootAttrs = this.$self().attrs();
  6. let ns = 'l';
  7. for (const [key, value] of rootAttrs) {
  8. if (value == 'http://www.w3.org/1999/xlink') {
  9. ns = key.split(':')[1] || ns;
  10. break;
  11. }
  12. }
  13. this._xlinkNS = ns;
  14. }
  15. return this._xlinkNS;
  16. }
  17. bookInfo(fb2Object) {
  18. const result = {};
  19. if (!fb2Object)
  20. fb2Object = this.toObject();
  21. const desc = this.inspector(fb2Object).$('fictionbook/description');
  22. if (!desc)
  23. return result;
  24. //title-info
  25. const titleInfo = desc.$('title-info');
  26. if (titleInfo) {
  27. const info = {};
  28. info.genre = [];
  29. for (const g of titleInfo.$$('genre'))
  30. info.genre.push(g.text());
  31. const parseAuthors = (tagName) => {
  32. const authors = [];
  33. for (const a of titleInfo.$$(tagName)) {
  34. let names = [];
  35. names.push(a.text('last-name'));
  36. names.push(a.text('first-name'));
  37. names.push(a.text('middle-name'));
  38. names = names.filter(n => n);
  39. if (!names.length)
  40. names.push(a.text('nickname'));
  41. authors.push(names.join(' '));
  42. }
  43. return authors;
  44. }
  45. info.author = parseAuthors('author');
  46. info.bookTitle = titleInfo.text('book-title');
  47. info.annotation = null;
  48. info.annotationHtml = null;
  49. const node = titleInfo.$('annotation') && titleInfo.$('annotation').value;
  50. if (node) {
  51. //annotation как кусок xml
  52. info.annotation = (new XmlParser()).fromObject(node).toString({noHeader: true});
  53. //annotation как html
  54. info.annotationHtml = this.toHtml(info.annotation);
  55. }
  56. info.keywords = titleInfo.text('keywords');
  57. info.date = titleInfo.text('date');
  58. info.coverpage = titleInfo.$('coverpage') && titleInfo.$('coverpage').value;
  59. info.lang = titleInfo.text('lang');
  60. info.srcLang = titleInfo.text('src-lang');
  61. info.translator = parseAuthors('translator');
  62. const seqAttrs = titleInfo.attrs('sequence') || new Map();
  63. info.sequenceName = seqAttrs.get('name') || null;
  64. info.sequenceNum = seqAttrs.get('number') || null;
  65. info.sequenceLang = seqAttrs.get('xml:lang') || null;
  66. result.titleInfo = info;
  67. }
  68. return result;
  69. }
  70. bookInfoList(fb2Object, options = {}) {
  71. let {
  72. correctMapping = false,
  73. valueToString = false,
  74. } = options;
  75. if (!correctMapping)
  76. correctMapping = mapping => mapping;
  77. if (!valueToString) {
  78. valueToString = (value, nodePath) => {//eslint-disable-line no-unused-vars
  79. if (typeof(value) === 'string') {
  80. return value;
  81. } else if (Array.isArray(value)) {
  82. return value.join(', ');
  83. } else if (typeof(value) === 'object') {
  84. return JSON.stringify(value);
  85. }
  86. return value;
  87. };
  88. }
  89. let mapping = [
  90. {name: 'titleInfo', label: 'Общая информация', value: [
  91. {name: 'author', label: 'Автор(ы)'},
  92. {name: 'bookTitle', label: 'Название'},
  93. {name: 'sequenceName', label: 'Серия'},
  94. {name: 'sequenceNum', label: 'Номер в серии'},
  95. {name: 'genre', label: 'Жанр'},
  96. {name: 'date', label: 'Дата'},
  97. {name: 'lang', label: 'Язык книги'},
  98. {name: 'srcLang', label: 'Язык оригинала'},
  99. {name: 'translator', label: 'Переводчик(и)'},
  100. {name: 'keywords', label: 'Ключевые слова'},
  101. ]},
  102. ];
  103. mapping = correctMapping(mapping);
  104. const bookInfo = this.bookInfo(fb2Object);
  105. //заполняем mapping
  106. let result = [];
  107. for (const item of mapping) {
  108. const itemOut = {name: item.name, label: item.label, value: []};
  109. const info = bookInfo[item.name];
  110. if (!info)
  111. continue;
  112. for (const subItem of item.value) {
  113. if (info[subItem.name] !== null)
  114. itemOut.value.push({
  115. name: subItem.name,
  116. label: subItem.label,
  117. value: valueToString(info[subItem.name])
  118. });
  119. }
  120. if (itemOut.value.length)
  121. result.push(itemOut);
  122. }
  123. return result;
  124. }
  125. toHtml(xmlString) {
  126. const substs = {
  127. '<subtitle>': '<p><b>',
  128. '</subtitle>': '</b></p>',
  129. '<empty-line/>': '<br>',
  130. '<strong>': '<b>',
  131. '</strong>': '</b>',
  132. '<emphasis>': '<i>',
  133. '</emphasis>': '</i>',
  134. '<stanza>': '<br>',
  135. '</stanza>': '',
  136. '<poem>': '<br>',
  137. '</poem>': '',
  138. '<cite>': '<i>',
  139. '</cite>': '</i>',
  140. '<table>': '<br>',
  141. '</table>': '',
  142. };
  143. for (const [tag, s] of Object.entries(substs)) {
  144. const r = new RegExp(`${tag}`, 'g');
  145. xmlString = xmlString.replace(r, s);
  146. }
  147. return xmlString;
  148. }
  149. }
  150. module.exports = Fb2Parser;