瀏覽代碼

Merge branch 'release/0.5.4'

Book Pauk 6 年之前
父節點
當前提交
89bf907613

+ 2 - 2
client/components/Reader/Reader.vue

@@ -340,8 +340,8 @@ class Reader extends Vue {
             this.setPositionActive = true;
             this.setPositionActive = true;
 
 
             this.$nextTick(() => {
             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 {
         } else {
             this.setPositionActive = false;
             this.setPositionActive = false;

+ 9 - 2
client/components/Reader/SetPositionPage/SetPositionPage.vue

@@ -18,7 +18,6 @@
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 import Vue from 'vue';
 import Vue from 'vue';
 import Component from 'vue-class-component';
 import Component from 'vue-class-component';
-import _ from 'lodash';
 
 
 import Window from '../../share/Window.vue';
 import Window from '../../share/Window.vue';
 
 
@@ -28,7 +27,8 @@ export default @Component({
     },
     },
     watch: {
     watch: {
         sliderValue: function(newValue) {
         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() {
     created() {
         this.commit = this.$store.commit;
         this.commit = this.$store.commit;
         this.reader = this.$store.state.reader;
         this.reader = this.$store.state.reader;
+        this.initialized = false;
+    }
+
+    init(sliderValue, sliderMax) {
+        this.sliderMax = sliderMax;
+        this.sliderValue = sliderValue;
+        this.initialized = true;
     }
     }
 
 
     formatTooltip(val) {
     formatTooltip(val) {

+ 4 - 3
client/components/Reader/share/bookManager.js

@@ -25,6 +25,7 @@ class BookManager {
     async init(settings) {
     async init(settings) {
         this.settings = settings;
         this.settings = settings;
 
 
+        //bmCacheStore нужен только для ускорения загрузки читалки
         this.booksCached = await bmCacheStore.getItem('books');
         this.booksCached = await bmCacheStore.getItem('books');
         if (!this.booksCached)
         if (!this.booksCached)
             this.booksCached = {};
             this.booksCached = {};
@@ -47,9 +48,9 @@ class BookManager {
         }
         }
     }
     }
 
 
-    //долгая загрузка из хранилища
-    //bmMetaStore и bmRecentStore в будущем можно будет убрать
-    //bmCacheStore достаточно
+    //долгая загрузка из хранилища,
+    //хранение в отдельных записях дает относительно
+    //нормальное поведение при нескольких вкладках с читалкой в браузере
     async loadMeta(immediate) {
     async loadMeta(immediate) {
         if (!immediate)
         if (!immediate)
             await utils.sleep(2000);
             await utils.sleep(2000);

+ 1 - 1
package.json

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

+ 77 - 6
server/core/BookConverter/ConvertHtml.js

@@ -34,10 +34,15 @@ class ConvertHtml extends ConvertBase {
         let desc = {_n: 'description', 'title-info': titleInfo};
         let desc = {_n: 'description', 'title-info': titleInfo};
         let pars = [];
         let pars = [];
         let body = {_n: 'body', section: {_a: []}};
         let body = {_n: 'body', section: {_a: []}};
-        let fb2 = [desc, body];
+        let binary = [];
+        let fb2 = [desc, body, binary];
 
 
         let title = '';
         let title = '';
         let inTitle = false;
         let inTitle = false;
+        let inImage = false;
+        let image = {};
+        let bold = false;
+        let italic = false;
 
 
         let spaceCounter = [];
         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
         const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
             if (!cutCounter && !(cutTitle && inTitle)) {
             if (!cutCounter && !(cutTitle && inTitle)) {
-                growParagraph(text);
+                let tOpen = (bold ? '<strong>' : '');
+                tOpen += (italic ? '<emphasis>' : '');
+                let tClose = (italic ? '</emphasis>' : '');
+                tClose += (bold ? '</strong>' : '');
+
+                growParagraph(`${tOpen}${text}${tClose}`);
             }
             }
 
 
             if (inTitle && !title)
             if (inTitle && !title)
                 title = text;
                 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
         const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
             if (!cutCounter) {
             if (!cutCounter) {
                 if (newPara.has(tag))
                 if (newPara.has(tag))
                     newParagraph();
                     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')
             if (tag == 'title')
                 inTitle = true;
                 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
         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')
             if (tag == 'title')
                 inTitle = false;
                 inTitle = false;
+
+            if (tag == 'fb2-image')
+                inImage = false;
         };
         };
 
 
         let buf = this.decode(data).toString();
         let buf = this.decode(data).toString();
 
 
         sax.parseSync(buf, {
         sax.parseSync(buf, {
             onStartNode, onEndNode, onTextNode,
             onStartNode, onEndNode, onTextNode,
-            innerCut: new Set(['head', 'script', 'style', 'binary'])
+            innerCut: new Set(['head', 'script', 'style', 'binary', 'fb2-image'])
         });
         });
 
 
         titleInfo['book-title'] = title;
         titleInfo['book-title'] = title;
@@ -148,10 +209,16 @@ class ConvertHtml extends ConvertBase {
 
 
             i = 0;
             i = 0;
             for (const par of pars) {
             for (const par of pars) {
+                if (par._n != 'p') {
+                    newPars.push(par);
+                    continue;
+                }
+
                 if (i > 0)
                 if (i > 0)
                     newPar();
                     newPar();
                 i++;
                 i++;
 
 
+                let j = 0;
                 const lines = par._t.split('\n');
                 const lines = par._t.split('\n');
                 for (let line of lines) {
                 for (let line of lines) {
                     line = repCrLfTab(line);
                     line = repCrLfTab(line);
@@ -161,8 +228,11 @@ class ConvertHtml extends ConvertBase {
                         l++;
                         l++;
                     }
                     }
 
 
-                    if (l >= parIndent)
-                        newPar();
+                    if (l >= parIndent) {
+                        if (j > 0)
+                            newPar();
+                        j++;
+                    }
                     growPar(line.trim() + ' ');
                     growPar(line.trim() + ' ');
                 }
                 }
             }
             }
@@ -173,6 +243,7 @@ class ConvertHtml extends ConvertBase {
         }
         }
 
 
         //убираем лишнее
         //убираем лишнее
+        pars = body.section._a[0];
         for (let i = 0; i < pars.length; i++)
         for (let i = 0; i < pars.length; i++)
             pars[i]._t = this.repSpaces(pars[i]._t).trim();
             pars[i]._t = this.repSpaces(pars[i]._t).trim();
 
 

+ 91 - 2
server/core/BookConverter/ConvertPdf.js

@@ -1,4 +1,5 @@
 const fs = require('fs-extra');
 const fs = require('fs-extra');
+const path = require('path');
 
 
 const sax = require('./sax');
 const sax = require('./sax');
 const utils = require('../utils');
 const utils = require('../utils');
@@ -34,14 +35,47 @@ class ConvertPdf extends ConvertHtml {
 
 
         //парсим xml
         //парсим xml
         let lines = [];
         let lines = [];
+        let images = [];
+        let loading = [];
         let inText = false;
         let inText = false;
+        let bold = false;
+        let italic = false;
         let title = '';
         let title = '';
         let prevTop = 0;
         let prevTop = 0;
         let i = -1;
         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
         const onTextNode = (text, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
             if (!cutCounter && inText) {
             if (!cutCounter && inText) {
-                lines[i].text += text + ' ';
+                let tOpen = (bold ? '<b>' : '');
+                tOpen += (italic ? '<i>' : '');
+                let tClose = (italic ? '</i>' : '');
+                tClose += (bold ? '</b>' : '');
+
+                lines[i].text += `${tOpen}${text}${tClose} `;
                 if (i < 2)
                 if (i < 2)
                     title += text + ' ';
                     title += text + ' ';
             }
             }
@@ -49,6 +83,17 @@ class ConvertPdf extends ConvertHtml {
 
 
         const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
         const onStartNode = (tag, tail, singleTag, cutCounter, cutTag) => {// eslint-disable-line no-unused-vars
             if (!cutCounter) {
             if (!cutCounter) {
+                if (inText) {
+                    switch (tag) {
+                        case 'i':
+                            italic = true;
+                            break;
+                        case 'b':
+                            bold = true;
+                            break;
+                    }
+                }
+
                 if (tag == 'text' && !inText) {
                 if (tag == 'text' && !inText) {
                     let attrs = sax.getAttrsSync(tail);
                     let attrs = sax.getAttrsSync(tail);
                     const line = {
                     const line = {
@@ -59,19 +104,52 @@ class ConvertPdf extends ConvertHtml {
                         height: parseInt((attrs.height && attrs.height.value ? attrs.height.value : null), 10),
                         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;
                         inText = true;
                         if (isNaN(line.top) || isNaN(prevTop) || (Math.abs(prevTop - line.top) > 3)) {
                         if (isNaN(line.top) || isNaN(prevTop) || (Math.abs(prevTop - line.top) > 3)) {
+                            putImage(line.top);
                             i++;
                             i++;
                             lines[i] = line;
                             lines[i] = line;
                         }
                         }
                         prevTop = line.top;
                         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
         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')
             if (tag == 'text')
                 inText = false;
                 inText = false;
         };
         };
@@ -81,9 +159,15 @@ class ConvertPdf extends ConvertHtml {
             onStartNode, onEndNode, onTextNode
             onStartNode, onEndNode, onTextNode
         });
         });
 
 
+        putImage(100000);
+
+        await Promise.all(loading);
+
         //найдем параграфы и отступы
         //найдем параграфы и отступы
         const indents = [];
         const indents = [];
         for (const line of lines) {
         for (const line of lines) {
+            if (line.isImage)
+                continue;
             if (!isNaN(line.left)) {
             if (!isNaN(line.left)) {
                 indents[line.left] = 1;
                 indents[line.left] = 1;
             }
             }
@@ -103,6 +187,11 @@ class ConvertPdf extends ConvertHtml {
         let concat = '';
         let concat = '';
         let sp = '';
         let sp = '';
         for (const line of lines) {
         for (const line of lines) {
+            if (line.isImage) {
+                text += `<fb2-image type="${line.type}" name="${line.name}">${line.data}</fb2-image>`;
+                continue;
+            }
+
             if (concat == '') {
             if (concat == '') {
                 const left = line.left || 0;
                 const left = line.left || 0;
                 sp = ' '.repeat(indents[left]);
                 sp = ' '.repeat(indents[left]);

+ 1 - 1
server/core/BookConverter/ConvertSamlib.js

@@ -135,7 +135,7 @@ class ConvertSamlib extends ConvertBase {
                             let href = attrs.src.value;
                             let href = attrs.src.value;
                             if (href[0] == '/')
                             if (href[0] == '/')
                                 href = `http://${hostname}${href}`;
                                 href = `http://${hostname}${href}`;
-                            openTag('image', {href});
+                            openTag('image', {'l:href': href});
                             inImage = true;
                             inImage = true;
                         }
                         }
                         break;
                         break;