Browse Source

Merge branch 'release/0.3.3'

Book Pauk 6 years ago
parent
commit
d8fddd4128

+ 1 - 1
client/components/Reader/HelpPage/HotkeysHelpPage/HotkeysHelpPage.vue

@@ -4,7 +4,7 @@
         <ul>
             <li><b>F1, H</b> - открыть справку</li>
             <li><b>Escape</b> - показать/скрыть страницу загрузки</li>
-            <li><b>Tab</b> - показать/скрыть панель управления</li>
+            <li><b>Tab, Q</b> - показать/скрыть панель управления</li>
             <li><b>PageUp, Left, Shift+Space, Backspace</b> - страницу назад</li>
             <li><b>PageDown, Right, Space</b> - страницу вперед</li>
             <li><b>Home</b> - в начало книги</li>

+ 7 - 0
client/components/Reader/LoaderPage/LoaderPage.vue

@@ -103,6 +103,13 @@ class LoaderPage extends Vue {
             event.stopPropagation();
             return true;
         }
+
+        if (event.type == 'keydown' && (document.activeElement !== input && event.code == 'KeyQ')) {
+            this.$emit('tool-bar-toggle');
+            event.preventDefault();
+            event.stopPropagation();
+            return true;
+        }
     }
 }
 //-----------------------------------------------------------------------------

+ 1 - 0
client/components/Reader/TextPage/TextPage.vue

