Merge branch 'release/0.3.3'
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -930,6 +930,7 @@ class TextPage extends Vue {
|
||||
this.$emit('full-screen-toogle');
|
||||
break;
|
||||
case 'Tab':
|
||||
case 'KeyQ':
|
||||
this.doToolBarToggle();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "Liberama",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user