Конвертирование книг электронных форматов \ convert ebook format linux cli

DJVU (из djvu формата)

Обычно я конвертирую книгу менеджером книг Callibre. но Наибольшую ценность представляет возможность читать на читалке книги из формата djvu а такое в fb2 никто не показывает как конвертировать. Показываю. Я для этого написал свою утилиту.

Вступение

у меня много таких книг, в формате djvu, которые я бы хотел читать на своей электронной читалке.

DJVU это формат хороший компактностью формат для сканированных книг, если у вас много сканированных книг - djvu для их хранения отличный выбор.

Внутри это чаще всего набор картинко - страниц, хотя djvu может содержать и “текстовый слой”, но часто его не содержит (OCR).

Скрипт который я написал, чтобы конвертировать djvu2fb2 (linux lua bash … cli)

Подготовка

1
2
3
4
5
6
7
sudo apt-get install python-opencv libopencv-dev python-numpy python-dev

sudo apt-get install luarocks
sudo luarocks install penlight
sudo luarocks install xml
sudo apt-get install imagemagick # convert
# -- base64 # sh

Еще понадобится

  • ddjvu - для распаковки книги djvu (ddjvu: converts DjVu documents to PBM/PGM/PPM images.)
  • tesseract - для распознавания текста на изображениях-страницах
1
2
3
4
5
6
7
8
9
$ sudo apt install -y tesseract-ocr
$ sudo apt install -y libtesseract-dev
$ tesseract --list-langs
List of available languages (3):
eng
osd
rus
# на всякий случай
$ sudo apt install -y tesseract-ocr-rus

djvu

1
$ sudo apt-get install -y djvulibre-bin

Мой скрипт на LUA (права копирования и модификации за мной, только для частных целей 1 книга в год на человека)

1
cat djvuToFB2.lua

выведет:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
-- luarocks install penlight
-- luarocks install xml
-- sudo apt-get install imagemagick # convert
-- base64 # sh

require 'pl'
xml = require 'xml'
local p = print

-- mappings = {["A"]="B",["B"]="A"}
local function tr(s,mappings)
return string.gsub(s,
"(.)",
function(m)
-- print("found",m,"replace with",mappings[m],mappings[m] or m)
if mappings[m] == nil then return m else return mappings[m] end
end
)
end

-------------

local function exec(command)
local handle = io.popen(command)
local result = handle:read("*a")
handle:close()
return result
end
-- print(exec('ls -lF'))

local function extractTextFromImage(imagepath, lang)
local cmd = 'tesseract '..imagepath..' stdout -l '..lang
--print('cmd=',cmd)
local text = exec(cmd)
return text
end
-- local mappings = {["["]='\\[' ,["]"]='\\]'}
--print(extractTextFromImage( tr('~/Загрузки/знай_и_умей_[tfile.ru]/p0015.png', mappings) ,'rus'))

local function extractPageFromDJVU(path, pagenum, image)
local cmd = 'ddjvu -format=tiff -page="'..pagenum..'" '..path..' '..image
--p(cmd)
local ok = exec( cmd )
end
-- extractPageFromDJVU('"/home/pavlov/Загрузки/знай_и_умей_[tfile.ru]/Ерлыкин Л. - Пионер-умелец (Знай и умей) - 1977.djvu"', 90, 'page090.tiff')

--[[
local function unpackDJVUFile(djvu, dir)
local cmd = 'ddjvu -format=tiff -eachpage '.. djvu.. ' '..dir..'page%03d.tiff' -- -mode=black
p('cmd = ',cmd)
local r = exec(cmd)
end
]]

--[[
local function convertTiffToJpeg(tiff, jpeg, opts)
if not opts then opts = '' end
local cmd = 'convert '..opts..' '..tiff..' '..jpeg
--p('cmd = ',cmd)
local r = exec(cmd)
end
]]
--convertTiffToJpeg('/home/pavlov/Yandex.Disk/page090.tiff', '/home/pavlov/Yandex.Disk/page090.jpeg', '-resize 1500x1500')

local function getDJVUPageCount(djvu)
local cmd = 'djvused '..djvu..' -e n '
--p('cmd = ',cmd)
local r = exec(cmd)
return r
end


local function bookAppendScanedPage(book, index, image)
--p('page='..tostring(index) )
local text = extractTextFromImage(image,'rus')
local body = xml.find(book, 'body')
local notes = xml.find(book, 'body', 'name', 'notes')
--print('body,notes=',body,notes)

local text = extractTextFromImage( image,'rus')
--p(text)

local path,ext = path.splitext(image)
local jpgpath = path..'.jpg'
--p(jpgpath )
local r = exec('convert '..image..' -resize 1000x1000 '..path..'.jpg') -- файл записан

local base64 = exec('base64 '..jpgpath)

body[#body+1] = {xml='section' ,
{xml='title', tostring('стр.№'..index)},
-- <a l:href="#n_1" type="note">[1]</a>
{xml='p', {xml='a', ['l:href']='#n_'..index, type='note', 'оригинал страницы (картинка/Image scaned )'}},
{xml='p', text }
}