@@ -930,6 +930,7 @@ class TextPage extends Vue {
                     this.$emit('full-screen-toogle');
                     break;
                 case 'Tab':
+                case 'KeyQ':
                     this.doToolBarToggle();
                     event.preventDefault();
                     event.stopPropagation();

+ 75 - 5
client/components/Reader/share/BookParser.js

@@ -2,6 +2,8 @@ import he from 'he';
 import sax from '../../../../server/core/BookConverter/sax';
 import {sleep} from '../../../share/utils';
 
+const maxImageLineCount = 100;
+
 export default class BookParser {
     constructor() {
         // defaults
@@ -37,6 +39,10 @@ export default class BookParser {
         let center = false;
         let bold = false;
         let italic = false;
+        this.binary = {};
+        let binaryId = '';
+        let binaryType = '';
+        let dimPromises = [];
 
         let paraIndex = -1;
         let paraOffset = 0;
@@ -50,6 +56,28 @@ export default class BookParser {
                 addIndex: Number, //индекс добавляемого пустого параграфа (addEmptyParagraphs)
             }
         */
+        const getImageDimensions = (binaryId, binaryType, data) => {
+            return new Promise (async(resolve, reject) => {
+                const i = new Image();
+                let resolved = false;
+                i.onload = () => {
+                    resolved = true;
+                    this.binary[binaryId] = {
+                        w: i.width,
+                        h: i.height,
+                        type: binaryType,
+                        data
+                    };
+                    resolve();
+                };
+
+                i.src = `data:${binaryType};base64,${data}`;
+                await sleep(30*1000);
+                if (!resolved)
+                    reject('Не удалось получить размер изображения');
+            });
+        };
+
         const newParagraph = (text, len, addIndex) => {
             paraIndex++;
             let p = {
@@ -103,13 +131,26 @@ export default class BookParser {
             paraOffset += p.length;
         };
 
-        const onStartNode = (elemName) => {// eslint-disable-line no-unused-vars
+        const onStartNode = (elemName, tail) => {// eslint-disable-line no-unused-vars
             if (elemName == '?xml')
                 return;
 
             tag = elemName;
             path += '/' + elemName;
 
+            if (tag == 'binary') {
+                let attrs = sax.getAttrsSync(tail);
+                binaryType = (attrs['content-type'].value ? attrs['content-type'].value : '');
+                if (binaryType == 'image/jpeg' || binaryType == 'image/png')
+                    binaryId = (attrs.id.value ? attrs.id.value : '');
+            }
+
+            if (tag == 'image') {
+                let attrs = sax.getAttrsSync(tail);
+                if (attrs.href.value)
+                    newParagraph(`<image href="${attrs.href.value}">${' '.repeat(maxImageLineCount)}</image>`, maxImageLineCount);
+            }
+
             if (path.indexOf('/fictionbook/body') == 0) {
                 if (tag == 'title') {
                     newParagraph(' ', 1);
@@ -146,6 +187,10 @@ export default class BookParser {
 
         const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars
             if (tag == elemName) {
+                if (tag == 'binary') {
+                    binaryId = '';
+                }
+            
                 if (path.indexOf('/fictionbook/body') == 0) {
                     if (tag == 'title') {
                         bold = false;
@@ -245,6 +290,10 @@ export default class BookParser {
                         growParagraph(`${tOpen}${text}${tClose}`, text.length);
                 }
             }
+
+            if (binaryId) {
+                dimPromises.push(getImageDimensions(binaryId, binaryType, text));
+            }
         };
 
         const onProgress = async(prog) => {
@@ -256,6 +305,14 @@ export default class BookParser {
             onStartNode, onEndNode, onTextNode, onProgress
         });
 
+        if (dimPromises.length) {
+            try {
+                await Promise.all(dimPromises);
+            } catch (e) {
+                //
+            }
+        }
+
         this.fb2 = fb2;
         this.para = para;
 
@@ -292,18 +349,26 @@ export default class BookParser {
     splitToStyle(s) {
         let result = [];/*array of {
             style: {bold: Boolean, italic: Boolean, center: Boolean},
+            image: Boolean,
+            imageId: String,
             text: String,
         }*/
         let style = {};
+        let image = {};
 
+                /*let attrs = sax.getAttrsSync(tail);
+                if (attrs.href.value)
+                    newParagraph(' '.repeat(maxImageLineCount) + `<image href="${attrs.href.value}" />`, maxImageLineCount);
+*/
         const onTextNode = async(text) => {// eslint-disable-line no-unused-vars
             result.push({
                 style: Object.assign({}, style),
-                text: text
+                image,
+                text
             });
         };
 
-        const onStartNode = async(elemName) => {// eslint-disable-line no-unused-vars
+        const onStartNode = async(elemName, tail) => {// eslint-disable-line no-unused-vars
             switch (elemName) {
                 case 'strong':
                     style.bold = true;
@@ -314,6 +379,9 @@ export default class BookParser {
                 case 'center':
                     style.center = true;
                     break;
+                case 'image':
+                    image = {};
+                    break;
             }
         };
 
@@ -328,6 +396,9 @@ export default class BookParser {
                 case 'center':
                     style.center = false;
                     break;
+                case 'image':
+                    image = {};
+                    break;
             }
         };
 
@@ -335,7 +406,6 @@ export default class BookParser {
             onStartNode, onEndNode, onTextNode
         });
 
-
         //длинные слова (или белиберду без пробелов) тоже разобьем
         const maxWordLength = this.maxWordLength;
         const parts = result;
@@ -350,7 +420,7 @@ export default class BookParser {
 
                 if (i - spaceIndex >= maxWordLength && i < p.text.length - 1 && 
                     this.measureText(p.text.substr(spaceIndex + 1, i - spaceIndex), p.style) >= this.w - this.p) {
-                    result.push({style: p.style, text: p.text.substr(0, i + 1)});
+                    result.push({style: p.style, image: p.image, text: p.text.substr(0, i + 1)});
                     p = {style: p.style, text: p.text.substr(i + 1)};
                     spaceIndex = -1;
                     i = -1;

+ 1 - 1
package.json

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

+ 8 - 10
server/core/BookConverter/index.js

@@ -53,18 +53,16 @@ class BookConverter {
     }
 
     decode(data) {
-        const charsetAll = chardet.detectAll(data.slice(0, 20000));
-
-        let selected = 'ISO-8859-5';
-        for (const charset of charsetAll) {
-            if (charset.name.indexOf('ISO-8859') < 0) {
-                selected = charset.name;
-                break;
-            }
-        }
+        let selected = textUtils.getEncoding(data);
 
         if (selected == 'ISO-8859-5') {
-            selected = textUtils.getEncoding(data);
+            const charsetAll = chardet.detectAll(data.slice(0, 20000));
+            for (const charset of charsetAll) {
+                if (charset.name.indexOf('ISO-8859') < 0) {
+                    selected = charset.name;
+                    break;
+                }
+            }
         }
 
         return iconv.decode(data, selected);

+ 77 - 0
server/core/BookConverter/sax.js

@@ -276,7 +276,84 @@ async function parse(xstr, options) {
     await _onProgress(100);
 }
 
+function getAttrsSync(tail) {
+    let result = {};
+    let name = '';    
+    let value = '';
+    let vOpen = '';
+    let inName = false;
+    let inValue = false;
+    let waitValue = false;
+    let waitEq = false;
+
+    const pushResult = () => {
+        if (name != '') {
+            let ns = '';
+            if (name.indexOf(':') >= 0) {
+                [ns, name] = name.split(':');
+            }
+
+            result[name] = {value, ns};
+        }
+        name = '';
+        value = '';
+        vOpen = '';
+        inName = false;
+        inValue = false;
+        waitValue = false;
+        waitEq = false;
+    };
+
+    tail = tail.replace(/[\t\n\r]/g, ' ');
+    for (let i = 0; i < tail.length; i++) {
+        const c = tail.charAt(i);
+        if (c == ' ') {
+            if (inValue) {
+                if (vOpen == '"')
+                    value += c;
+                else
+                    pushResult();
+            } else if (inName) {
+                waitEq = true;
+                inName = false;
+            }
+        } else if (!inValue && c == '=') {
+            waitEq = false;
+            waitValue = true;
+            inName = false;
+        } else if (c == '"') {
+            if (inValue) {
+                pushResult();
+            } else if (waitValue) {
+                inValue = true;
+                vOpen = '"';
+            }
+        } else if (inValue) {
+            value += c;
+        } else if (inName) {
+            name += c;
+        } else if (waitEq) {
+            pushResult();
+            inName = true;
+            name = c;
+        } else if (waitValue) {
+            waitValue = false;
+            inValue = true;
+            vOpen = ' ';
+            value = c;
+        } else {
+            inName = true;
+            name = c;
+        }
+    }
+    if (name != '')
+        pushResult();
+
+    return result;
+}
+
 module.exports = {
     parseSync,
+    getAttrsSync,
     parse
 }

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

@@ -85,7 +85,7 @@ function checkIfText(buf) {
     const crFreq = crCount/(buf.length + 1);
     const lfFreq = lfCount/(buf.length + 1);
 
-    return (spaceFreq > 0.1 || crFreq > 0.03 || lfFreq > 0.03);
+    return (buf.length < 1000 || spaceFreq > 0.1 || crFreq > 0.03 || lfFreq > 0.03);
 }
 
 module.exports = {