Compare commits

...

34 Commits
0.4.2 ... 0.4.6

Author SHA1 Message Date
Book Pauk
1670df02db Merge branch 'release/0.4.6' 2019-02-24 22:02:43 +07:00
Book Pauk
0268e647cd Версия 0.4.6 2019-02-24 22:02:27 +07:00
Book Pauk
4b9315c13c Поправка бага 2019-02-24 22:00:41 +07:00
Book Pauk
14a9948dd2 Merge tag '0.4.5' into develop
0.4.5
2019-02-24 21:37:21 +07:00
Book Pauk
060ec98b0c Merge branch 'release/0.4.5' 2019-02-24 21:37:11 +07:00
Book Pauk
9c1efe381e Версия 0.4.5 2019-02-24 21:36:49 +07:00
Book Pauk
6a1b052d16 Поправил баг 2019-02-24 21:36:06 +07:00
Book Pauk
d6151d541e Поправил баг 2019-02-24 21:27:18 +07:00
Book Pauk
3e90277e1e Добавил опцию imageFitWidth 2019-02-24 19:32:16 +07:00
Book Pauk
641cbdfe85 Merge tag '0.4.4' into develop
0.4.4
2019-02-24 00:41:59 +07:00
Book Pauk
c36e9b36d8 Merge branch 'release/0.4.4' 2019-02-24 00:41:48 +07:00
Book Pauk
ecc3acce93 Версия 0.4.4 2019-02-24 00:41:35 +07:00
Book Pauk
33a2ca55f0 Поправки 2019-02-24 00:32:19 +07:00
Book Pauk
9d0bbec4b3 Поправка отображения картинок внутри title 2019-02-24 00:00:55 +07:00
Book Pauk
4de0b3cffd Поправки парсера fb2 2019-02-23 23:51:06 +07:00
Book Pauk
06221a474b Мелкая поправка 2019-02-23 23:38:19 +07:00
Book Pauk
e99a42b7af К предыдущему 2019-02-23 23:20:03 +07:00
Book Pauk
37822e8409 Мелкая поправка 2019-02-23 23:18:03 +07:00
Book Pauk
2e477e6c99 Улучшение парсинга СИ 2019-02-23 23:03:01 +07:00
Book Pauk
360ee98d8d Улучшение парсинга fb2 2019-02-23 22:17:16 +07:00
Book Pauk
69afd7720a Поправки форматирования fb2 2019-02-23 20:45:21 +07:00
Book Pauk
a75590c493 Улучшение формирования fb2 2019-02-23 20:15:40 +07:00
Book Pauk
2acb65f6b3 Мелкие поправки 2019-02-23 20:04:41 +07:00
Book Pauk
1e1a58b58c Улучшен convertHtml 2019-02-23 19:47:16 +07:00
Book Pauk
aeadb5aeb8 Улучшено определение кодировки и текстового файла 2019-02-23 19:09:57 +07:00
Book Pauk
3e2f01d56d Мелкие поправки 2019-02-23 18:06:29 +07:00
Book Pauk
cad97e639a Добавил цвет фона 2019-02-23 14:37:40 +07:00
Book Pauk
e2632f1802 Добавил вывод версии в консоль 2019-02-23 14:24:29 +07:00
Book Pauk
9aa0bb2bde Merge tag '0.4.3' into develop
0.4.3
2019-02-23 14:17:13 +07:00
Book Pauk
f015d5f7ed Merge branch 'release/0.4.3' 2019-02-23 14:17:00 +07:00
Book Pauk
74f8f7f9a4 0.4.3 2019-02-23 14:16:43 +07:00
Book Pauk
2598538de9 Улучшил отзывчивость historyPage при открытии 2019-02-23 14:13:41 +07:00
Book Pauk
a78a00df2b Поправка label 2019-02-23 14:03:28 +07:00
Book Pauk
92f6beb64e Merge tag '0.4.2' into develop
0.4.2
2019-02-21 21:39:19 +07:00
11 changed files with 201 additions and 91 deletions

View File

