浏览代码

Добавлена возможность конвертирования pdf как набор изображений.
Добавлены соответствующие настройки в читалку.

Book Pauk 4 年之前
父节点
当前提交
480c95bd63

+ 4 - 0
client/components/Reader/Reader.vue

@@ -319,6 +319,8 @@ class Reader extends Vue {
         this.showNeedUpdateNotify = settings.showNeedUpdateNotify;
         this.splitToPara = settings.splitToPara;
         this.djvuQuality = settings.djvuQuality;
+        this.pdfAsText = settings.pdfAsText;
+        this.pdfQuality = settings.pdfQuality;
 
         this.readerActionByKeyCode = utils.userHotKeysObjectSwap(settings.userHotKeys);
         this.$root.readerActionByKeyEvent = (event) => {
@@ -979,6 +981,8 @@ class Reader extends Vue {
                         skipHtmlCheck: (this.splitToPara ? true : false),
                         isText: (this.splitToPara ? true : false),
                         djvuQuality: this.djvuQuality,
+                        pdfAsText: this.pdfAsText,
+                        pdfQuality: this.pdfQuality,
                     },
                     (state) => {
                         progress.setState(state);

+ 4 - 0
client/components/Reader/SettingsPage/SettingsPage.vue

@@ -223,6 +223,10 @@ class SettingsPage extends Vue {
         return this.$store.state.config.mode;
     }
 
+    get isExternalConverter() {
+        return this.$store.state.config.useExternalBookConverter;
+    }
+
     get settings() {
         return this.$store.state.reader.settings;
     }

+ 46 - 15
client/components/Reader/SettingsPage/include/ConvertTab.inc

@@ -10,7 +10,7 @@
 <div class="item row">
     <div class="label-7">Текст</div>
     <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%">
                 Опция принудительно включает эвристику разбиения текста на<br>
                 параграфы в случае, если формат файла определен как html,<br>
@@ -23,7 +23,7 @@
 <div class="item row">
     <div class="label-7">Сайты</div>
     <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%">
                 Html-фильтр вырезает лишние элементы со<br>
                 страницы для определенных сайтов, таких как:<br>
@@ -37,20 +37,51 @@
 </div>
 
 <!---------------------------------------------->
-<div class="part-header">PDF</div>
+<div v-if="isExternalConverter">
+    <div class="part-header">PDF</div>
 
-<!---------------------------------------------->
-<div class="part-header">DJVU</div>
-<div class="item row">
-    <div class="label-7">Качество</div>
-    <div class="col row">
-        <NumInput class="col-5" v-model="djvuQuality" :min="10" :max="100">
-            <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
-                Качество конвертирования Djvu в Fb2. Чем значение выше, тем больше<br>
-                размер итогового файла. Если сервер отказывается конвертировать<br>
-                слишком большой файл, то попробуйте понизить качество.
-            </q-tooltip>
-        </NumInput>
+    <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 v-if="isExternalConverter">
+    <div class="part-header">DJVU</div>
+
+    <div class="item row">
+        <div class="label-7">Качество</div>
+        <div class="col row">
+            <NumInput class="col-5" v-model="djvuQuality" :min="10" :max="100">
+                <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
+                    Качество конвертирования Djvu в Fb2. Чем значение выше, тем больше<br>
+                    размер итогового файла. Если сервер отказывается конвертировать<br>
+                    слишком большой файл, то попробуйте понизить качество.
+                </q-tooltip>
+            </NumInput>
+        </div>
+    </div>
+</div>

+ 2 - 0
client/store/modules/reader.js

@@ -254,6 +254,8 @@ const settingDefaults = {
     enableSitesFilter: true,
     splitToPara: false,
     djvuQuality: 20,
+    pdfAsText: true,
+    pdfQuality: 20,
 
     showServerStorageMessages: true,
     showWhatsNewDialog: true,

+ 2 - 0
server/controllers/ReaderController.js

@@ -24,6 +24,8 @@ class ReaderController extends BaseController {
                 isText: (request.hasOwnProperty('isText') ? request.isText : false),
                 uploadFileName: (request.hasOwnProperty('uploadFileName') ? request.uploadFileName : 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);
             return (state ? state : {});

+ 1 - 1
server/core/Reader/BookConverter/ConvertPdf.js

@@ -15,7 +15,7 @@ class ConvertPdf extends ConvertHtml {
     }
 
     async run(notUsed, opts) {
-        if (!this.check(notUsed, opts))
+        if (!opts.pdfAsText || !this.check(notUsed, opts))
             return false;
 
         await this.checkExternalConverterPresent();

+ 79 - 0
server/core/Reader/BookConverter/ConvertPdfImages.js

@@ -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;

+ 1 - 0
server/core/Reader/BookConverter/index.js

@@ -7,6 +7,7 @@ const convertClassFactory = [
     require('./ConvertEpub'),
     require('./ConvertDjvu'),
     require('./ConvertPdf'),
+    require('./ConvertPdfImages'),
     require('./ConvertRtf'),
     require('./ConvertDocX'),
     require('./ConvertFb3'),