notes[#notes+1] = { xml='section', id='n_'..index,
-- <image l:href="#i_001.jpg"/>
{xml='p', {xml='image', ['l:href']= string.format("#i_%03d.jpg",index) }}
}

-- todo need index dependent address
book[#book+1] = {xml='binary', id=string.format("i_%03d.jpg", index), ['content-type']="image/jpeg", base64}

--p(xml.dump(body))
--p(xml.dump(notes))
end

local function makeSimpleFictionBook()
local xml={ xml='FictionBook' , xmlns="http://www.gribuser.ru/xml/fictionbook/2.0", ['xmlns:l']="http://www.w3.org/1999/xlink"}
xml[#xml+1] = {xml='description'} -- todo
xml[#xml+1] = {xml='body'}
xml[#xml+1] = {xml='body', name="notes"}
return xml
end
-- p(xml.dump(makeSimpleFictionBook()))


local function djvuToFB2(djvu, fb2path)
--local djvu = '"/home/pavlov/Загрузки/знай_и_умей_[tfile.ru]/Ерлыкин Л. - Пионер-умелец (Знай и умей) - 1977.djvu"'

local n = path.tmpname()
file.delete(n)
dir.makepath(n)

-- unpackDJVUFile(djvu, n..'/')

local N = getDJVUPageCount(djvu)
p('N=',N)

local book = makeSimpleFictionBook()

--for i =1, tonumber(N) do
for i=1, tonumber(N) do
p('page='..tostring(i) )
-- extract one image
local image = n..'/'..string.format('page%03d.tiff',i) -- путь
--p(image)
extractPageFromDJVU(djvu, i, image) -- файл записан в путь

bookAppendScanedPage(book, i, image)

end

-- luajit: not enough memory
-- file.write( 'book.fb2', xml.dump(book) )

local f = io.open (fb2path, 'w')
f:write([[
<?xml version="1.0" encoding="UTF-8"?>
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink">
]])
for i in ipairs(book) do
f:write(xml.dump(book[i]))
end
f:write('</FictionBook>')
f:close()

end

djvuToFB2( '"/home/pavlov/Загрузки/знай_и_умей_[tfile.ru]/Якуб С.К. - Вспомним забытые игры (Знай и умей) - 1988.djvu"',
'Якуб С.К. - Вспомним забытые игры (Знай и умей) - 1988')

Запуск

Внизу скрипта (несколлько строк выше) вписан путь к кконвертируемой книге и имя для выходного файла, кавычки тут важны.

1
2
djvuToFB2(	'"/home/pavlov/Загрузки/знай_и_умей_[tfile.ru]/Якуб С.К. - Вспомним забытые игры (Знай и умей) - 1988.djvu"', 
'Якуб С.К. - Вспомним забытые игры (Знай и умей) - 1988')

Пример результата:

Текст читать конечно всегда удобно на читалке, но нужно иметь доступ и к карптинкам, особенно это важно, когда текст не понятен.

бывает так, что картинки идут на странице “вразнобой” с текстом и становится не понятно.

Поэтому я встраиваю в поток текста исходные страницы изображений страницы. - это ссылка по которой можно перейти к изображению или увидеть сразу изображение (в продвинутых читалках)

Вообще учитывается эта особеннность: распознавание не всегда работает хорошо т.к.

  • сканирование бывает сделано плохо,
  • углы листа при сканировании уходят вниз или вверх
  • обтекание текстом картинок в книге мешают автоматически распознать поток текста
  • итд…

Ещё я встраиваю в начало каждого блока текста страницу с которой он распознан с номером.

![Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_001](img/Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_001.png)

![Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_003](img/Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_003.png)

![Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_004](img/Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_004.png)

![Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_005](img/Акентьев В. - Веселые тайны (Знай и умей) - 1964 [FB2] — Читалка электронных книг_005.png)

Таким образом очень удобно читать получается такую книгу. Правда она содержит картинки КАЖДОЙ страницы и получается большой. - это небольшой минус.

Я такие книги удаляю после чтения с устройства.

По идее можно не включать саму картинку в книгу, но я так не пробовал - меня устраивает. ;-)

PDF to HTML

Бонус. Встречаются на просторах сети такие книги с таким комментарием внутри их кода:

1
<!-- Created by pdf2htmlEX (https://github.com/coolwanglu/pdf2htmlex) -->

так что можете пользоваться этим проектом https://github.com/coolwanglu/pdf2htmlex

Книга получается “тяжелая”, обычно под 100 МБ файлик… но читать на компе из большого браузера ее удобно - содержание, оформление, все сохранено.

Какой в этом смысл, если можно прочитать сразу pdf? - не знаю! Но штука работает.

Calibre из linux консоли

Если на мешине уже есть calibre - можете всю пачку книгу бахнуть таким способом например в epub

1
2
HERE=$(pwd)
ls *.fb2| xargs -I {} sh -x -c "ebook-convert '$HERE/{}' '$HERE/{}.epub'"