@@ -15,8 +15,14 @@
border border
:default-sort = "{prop: 'touchDateTime', order: 'descending'}" :default-sort = "{prop: 'touchDateTime', order: 'descending'}"
:header-cell-style = "headerCellStyle" :header-cell-style = "headerCellStyle"
:row-key = "rowKey"
> >
<el-table-column
type="index"
width="35px"
>
</el-table-column>
<el-table-column <el-table-column
prop="touchDateTime" prop="touchDateTime"
min-width="90px" min-width="90px"
@@ -43,6 +49,7 @@
placeholder="Найти"/--> placeholder="Найти"/-->
<div class="el-input el-input--mini"> <div class="el-input el-input--mini">
<input class="el-input__inner" <input class="el-input__inner"
ref="input"
placeholder="Найти" placeholder="Найти"
style="margin: 0; padding: 0; vertical-align: bottom; margin-top: 20px; padding: 0 10px 0 10px" style="margin: 0; padding: 0; vertical-align: bottom; margin-top: 20px; padding: 0 10px 0 10px"
:value="search" @input="search = $event.target.value" :value="search" @input="search = $event.target.value"
@@ -118,9 +125,16 @@ class HistoryPage extends Vue {
created() { created() {
} }
mounted() { init() {
this.updateTableData(); this.updateTableData();
this.mostRecentBook = bookManager.mostRecentBook(); this.mostRecentBook = bookManager.mostRecentBook();
this.$nextTick(() => {
this.$refs.input.focus();
});
}
rowKey(row) {
return row.key;
} }
updateTableData() { updateTableData() {
@@ -172,13 +186,15 @@ class HistoryPage extends Vue {
} }
const search = this.search; const search = this.search;
this.tableData = result.filter(item => { result = result.filter(item => {
return !search || return !search ||
item.touchTime.includes(search) || item.touchTime.includes(search) ||
item.touchDate.includes(search) || item.touchDate.includes(search) ||
item.desc.title.toLowerCase().includes(search.toLowerCase()) || item.desc.title.toLowerCase().includes(search.toLowerCase()) ||
item.desc.author.toLowerCase().includes(search.toLowerCase()) item.desc.author.toLowerCase().includes(search.toLowerCase())
}); });
this.tableData = result;
} }
headerCellStyle(cell) { headerCellStyle(cell) {

View File

@@ -69,7 +69,7 @@
@stop-text-search="stopTextSearch"> @stop-text-search="stopTextSearch">
</SearchPage> </SearchPage>
<CopyTextPage v-if="copyTextActive" ref="copyTextPage" @copy-text-toggle="copyTextToggle"></CopyTextPage> <CopyTextPage v-if="copyTextActive" ref="copyTextPage" @copy-text-toggle="copyTextToggle"></CopyTextPage>
<HistoryPage v-if="historyActive" ref="historyPage" @load-book="loadBook" @history-toggle="historyToggle"></HistoryPage> <HistoryPage v-show="historyActive" ref="historyPage" @load-book="loadBook" @history-toggle="historyToggle"></HistoryPage>
<SettingsPage v-if="settingsActive" ref="settingsPage" @settings-toggle="settingsToggle"></SettingsPage> <SettingsPage v-if="settingsActive" ref="settingsPage" @settings-toggle="settingsToggle"></SettingsPage>
<HelpPage v-if="helpActive" ref="helpPage" @help-toggle="helpToggle"></HelpPage> <HelpPage v-if="helpActive" ref="helpPage" @help-toggle="helpToggle"></HelpPage>
<ClickMapPage v-show="clickMapActive" ref="clickMapPage"></ClickMapPage> <ClickMapPage v-show="clickMapActive" ref="clickMapPage"></ClickMapPage>
@@ -415,6 +415,7 @@ class Reader extends Vue {
this.historyActive = !this.historyActive; this.historyActive = !this.historyActive;
if (this.historyActive) { if (this.historyActive) {
this.closeAllTextPages(); this.closeAllTextPages();
this.$refs.historyPage.init();
this.historyActive = true; this.historyActive = true;
} else { } else {
this.historyActive = false; this.historyActive = false;
@@ -831,6 +832,8 @@ class Reader extends Vue {
break; break;
case 'KeyX': case 'KeyX':
this.historyToggle(); this.historyToggle();
event.preventDefault();
event.stopPropagation();
break; break;
case 'KeyS': case 'KeyS':
this.settingsToggle(); this.settingsToggle();

View File

@@ -185,7 +185,7 @@
<el-checkbox v-model="wordWrap">Перенос по слогам</el-checkbox> <el-checkbox v-model="wordWrap">Перенос по слогам</el-checkbox>
</el-form-item> </el-form-item>
<el-form-item label="Обработка"> <el-form-item label="Обработка">
<el-checkbox v-model="cutEmptyParagraphs">Убирать пустые параграфы</el-checkbox> <el-checkbox v-model="cutEmptyParagraphs">Убирать пустые строки</el-checkbox>
</el-form-item> </el-form-item>
<el-form-item label=""> <el-form-item label="">
<el-col :span="12"> <el-col :span="12">
@@ -212,7 +212,12 @@
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item label=""> <el-form-item label="">
Размер изображения <el-checkbox v-model="imageFitWidth">Ширина не более размера экрана</el-checkbox>
</el-form-item>
<el-form-item label="">
<el-col :span="12">
Высота не более
</el-col>
<el-tooltip :open-delay="500" effect="light" placement="top"> <el-tooltip :open-delay="500" effect="light" placement="top">
<template slot="content"> <template slot="content">
Определяет высоту изображения количеством строк.<br> Определяет высоту изображения количеством строк.<br>
@@ -437,11 +442,12 @@ class SettingsPage extends Vue {
'#000000', '#000000',
'#202020', '#202020',
'#ebe2c9', '#ebe2c9',
'#cfdc99',
'#478355',
'#a6caf0',
'#909080', '#909080',
'#808080', '#808080',
'#c8c8c8', '#c8c8c8',
'#478355',
'#a6caf0',
]; ];
} }

View File

@@ -26,9 +26,9 @@ export default class DrawHelper {
const font = this.fontByStyle({}); const font = this.fontByStyle({});
const justify = (this.textAlignJustify ? 'text-align: justify; text-align-last: justify;' : ''); const justify = (this.textAlignJustify ? 'text-align: justify; text-align-last: justify;' : '');
let out = `<div style="width: ${this.w}px; height: ${this.h}px;` + let out = `<div style="width: ${this.w}px; height: ${this.h + (isScrolling ? this.lineHeight : 0)}px;` +
` position: absolute; top: ${this.fontSize*this.textShift}px; color: ${this.textColor}; font: ${font}; ${justify}` + ` position: absolute; top: ${this.fontSize*this.textShift}px; color: ${this.textColor}; font: ${font}; ${justify}` +
` line-height: ${this.lineHeight}px;">`; ` line-height: ${this.lineHeight}px; white-space: nowrap; overflow: hidden;">`;
let imageDrawn = new Set(); let imageDrawn = new Set();
let len = lines.length; let len = lines.length;
@@ -91,32 +91,27 @@ export default class DrawHelper {
} else } else
text = part.text; text = part.text;
if (text.trim() == '') if (text && text.trim() == '')
text = `<span style="white-space: pre">${text}</span>`; text = `<span style="white-space: pre">${text}</span>`;
lineText += `${tOpen}${text}${tClose}`; lineText += `${tOpen}${text}${tClose}`;
center = center || part.style.center; center = center || part.style.center;
space = (part.style.space > 0 ? part.style.space : space); space = (part.style.space > space ? part.style.space : space);
//избражения //избражения
//image: {local: Boolean, inline: Boolean, id: String, imageLine: Number, lineCount: Number, paraIndex: Number}, //image: {local: Boolean, inline: Boolean, id: String, imageLine: Number, lineCount: Number, paraIndex: Number, w: Number, h: Number},
const img = part.image; const img = part.image;
if (img && img.id && !img.inline && !imageDrawn.has(img.paraIndex)) { if (img && img.id && !img.inline && !imageDrawn.has(img.paraIndex)) {
const bin = this.parsed.binary[img.id]; const bin = this.parsed.binary[img.id];
if (bin) { if (bin) {
let imgH = img.lineCount*this.lineHeight;
imgH = (imgH <= bin.h ? imgH : bin.h);
let imgW = bin.w;
let resize = ''; let resize = '';
if (bin.h > imgH) { if (bin.h > img.h) {
resize = `height: ${imgH}px`; resize = `height: ${img.h}px`;
imgW = imgW*imgH/bin.h;
} }
const left = (this.w - imgW)/2; const left = (this.w - img.w)/2;
const top = ((img.lineCount*this.lineHeight - imgH)/2) + (i - img.imageLine)*this.lineHeight; const top = ((img.lineCount*this.lineHeight - img.h)/2) + (i - img.imageLine)*this.lineHeight;
if (img.local) { if (img.local) {
lineText += `<img src="data:${bin.type};base64,${bin.data}" style="position: absolute; left: ${left}px; top: ${top}px; ${resize}"/>`; lineText += `<img src="data:${bin.type};base64,${bin.data}" style="position: absolute; left: ${left}px; top: ${top}px; ${resize}"/>`;
} else { } else {

View File

@@ -221,6 +221,7 @@ class TextPage extends Vue {
this.parsed.showImages = this.showImages; this.parsed.showImages = this.showImages;
this.parsed.showInlineImagesInCenter = this.showInlineImagesInCenter; this.parsed.showInlineImagesInCenter = this.showInlineImagesInCenter;
this.parsed.imageHeightLines = this.imageHeightLines; this.parsed.imageHeightLines = this.imageHeightLines;
this.parsed.imageFitWidth = this.imageFitWidth;
} }
//statusBar //statusBar

View File

@@ -45,6 +45,9 @@ export default class BookParser {
let italic = false; let italic = false;
let space = 0; let space = 0;
let inPara = false; let inPara = false;
let isFirstBody = true;
let isFirstSection = true;
let isFirstTitlePara = false;
this.binary = {}; this.binary = {};
let binaryId = ''; let binaryId = '';
@@ -185,7 +188,7 @@ export default class BookParser {
if (attrs.href && attrs.href.value) { if (attrs.href && attrs.href.value) {
const href = attrs.href.value; const href = attrs.href.value;
if (href[0] == '#') {//local if (href[0] == '#') {//local
if (inPara && !this.showInlineImagesInCenter) if (inPara && !this.showInlineImagesInCenter && !center)
growParagraph(`<image-inline href="${href}"></image-inline>`, 0); growParagraph(`<image-inline href="${href}"></image-inline>`, 0);
else else
newParagraph(`<image href="${href}">${' '.repeat(maxImageLineCount)}</image>`, maxImageLineCount); newParagraph(`<image href="${href}">${' '.repeat(maxImageLineCount)}</image>`, maxImageLineCount);
@@ -199,14 +202,23 @@ export default class BookParser {
} }
if (path.indexOf('/fictionbook/body') == 0) { if (path.indexOf('/fictionbook/body') == 0) {
if (tag == 'body') {
if (!isFirstBody)
newParagraph(' ', 1);
isFirstBody = false;
}
if (tag == 'title') { if (tag == 'title') {
newParagraph(' ', 1); newParagraph(' ', 1);
isFirstTitlePara = true;
bold = true; bold = true;
center = true; center = true;
} }
if (tag == 'section') { if (tag == 'section') {
newParagraph(' ', 1); if (!isFirstSection)
newParagraph(' ', 1);
isFirstSection = false;
} }
if (tag == 'emphasis' || tag == 'strong') { if (tag == 'emphasis' || tag == 'strong') {
@@ -214,13 +226,17 @@ export default class BookParser {
} }
if ((tag == 'p' || tag == 'empty-line' || tag == 'v')) { if ((tag == 'p' || tag == 'empty-line' || tag == 'v')) {
newParagraph(' ', 1); if (!(tag == 'p' && isFirstTitlePara))
if (tag == 'p') newParagraph(' ', 1);
if (tag == 'p') {
inPara = true; inPara = true;
isFirstTitlePara = false;
}
} }
if (tag == 'subtitle') { if (tag == 'subtitle') {
newParagraph(' ', 1); newParagraph(' ', 1);
isFirstTitlePara = true;
bold = true; bold = true;
} }
@@ -248,6 +264,7 @@ export default class BookParser {
if (path.indexOf('/fictionbook/body') == 0) { if (path.indexOf('/fictionbook/body') == 0) {
if (tag == 'title') { if (tag == 'title') {
isFirstTitlePara = false;
bold = false; bold = false;
center = false; center = false;
} }
@@ -261,6 +278,7 @@ export default class BookParser {
} }
if (tag == 'subtitle') { if (tag == 'subtitle') {
isFirstTitlePara = false;
bold = false; bold = false;
} }
@@ -293,10 +311,10 @@ export default class BookParser {
text = text.replace(/>/g, '&gt;'); text = text.replace(/>/g, '&gt;');
text = text.replace(/</g, '&lt;'); text = text.replace(/</g, '&lt;');
if (text != ' ' && text.trim() == '') if (text && text.trim() == '')
text = text.trim(); text = (text.indexOf(' ') >= 0 ? ' ' : '');
if (text == '') if (!text)
return; return;
text = text.replace(/[\t\n\r]/g, ' '); text = text.replace(/[\t\n\r]/g, ' ');
@@ -348,13 +366,7 @@ export default class BookParser {
} }
if (path.indexOf('/fictionbook/body/section') == 0) { if (path.indexOf('/fictionbook/body/section') == 0) {
switch (tag) { growParagraph(`${tOpen}${text}${tClose}`, text.length);
case 'p':
growParagraph(`${tOpen}${text}${tClose}`, text.length);
break;
default:
growParagraph(`${tOpen}${text}${tClose}`, text.length);
}
} }
if (binaryId) { if (binaryId) {
@@ -597,7 +609,8 @@ export default class BookParser {
para.parsed.cutEmptyParagraphs === this.cutEmptyParagraphs && para.parsed.cutEmptyParagraphs === this.cutEmptyParagraphs &&
para.parsed.addEmptyParagraphs === this.addEmptyParagraphs && para.parsed.addEmptyParagraphs === this.addEmptyParagraphs &&
para.parsed.showImages === this.showImages && para.parsed.showImages === this.showImages &&
para.parsed.imageHeightLines === this.imageHeightLines para.parsed.imageHeightLines === this.imageHeightLines &&
para.parsed.imageFitWidth === this.imageFitWidth
) )
return para.parsed; return para.parsed;
@@ -611,6 +624,7 @@ export default class BookParser {
addEmptyParagraphs: this.addEmptyParagraphs, addEmptyParagraphs: this.addEmptyParagraphs,
showImages: this.showImages, showImages: this.showImages,
imageHeightLines: this.imageHeightLines, imageHeightLines: this.imageHeightLines,
imageFitWidth: this.imageFitWidth,
visible: !( visible: !(
(this.cutEmptyParagraphs && para.cut) || (this.cutEmptyParagraphs && para.cut) ||
(para.addIndex > this.addEmptyParagraphs) (para.addIndex > this.addEmptyParagraphs)
@@ -626,7 +640,7 @@ export default class BookParser {
last: Boolean, last: Boolean,
parts: array of { parts: array of {
style: {bold: Boolean, italic: Boolean, center: Boolean}, style: {bold: Boolean, italic: Boolean, center: Boolean},
image: {local: Boolean, inline: Boolean, id: String, imageLine: Number, lineCount: Number, paraIndex: Number}, image: {local: Boolean, inline: Boolean, id: String, imageLine: Number, lineCount: Number, paraIndex: Number, w: Number, h: Number},
text: String, text: String,
} }
}*/ }*/
@@ -654,8 +668,21 @@ export default class BookParser {
bin = {h: 0, w: 0}; bin = {h: 0, w: 0};
let lineCount = this.imageHeightLines; let lineCount = this.imageHeightLines;
const c = Math.ceil(bin.h/this.lineHeight); let c = Math.ceil(bin.h/this.lineHeight);
const maxH = lineCount*this.lineHeight;
let maxH2 = maxH;
if (this.imageFitWidth && bin.w > this.w) {
maxH2 = bin.h*this.w/bin.w;
c = Math.ceil(maxH2/this.lineHeight);
}
lineCount = (c < lineCount ? c : lineCount); lineCount = (c < lineCount ? c : lineCount);
let imageHeight = (maxH2 < maxH ? maxH2 : maxH);
imageHeight = (imageHeight <= bin.h ? imageHeight : bin.h);
let imageWidth = (bin.h > imageHeight ? bin.w*imageHeight/bin.h : bin.w);
let i = 0; let i = 0;
for (; i < lineCount - 1; i++) { for (; i < lineCount - 1; i++) {
line.end = para.offset + ofs; line.end = para.offset + ofs;
@@ -667,7 +694,9 @@ export default class BookParser {
id: part.image.id, id: part.image.id,
imageLine: i, imageLine: i,
lineCount, lineCount,
paraIndex paraIndex,
w: imageWidth,
h: imageHeight,
}}); }});
lines.push(line); lines.push(line);
line = {begin: line.end + 1, parts: []}; line = {begin: line.end + 1, parts: []};
@@ -677,7 +706,9 @@ export default class BookParser {
line.first = (j == 0); line.first = (j == 0);
line.last = true; line.last = true;
line.parts.push({style, text: ' ', line.parts.push({style, text: ' ',
image: {local: part.image.local, inline: false, id: part.image.id, imageLine: i, lineCount, paraIndex}}); image: {local: part.image.local, inline: false, id: part.image.id,
imageLine: i, lineCount, paraIndex, w: imageWidth, h: imageHeight}
});
continue; continue;
} }

View File

@@ -164,6 +164,7 @@ const settingDefaults = {
showImages: true, showImages: true,
showInlineImagesInCenter: false, showInlineImagesInCenter: false,
imageHeightLines: 100, imageHeightLines: 100,
imageFitWidth: true,
fontShifts: {}, fontShifts: {},
}; };

View File

@@ -1,6 +1,6 @@
{ {
"name": "Liberama", "name": "Liberama",
"version": "0.4.2", "version": "0.4.6",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },

View File

@@ -10,6 +10,7 @@ const FileDetector = require('../FileDetector');
const repSpaces = (text) => text.replace(/&nbsp;|[\t\n\r]/g, ' '); const repSpaces = (text) => text.replace(/&nbsp;|[\t\n\r]/g, ' ');
const repSpaces2 = (text) => text.replace(/[\n\r]/g, ''); const repSpaces2 = (text) => text.replace(/[\n\r]/g, '');
const repSpaces3 = (text) => text.replace(/&nbsp;/g, ' ');
class BookConverter { class BookConverter {
constructor() { constructor() {
@@ -66,7 +67,10 @@ class BookConverter {
} }
} }
return iconv.decode(data, selected); if (selected.toLowerCase() != 'utf-8')
return iconv.decode(data, selected);
else
return data;
} }
checkEncoding(data) { checkEncoding(data) {
@@ -108,19 +112,21 @@ class BookConverter {
}; };
const growParagraph = (text) => { const growParagraph = (text) => {
if (!pars.length)
newParagraph();
const l = pars.length; const l = pars.length;
if (l) { if (pars[l - 1]._t == '')
if (pars[l - 1]._t == '') text = text.trimLeft();
text = text.trimLeft(); pars[l - 1]._t += text;
pars[l - 1]._t += text;
}
//посчитаем отступы у текста, чтобы выделить потом параграфы //посчитаем отступы у текста, чтобы выделить потом параграфы
const lines = text.split('\n'); const lines = text.split('\n');
for (const line of lines) { for (let line of lines) {
const sp = line.split(' '); line = repSpaces2(line).replace(/\t/g, ' ');
let l = 0; let l = 0;
while (l < sp.length && sp[l].trim() == '') { while (l < line.length && line[l] == ' ') {
l++; l++;
} }
if (!spaceCounter[l]) if (!spaceCounter[l])
@@ -129,7 +135,6 @@ class BookConverter {
} }
}; };
newParagraph();
const newPara = new Set(['tr', 'br', 'br/', 'dd', 'p', 'title', '/title', 'h1', 'h2', 'h3', '/h1', '/h2', '/h3']); const newPara = new Set(['tr', 'br', 'br/', 'dd', 'p', 'title', '/title', 'h1', 'h2', 'h3', '/h1', '/h2', '/h3']);
const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
@@ -183,22 +188,28 @@ class BookConverter {
}; };
const growPar = (text) => { const growPar = (text) => {
if (!newPars.length)
newPar();
const l = newPars.length; const l = newPars.length;
if (l) { newPars[l - 1]._t += text;
newPars[l - 1]._t += text;
}
} }
i = 0;
for (const par of pars) { for (const par of pars) {
newPar(); if (i > 0)
newPar();
i++;
const lines = par._t.split('\n'); const lines = par._t.split('\n');
for (const line of lines) { for (let line of lines) {
const sp = line.split(' '); line = repSpaces2(line).replace(/\t/g, ' ');
let l = 0; let l = 0;
while (l < sp.length && sp[l].trim() == '') { while (l < line.length && line[l] == ' ') {
l++; l++;
} }
if (l >= parIndent) if (l >= parIndent)
newPar(); newPar();
growPar(line.trim() + ' '); growPar(line.trim() + ' ');
@@ -227,6 +238,7 @@ class BookConverter {
let inSubtitle = false; let inSubtitle = false;
let inJustify = true; let inJustify = true;
let inImage = false; let inImage = false;
let isFirstPara = false;
let path = ''; let path = '';
let tag = '';// eslint-disable-line no-unused-vars let tag = '';// eslint-disable-line no-unused-vars
@@ -258,13 +270,17 @@ class BookConverter {
}; };
const growParagraph = (text) => { const growParagraph = (text) => {
if (!node._p) {
if (text.trim() != '')
openTag('p');
else
return;
}
if (node._n == 'p' && node._a.length == 0) if (node._n == 'p' && node._a.length == 0)
text = text.trimLeft(); text = text.trimLeft();
node._a.push({_t: text}); node._a.push({_t: text});
}; };
openTag('p');
const onStartNode = (elemName, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars const onStartNode = (elemName, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
if (elemName == '') if (elemName == '')
return; return;
@@ -272,18 +288,25 @@ class BookConverter {
path += '/' + elemName; path += '/' + elemName;
tag = elemName; tag = elemName;
} else { } else {
if (inPara && elemName != 'i' && elemName != 'b' && elemName != 'em' && elemName != 'strong' && elemName != 'img')
closeTag('p');
switch (elemName) { switch (elemName) {
case 'li': case 'li':
case 'p': case 'p':
case 'dd': case 'dd':
case 'br':
if (!(inSubtitle && isFirstPara)) {
if (inPara)
closeTag('p');
openTag('p');
}
isFirstPara = false;
break;
case 'h1': case 'h1':
case 'h2': case 'h2':
case 'h3': case 'h3':
case 'br': if (inPara)
closeTag('p');
openTag('p'); openTag('p');
bold = true;
break; break;
case 'i': case 'i':
case 'em': case 'em':
@@ -294,9 +317,12 @@ class BookConverter {
bold = true; bold = true;
break; break;
case 'div': case 'div':
if (inPara)
closeTag('p');
if (tail.indexOf('align="center"') >= 0) { if (tail.indexOf('align="center"') >= 0) {
openTag('subtitle'); openTag('subtitle');
inSubtitle = true; inSubtitle = true;
isFirstPara = true;
} }
if (tail.indexOf('align="justify"') >= 0) { if (tail.indexOf('align="justify"') >= 0) {
@@ -306,6 +332,8 @@ class BookConverter {
break; break;
case 'img': { case 'img': {
if (inPara)
closeTag('p');
const attrs = sax.getAttrsSync(tail); const attrs = sax.getAttrsSync(tail);
if (attrs.src && attrs.src.value) { if (attrs.src && attrs.src.value) {
let href = attrs.src.value; let href = attrs.src.value;
@@ -341,10 +369,13 @@ class BookConverter {
case 'li': case 'li':
case 'p': case 'p':
case 'dd': case 'dd':
closeTag('p');
break;
case 'h1': case 'h1':
case 'h2': case 'h2':
case 'h3': case 'h3':
closeTag('p'); closeTag('p');
bold = false;
break; break;
case 'i': case 'i':
case 'em': case 'em':
@@ -358,6 +389,7 @@ class BookConverter {
if (inSubtitle) { if (inSubtitle) {
closeTag('subtitle'); closeTag('subtitle');
inSubtitle = false; inSubtitle = false;
isFirstPara = false;
} }
if (inJustify) { if (inJustify) {
@@ -384,10 +416,10 @@ class BookConverter {
}; };
const onTextNode = (text) => {// eslint-disable-line no-unused-vars const onTextNode = (text) => {// eslint-disable-line no-unused-vars
if (text != ' ' && text.trim() == '') if (text && text.trim() == '')
text = text.trim(); text = (text.indexOf(' ') >= 0 ? ' ' : '');
if (text == '') if (!text)
return; return;
switch (path) { switch (path) {
@@ -416,7 +448,7 @@ class BookConverter {
growParagraph(`${tOpen}${text}${tClose}`); growParagraph(`${tOpen}${text}${tClose}`);
}; };
sax.parseSync(repSpaces(repSpaces2(this.decode(data).toString())), { sax.parseSync(repSpaces3(this.decode(data).toString()), {
onStartNode, onEndNode, onTextNode, onComment, onStartNode, onEndNode, onTextNode, onComment,
innerCut: new Set(['head', 'script', 'style']) innerCut: new Set(['head', 'script', 'style'])
}); });
@@ -474,21 +506,29 @@ class BookConverter {
} }
} }
let tOpen = '';
let tBody = '';
let tClose = '';
if (name) if (name)
out += `<${name}${attrs}>`; tOpen += `<${name}${attrs}>`;
if (node.hasOwnProperty('_t')) if (node.hasOwnProperty('_t'))
out += repSpaces(node._t); tBody += repSpaces(node._t);
for (let nodeName in node) { for (let nodeName in node) {
if (nodeName && nodeName[0] == '_' && nodeName != '_a') if (nodeName && nodeName[0] == '_' && nodeName != '_a')
continue; continue;
const n = node[nodeName]; const n = node[nodeName];
out += this.formatFb2Node(n, nodeName); tBody += this.formatFb2Node(n, nodeName);
} }
if (name) if (name)
out += `</${name}>`; tClose += `</${name}>`;
if (attrs == '' && name == 'p' && tBody.trim() == '')
out += '<empty-line/>'
else
out += `${tOpen}${tBody}${tClose}`;
} }
return out; return out;
} }

View File

@@ -1,4 +1,4 @@
function getEncoding(buf) { function getEncoding(buf, returnAll) {
const lowerCase = 3; const lowerCase = 3;
const upperCase = 1; const upperCase = 1;
@@ -8,6 +8,7 @@ function getEncoding(buf) {
'd': 'cp866', 'd': 'cp866',
'i': 'ISO-8859-5', 'i': 'ISO-8859-5',
'm': 'maccyrillic', 'm': 'maccyrillic',
'u': 'utf-8',
}; };
let charsets = { let charsets = {
@@ -15,38 +16,47 @@ function getEncoding(buf) {
'w': 0, 'w': 0,
'd': 0, 'd': 0,
'i': 0, 'i': 0,
'm': 0 'm': 0,
'u': 0,
}; };
const len = buf.length; const len = buf.length;
const blockSize = (len > 5*3000 ? 3000 : len); const blockSize = (len > 5*3000 ? 3000 : len);
let counter = 0; let counter = 0;
let i = 0; let i = 0;
let totalChecked = 0;
while (i < len) { while (i < len) {
const char = buf[i]; const char = buf[i];
const nextChar = (i < len - 1 ? buf[i + 1] : 0);
totalChecked++;
i++; i++;
//non-russian characters //non-russian characters
if (char < 128 || char > 256) if (char < 128 || char > 256)
continue; continue;
//CP866 //UTF-8
if ((char > 159 && char < 176) || (char > 223 && char < 242)) charsets['d'] += lowerCase; if ((char == 208 || char == 209) && nextChar >= 128 && nextChar <= 190)
if ((char > 127 && char < 160)) charsets['d'] += upperCase; charsets['u'] += lowerCase;
else {
//CP866
if ((char > 159 && char < 176) || (char > 223 && char < 242)) charsets['d'] += lowerCase;
if ((char > 127 && char < 160)) charsets['d'] += upperCase;
//KOI8-R //KOI8-R
if ((char > 191 && char < 223)) charsets['k'] += lowerCase; if ((char > 191 && char < 223)) charsets['k'] += lowerCase;
if ((char > 222 && char < 256)) charsets['k'] += upperCase; if ((char > 222 && char < 256)) charsets['k'] += upperCase;
//WIN-1251 //WIN-1251
if (char > 223 && char < 256) charsets['w'] += lowerCase; if (char > 223 && char < 256) charsets['w'] += lowerCase;
if (char > 191 && char < 224) charsets['w'] += upperCase; if (char > 191 && char < 224) charsets['w'] += upperCase;
//MAC //MAC
if (char > 221 && char < 255) charsets['m'] += lowerCase; if (char > 221 && char < 255) charsets['m'] += lowerCase;
if (char > 127 && char < 160) charsets['m'] += upperCase; if (char > 127 && char < 160) charsets['m'] += upperCase;
//ISO-8859-5 //ISO-8859-5
if (char > 207 && char < 240) charsets['i'] += lowerCase; if (char > 207 && char < 240) charsets['i'] += lowerCase;
if (char > 175 && char < 208) charsets['i'] += upperCase; if (char > 175 && char < 208) charsets['i'] += upperCase;
}
counter++; counter++;
@@ -57,18 +67,24 @@ function getEncoding(buf) {
} }
let sorted = Object.keys(charsets).map(function(key) { let sorted = Object.keys(charsets).map(function(key) {
return { codePage: codePage[key], c: charsets[key] }; return { codePage: codePage[key], c: charsets[key], totalChecked };
}); });
sorted.sort((a, b) => b.c - a.c); sorted.sort((a, b) => b.c - a.c);
if (sorted[0].c > 0) if (returnAll)
return sorted;
else if (sorted[0].c > 0)
return sorted[0].codePage; return sorted[0].codePage;
else else
return 'ISO-8859-5'; return 'ISO-8859-5';
} }
function checkIfText(buf) { function checkIfText(buf) {
const enc = getEncoding(buf, true);
if (enc[0].c > enc[0].totalChecked*0.9)
return true;
let spaceCount = 0; let spaceCount = 0;
let crCount = 0; let crCount = 0;
let lfCount = 0; let lfCount = 0;

View File

@@ -31,6 +31,7 @@ async function init() {
} }
async function main() { async function main() {
log(`${config.name} v${config.version}`);
log('Initializing'); log('Initializing');
await init(); await init();