Добавлена возможность конвертирования pdf как набор изображений.
Добавлены соответствующие настройки в читалку.
This commit is contained in:
@@ -319,6 +319,8 @@ class Reader extends Vue {
|
|||||||
this.showNeedUpdateNotify = settings.showNeedUpdateNotify;
|
this.showNeedUpdateNotify = settings.showNeedUpdateNotify;
|
||||||
this.splitToPara = settings.splitToPara;
|
this.splitToPara = settings.splitToPara;
|
||||||
this.djvuQuality = settings.djvuQuality;
|
this.djvuQuality = settings.djvuQuality;
|
||||||
|
this.pdfAsText = settings.pdfAsText;
|
||||||
|
this.pdfQuality = settings.pdfQuality;
|
||||||
|
|
||||||
this.readerActionByKeyCode = utils.userHotKeysObjectSwap(settings.userHotKeys);
|
this.readerActionByKeyCode = utils.userHotKeysObjectSwap(settings.userHotKeys);
|
||||||
this.$root.readerActionByKeyEvent = (event) => {
|
this.$root.readerActionByKeyEvent = (event) => {
|
||||||
@@ -979,6 +981,8 @@ class Reader extends Vue {
|
|||||||
skipHtmlCheck: (this.splitToPara ? true : false),
|
skipHtmlCheck: (this.splitToPara ? true : false),
|
||||||
isText: (this.splitToPara ? true : false),
|
isText: (this.splitToPara ? true : false),
|
||||||
djvuQuality: this.djvuQuality,
|
djvuQuality: this.djvuQuality,
|
||||||
|
pdfAsText: this.pdfAsText,
|
||||||
|
pdfQuality: this.pdfQuality,
|
||||||
},
|
},
|
||||||
(state) => {
|
(state) => {
|
||||||
progress.setState(state);
|
progress.setState(state);
|
||||||
|
|||||||
@@ -223,6 +223,10 @@ class SettingsPage extends Vue {
|
|||||||
return this.$store.state.config.mode;
|
return this.$store.state.config.mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isExternalConverter() {
|
||||||
|
return this.$store.state.config.useExternalBookConverter;
|
||||||
|
}
|
||||||
|
|
||||||
get settings() {
|
get settings() {
|
||||||
return this.$store.state.reader.settings;
|
return this.$store.state.reader.settings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<div class="item row">
|
<div class="item row">
|
||||||
<div class="label-7">Текст</div>
|
<div class="label-7">Текст</div>
|
||||||
<div class="col row">
|
<div class="col row">
|
||||||
<q-checkbox v-model="splitToPara" @input="needTextReload" size="xs" label="Попытаться разбить текст на параграфы">
|
<q-checkbox v-model="splitToPara" size="xs" label="Попытаться разбить текст на параграфы">
|
||||||
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
||||||
Опция принудительно включает эвристику разбиения текста на<br>
|
Опция принудительно включает эвристику разбиения текста на<br>
|
||||||
параграфы в случае, если формат файла определен как html,<br>
|
параграфы в случае, если формат файла определен как html,<br>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<div class="item row">
|
<div class="item row">
|
||||||
<div class="label-7">Сайты</div>
|
<div class="label-7">Сайты</div>
|
||||||
<div class="col row">
|
<div class="col row">
|
||||||
<q-checkbox v-model="enableSitesFilter" @input="needTextReload" size="xs" label="Включить html-фильтр для сайтов">
|
<q-checkbox v-model="enableSitesFilter" size="xs" label="Включить html-фильтр для сайтов">
|
||||||
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
||||||
Html-фильтр вырезает лишние элементы со<br>
|
Html-фильтр вырезает лишние элементы со<br>
|
||||||
страницы для определенных сайтов, таких как:<br>
|
страницы для определенных сайтов, таких как:<br>
|
||||||
@@ -37,11 +37,42 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!---------------------------------------------->
|
<!---------------------------------------------->
|
||||||
<div class="part-header">PDF</div>
|
<div v-if="isExternalConverter">
|
||||||
|
<div class="part-header">PDF</div>
|
||||||
|
|
||||||
|
<div class="item row">
|
||||||
|
<div class="label-7">Формат</div>
|
||||||
|
<div class="col row">
|
||||||
|
<q-checkbox v-model="pdfAsText" size="xs" label="Извлекать текст из PDF">
|
||||||
|
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
||||||
|
Пытается извлечь текст из pdf-файла и переразбить на параграфы.<br>
|
||||||
|
Размер получаемого fb2-файла при этом относительно небольшой.<br>
|
||||||
|
При отключении этой опции, pdf будет представлен как набор<br>
|
||||||
|
изображений (аналогично ковертированию djvu).
|
||||||
|
</q-tooltip>
|
||||||
|
</q-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item row">
|
||||||
|
<div class="label-7">Качество</div>
|
||||||
|
<div class="col row">
|
||||||
|
<NumInput class="col-5" v-model="pdfQuality" :min="10" :max="100" :disable="pdfAsText" >
|
||||||
|
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
||||||
|
Качество конвертирования Pdf в Fb2. Чем значение выше, тем больше<br>
|
||||||
|
размер итогового файла. Если сервер отказывается конвертировать<br>
|
||||||
|
слишком большой файл, то попробуйте понизить качество.
|
||||||
|
</q-tooltip>
|
||||||
|
</NumInput>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!---------------------------------------------->
|
<!---------------------------------------------->
|
||||||
<div class="part-header">DJVU</div>
|
<div v-if="isExternalConverter">
|
||||||
<div class="item row">
|
<div class="part-header">DJVU</div>
|
||||||
|
|
||||||
|
<div class="item row">
|
||||||
<div class="label-7">Качество</div>
|
<div class="label-7">Качество</div>
|
||||||
<div class="col row">
|
<div class="col row">
|
||||||
<NumInput class="col-5" v-model="djvuQuality" :min="10" :max="100">
|
<NumInput class="col-5" v-model="djvuQuality" :min="10" :max="100">
|
||||||
@@ -52,5 +83,5 @@
|
|||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</NumInput>
|
</NumInput>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -254,6 +254,8 @@ const settingDefaults = {
|
|||||||
enableSitesFilter: true,
|
enableSitesFilter: true,
|
||||||
splitToPara: false,
|
splitToPara: false,
|
||||||
djvuQuality: 20,
|
djvuQuality: 20,
|
||||||
|
pdfAsText: true,
|
||||||
|
pdfQuality: 20,
|
||||||
|
|
||||||
showServerStorageMessages: true,
|
showServerStorageMessages: true,
|
||||||
showWhatsNewDialog: true,
|
showWhatsNewDialog: true,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class ReaderController extends BaseController {
|
|||||||
isText: (request.hasOwnProperty('isText') ? request.isText : false),
|
isText: (request.hasOwnProperty('isText') ? request.isText : false),
|
||||||
uploadFileName: (request.hasOwnProperty('uploadFileName') ? request.uploadFileName : false),
|
uploadFileName: (request.hasOwnProperty('uploadFileName') ? request.uploadFileName : false),
|
||||||
djvuQuality: (request.hasOwnProperty('djvuQuality') ? request.djvuQuality : false),
|
djvuQuality: (request.hasOwnProperty('djvuQuality') ? request.djvuQuality : false),
|
||||||
|
pdfAsText: (request.hasOwnProperty('pdfAsText') ? request.pdfAsText : false),
|
||||||
|
pdfQuality: (request.hasOwnProperty('pdfQuality') ? request.pdfQuality : false),
|
||||||
});
|
});
|
||||||
const state = this.workerState.getState(workerId);
|
const state = this.workerState.getState(workerId);
|
||||||
return (state ? state : {});
|
return (state ? state : {});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class ConvertPdf extends ConvertHtml {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async run(notUsed, opts) {
|
async run(notUsed, opts) {
|
||||||
if (!this.check(notUsed, opts))
|
if (!opts.pdfAsText || !this.check(notUsed, opts))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
await this.checkExternalConverterPresent();
|
await this.checkExternalConverterPresent();
|
||||||
|
|||||||
79
server/core/Reader/BookConverter/ConvertPdfImages.js
Normal file
79
server/core/Reader/BookConverter/ConvertPdfImages.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const utils = require('../../utils');
|
||||||
|
|
||||||
|
const ConvertJpegPng = require('./ConvertJpegPng');
|
||||||
|
|
||||||
|
class ConvertPdfImages extends ConvertJpegPng {
|
||||||
|
check(data, opts) {
|
||||||
|
const {inputFiles} = opts;
|
||||||
|
|
||||||
|
return this.config.useExternalBookConverter &&
|
||||||
|
inputFiles.sourceFileType && inputFiles.sourceFileType.ext == 'pdf';
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(data, opts) {
|
||||||
|
if (!this.check(data, opts))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let {inputFiles, callback, abort, pdfQuality} = opts;
|
||||||
|
|
||||||
|
pdfQuality = (pdfQuality && pdfQuality <= 100 && pdfQuality >= 10 ? pdfQuality : 20);
|
||||||
|
|
||||||
|
const pdftoppmPath = '/usr/bin/pdftoppm';
|
||||||
|
if (!await fs.pathExists(pdftoppmPath))
|
||||||
|
throw new Error('Внешний конвертер pdftoppm не найден');
|
||||||
|
|
||||||
|
const dir = `${inputFiles.filesDir}/`;
|
||||||
|
const baseFile = `${dir}${path.basename(inputFiles.sourceFile)}`;
|
||||||
|
const jpgFiles = `${baseFile}.tmp`;
|
||||||
|
|
||||||
|
//конвертируем в jpeg
|
||||||
|
let perc = 0;
|
||||||
|
await this.execConverter(pdftoppmPath, ['-jpeg', '-jpegopt', `quality=${pdfQuality},progressive=y`, inputFiles.sourceFile, jpgFiles], () => {
|
||||||
|
perc = (perc < 100 ? perc + 1 : 40);
|
||||||
|
callback(perc);
|
||||||
|
}, abort);
|
||||||
|
|
||||||
|
const limitSize = 2*this.config.maxUploadFileSize;
|
||||||
|
let jpgFilesSize = 0;
|
||||||
|
//ищем изображения
|
||||||
|
let files = [];
|
||||||
|
await utils.findFiles(async(file) => {
|
||||||
|
if (path.extname(file) == '.jpg') {
|
||||||
|
jpgFilesSize += (await fs.stat(file)).size;
|
||||||
|
if (jpgFilesSize > limitSize) {
|
||||||
|
throw new Error(`Файл для конвертирования слишком большой|FORLOG| jpgFilesSize: ${jpgFilesSize} > ${limitSize}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push({name: file, base: path.basename(file)});
|
||||||
|
}
|
||||||
|
}, dir);
|
||||||
|
|
||||||
|
files.sort((a, b) => a.base.localeCompare(b.base));
|
||||||
|
|
||||||
|
//схема документа (outline)
|
||||||
|
//const djvusedResult = await this.execConverter(djvusedPath, ['-u', '-e', 'print-outline', inputFiles.sourceFile]);
|
||||||
|
const outline = [];
|
||||||
|
/*const lines = djvusedResult.stdout.match(/\(".*"\s*?"#\d+".*?\)/g);
|
||||||
|
if (lines) {
|
||||||
|
lines.forEach(l => {
|
||||||
|
const m = l.match(/"(.*)"\s*?"#(\d+)"/);
|
||||||
|
if (m) {
|
||||||
|
outline[m[2]] = m[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
|
await utils.sleep(100);
|
||||||
|
let i = 0;
|
||||||
|
const imageFiles = files.map(f => {
|
||||||
|
i++;
|
||||||
|
let alt = (outline[i] ? outline[i] : '');
|
||||||
|
return {src: f.name, alt};
|
||||||
|
});
|
||||||
|
return await super.run(data, Object.assign({}, opts, {imageFiles}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ConvertPdfImages;
|
||||||
@@ -7,6 +7,7 @@ const convertClassFactory = [
|
|||||||
require('./ConvertEpub'),
|
require('./ConvertEpub'),
|
||||||
require('./ConvertDjvu'),
|
require('./ConvertDjvu'),
|
||||||
require('./ConvertPdf'),
|
require('./ConvertPdf'),
|
||||||
|
require('./ConvertPdfImages'),
|
||||||
require('./ConvertRtf'),
|
require('./ConvertRtf'),
|
||||||
require('./ConvertDocX'),
|
require('./ConvertDocX'),
|
||||||
require('./ConvertFb3'),
|
require('./ConvertFb3'),
|
||||||
|
|||||||
Reference in New Issue
Block a user