diff --git a/client/components/Reader/Reader.vue b/client/components/Reader/Reader.vue
index dc14f10e..efb833a2 100644
--- a/client/components/Reader/Reader.vue
+++ b/client/components/Reader/Reader.vue
@@ -340,8 +340,8 @@ class Reader extends Vue {
this.setPositionActive = true;
this.$nextTick(() => {
- this.$refs.setPositionPage.sliderMax = this.mostRecentBook().textLength - 1;
- this.$refs.setPositionPage.sliderValue = this.mostRecentBook().bookPos;
+ const recent = this.mostRecentBook();
+ this.$refs.setPositionPage.init(recent.bookPos, recent.textLength - 1);
});
} else {
this.setPositionActive = false;
diff --git a/client/components/Reader/SetPositionPage/SetPositionPage.vue b/client/components/Reader/SetPositionPage/SetPositionPage.vue
index 52206a4a..b652241d 100644
--- a/client/components/Reader/SetPositionPage/SetPositionPage.vue
+++ b/client/components/Reader/SetPositionPage/SetPositionPage.vue
@@ -18,7 +18,6 @@
//-----------------------------------------------------------------------------
import Vue from 'vue';
import Component from 'vue-class-component';
-import _ from 'lodash';
import Window from '../../share/Window.vue';
@@ -28,7 +27,8 @@ export default @Component({
},
watch: {
sliderValue: function(newValue) {
- this.$emit('book-pos-changed', {bookPos: newValue});
+ if (this.initialized)
+ this.$emit('book-pos-changed', {bookPos: newValue});
},
},
})
@@ -39,6 +39,13 @@ class SetPositionPage extends Vue {
created() {
this.commit = this.$store.commit;
this.reader = this.$store.state.reader;
+ this.initialized = false;
+ }
+
+ init(sliderValue, sliderMax) {
+ this.sliderMax = sliderMax;
+ this.sliderValue = sliderValue;
+ this.initialized = true;
}
formatTooltip(val) {
diff --git a/client/components/Reader/share/bookManager.js b/client/components/Reader/share/bookManager.js
index a1bfa326..a4c9e3e5 100644
--- a/client/components/Reader/share/bookManager.js
+++ b/client/components/Reader/share/bookManager.js
@@ -25,6 +25,7 @@ class BookManager {
async init(settings) {
this.settings = settings;
+ //bmCacheStore нужен только для ускорения загрузки читалки
this.booksCached = await bmCacheStore.getItem('books');
if (!this.booksCached)
this.booksCached = {};
@@ -47,9 +48,9 @@ class BookManager {
}
}
- //долгая загрузка из хранилища
- //bmMetaStore и bmRecentStore в будущем можно будет убрать
- //bmCacheStore достаточно
+ //долгая загрузка из хранилища,
+ //хранение в отдельных записях дает относительно
+ //нормальное поведение при нескольких вкладках с читалкой в браузере
async loadMeta(immediate) {
if (!immediate)
await utils.sleep(2000);
diff --git a/package.json b/package.json
index 021feee9..5e422b4c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "Liberama",
- "version": "0.5.3",
+ "version": "0.5.4",
"engines": {
"node": ">=10.0.0"
},
diff --git a/server/core/BookConverter/ConvertHtml.js b/server/core/BookConverter/ConvertHtml.js
index ce28f9d4..eb9f6e73 100644
--- a/server/core/BookConverter/ConvertHtml.js
+++ b/server/core/BookConverter/ConvertHtml.js
@@ -34,10 +34,15 @@ class ConvertHtml extends ConvertBase {
let desc = {_n: 'description', 'title-info': titleInfo};
let pars = [];
let body = {_n: 'body', section: {_a: []}};
- let fb2 = [desc, body];
+ let binary = [];
+ let fb2 = [desc, body, binary];
let title = '';
let inTitle = false;
+ let inImage = false;
+ let image = {};
+ let bold = false;
+ let italic = false;
let spaceCounter = [];
@@ -71,37 +76,93 @@ class ConvertHtml extends ConvertBase {
}
};
- const newPara = new Set(['tr', 'br', 'br/', 'dd', 'p', 'title', '/title', 'h1', 'h2', 'h3', '/h1', '/h2', '/h3']);
+ const newPara = new Set(['tr', '/table', 'hr', 'br', 'br/', 'li', 'dt', 'dd', 'p', 'title', '/title', 'h1', 'h2', 'h3', '/h1', '/h2', '/h3']);
const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
if (!cutCounter && !(cutTitle && inTitle)) {
- growParagraph(text);
+ let tOpen = (bold ? '' : '');
+ tOpen += (italic ? '' : '');
+ let tClose = (italic ? '' : '');
+ tClose += (bold ? '' : '');
+
+ growParagraph(`${tOpen}${text}${tClose}`);
}
if (inTitle && !title)
title = text;
+
+ if (inImage) {
+ image._t = text;
+ binary.push(image);
+
+ pars.push({_n: 'image', _attrs: {'l:href': '#' + image._attrs.id}, _t: ''});
+ newParagraph();
+ }
+
};
const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
if (!cutCounter) {
if (newPara.has(tag))
newParagraph();
+
+ switch (tag) {
+ case 'i':
+ case 'em':
+ italic = true;
+ break;
+ case 'b':
+ case 'strong':
+ case 'h1':
+ case 'h2':
+ case 'h3':
+ bold = true;
+ break;
+ }
}
if (tag == 'title')
inTitle = true;
+
+ if (tag == 'fb2-image') {
+ inImage = true;
+ const attrs = sax.getAttrsSync(tail);
+ image = {_n: 'binary', _attrs: {id: attrs.name.value, 'content-type': attrs.type.value}, _t: ''};
+ }
};
const onEndNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
+ if (!cutCounter) {
+ if (newPara.has('/' + tag))
+ newParagraph();
+
+ switch (tag) {
+ case 'i':
+ case 'em':
+ italic = false;
+ break;
+ case 'b':
+ case 'strong':
+ case 'h1':
+ case 'h2':
+ case 'h3':
+ bold = false;
+ break;
+ }
+ }
+
if (tag == 'title')
inTitle = false;
+
+ if (tag == 'fb2-image')
+ inImage = false;
};
let buf = this.decode(data).toString();
sax.parseSync(buf, {
onStartNode, onEndNode, onTextNode,
- innerCut: new Set(['head', 'script', 'style', 'binary'])
+ innerCut: new Set(['head', 'script', 'style', 'binary', 'fb2-image'])
});
titleInfo['book-title'] = title;
@@ -148,10 +209,16 @@ class ConvertHtml extends ConvertBase {
i = 0;
for (const par of pars) {
+ if (par._n != 'p') {
+ newPars.push(par);
+ continue;
+ }
+
if (i > 0)
newPar();
i++;
+ let j = 0;
const lines = par._t.split('\n');
for (let line of lines) {
line = repCrLfTab(line);
@@ -161,8 +228,11 @@ class ConvertHtml extends ConvertBase {
l++;
}
- if (l >= parIndent)
- newPar();
+ if (l >= parIndent) {
+ if (j > 0)
+ newPar();
+ j++;
+ }
growPar(line.trim() + ' ');
}
}
@@ -173,6 +243,7 @@ class ConvertHtml extends ConvertBase {
}
//убираем лишнее
+ pars = body.section._a[0];
for (let i = 0; i < pars.length; i++)
pars[i]._t = this.repSpaces(pars[i]._t).trim();
diff --git a/server/core/BookConverter/ConvertPdf.js b/server/core/BookConverter/ConvertPdf.js
index 236d0a1a..967bcdaa 100644
--- a/server/core/BookConverter/ConvertPdf.js
+++ b/server/core/BookConverter/ConvertPdf.js
@@ -1,4 +1,5 @@
const fs = require('fs-extra');
+const path = require('path');
const sax = require('./sax');
const utils = require('../utils');
@@ -34,14 +35,47 @@ class ConvertPdf extends ConvertHtml {
//парсим xml
let lines = [];
+ let images = [];
+ let loading = [];
let inText = false;
+ let bold = false;
+ let italic = false;
let title = '';
let prevTop = 0;
let i = -1;
+ const loadImage = async(image) => {
+ const src = path.parse(image.src);
+ let type = 'unknown';
+ switch (src.ext) {
+ case '.jpg': type = 'image/jpeg'; break;
+ case '.png': type = 'image/png'; break;
+ }
+ if (type != 'unknown') {
+ image.data = (await fs.readFile(image.src)).toString('base64');
+ image.type = type;
+ image.name = src.base;
+ }
+ }
+
+ const putImage = (curTop) => {
+ if (!isNaN(curTop) && images.length) {
+ while (images.length && images[0].top < curTop) {
+ i++;
+ lines[i] = images[0];
+ images.shift();
+ }
+ }
+ }
+
const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
if (!cutCounter && inText) {
- lines[i].text += text + ' ';
+ let tOpen = (bold ? '' : '');
+ tOpen += (italic ? '' : '');
+ let tClose = (italic ? '' : '');
+ tClose += (bold ? '' : '');
+
+ lines[i].text += `${tOpen}${text}${tClose} `;
if (i < 2)
title += text + ' ';
}
@@ -49,6 +83,17 @@ class ConvertPdf extends ConvertHtml {
const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
if (!cutCounter) {
+ if (inText) {
+ switch (tag) {
+ case 'i':
+ italic = true;
+ break;
+ case 'b':
+ bold = true;
+ break;
+ }
+ }
+
if (tag == 'text' && !inText) {
let attrs = sax.getAttrsSync(tail);
const line = {
@@ -59,19 +104,52 @@ class ConvertPdf extends ConvertHtml {
height: parseInt((attrs.height && attrs.height.value ? attrs.height.value : null), 10),
};
- if (line.width !== '0' || line.height !== '0') {
+ if (line.width != 0 || line.height != 0) {
inText = true;
if (isNaN(line.top) || isNaN(prevTop) || (Math.abs(prevTop - line.top) > 3)) {
+ putImage(line.top);
i++;
lines[i] = line;
}
prevTop = line.top;
}
}
+
+ if (tag == 'image') {
+ const attrs = sax.getAttrsSync(tail);
+ const src = (attrs.src && attrs.src.value ? attrs.src.value : '');
+ if (src) {
+ const image = {
+ isImage: true,
+ src,
+ data: '',
+ type: '',
+ top: parseInt((attrs.top && attrs.top.value ? attrs.top.value : null), 10) || 0,
+ };
+ loading.push(loadImage(image));
+ images.push(image);
+ images.sort((a, b) => a.top - b.top)
+ }
+ }
+
+ if (tag == 'page') {
+ putImage(100000);
+ }
}
};
const onEndNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
+ if (inText) {
+ switch (tag) {
+ case 'i':
+ italic = false;
+ break;
+ case 'b':
+ bold = false;
+ break;
+ }
+ }
+
if (tag == 'text')
inText = false;
};
@@ -81,9 +159,15 @@ class ConvertPdf extends ConvertHtml {
onStartNode, onEndNode, onTextNode
});
+ putImage(100000);
+
+ await Promise.all(loading);
+
//найдем параграфы и отступы
const indents = [];
for (const line of lines) {
+ if (line.isImage)
+ continue;
if (!isNaN(line.left)) {
indents[line.left] = 1;
}
@@ -103,6 +187,11 @@ class ConvertPdf extends ConvertHtml {
let concat = '';
let sp = '';
for (const line of lines) {
+ if (line.isImage) {
+ text += `${line.data}`;
+ continue;
+ }
+
if (concat == '') {
const left = line.left || 0;
sp = ' '.repeat(indents[left]);
diff --git a/server/core/BookConverter/ConvertSamlib.js b/server/core/BookConverter/ConvertSamlib.js
index 474fe0a1..30eda273 100644
--- a/server/core/BookConverter/ConvertSamlib.js
+++ b/server/core/BookConverter/ConvertSamlib.js
@@ -135,7 +135,7 @@ class ConvertSamlib extends ConvertBase {
let href = attrs.src.value;
if (href[0] == '/')
href = `http://${hostname}${href}`;
- openTag('image', {href});
+ openTag('image', {'l:href': href});
inImage = true;
}
break;