Около года назад я уже писал пост на эту же тему, который собрал немало положительных отзывов. Я не буду повторяться: суть, смысл и идея остались ровно те же, но код я переписал с bash на C, и теперь программа работает и под Windows. Под Linux тоже работает; полагаю, что будет работать (возможно с небольшими модификациями) и под любую другую ОС, для которой есть компилятор C и нужные утилиты (ImageMagick, Poppler, DjVuLibre, Pdftk).
Архив со всем необходимым для Windows
Краткая инструкция по применению:
Запускаете ebook2pdf.exe tune 800 600 50
, вместо 800 и 600 подставляете паспортное разрешение вашей читалки, 50 — это количество шагов. В папке с программой появится файл tune-800-600.pdf
, который нужен для выяснения реального разрешения области вывода читалки. Зачем это нужно и как его выяснить, описано в предыдущей статье.
Для обработки книги запускаете ebook2pdf.exe "D:путькфайлу.pdf" 778 584 40 L
. 778 и 584 — это разрешение области вывода, которое выяснили в предыдущем абзаце. 40 — размер перекрытия слайдов, L (ещё можно R) — направление поворота страницы в альбомный режим. Результат появится в папке с оригинальным файлом книги в виде файла книга [eBook].pdf
. Внутри он будет выглядеть как-то так. На вход, кроме PDF, принимает также DjVu.
В программе нет проверки возможных ошибок, поэтому при подаче неправильных параметров, отсутствия файла или прав записи, отсутствия необходимых служебных программ она может вылетать, сыпать ужасными ошибками в консоль и т. п. — простите :)
Исходный код. Под Windows я собирал с помощью mingw (сс ebook2pdf.c -o ebook2pdf.exe), под Linux — с помощью gcc. Нужные утилиты под windows можно найти в интернете в виде готовых бинарников (или взять из архива по ссылке выше), под Linux они обычно есть в репозиториях, из исходников тоже собирать не возбраняется. По-хорошему, было бы неплохо вынести часть процесса в отдельный поток — конвертация происходила бы заметно шустрее.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __MINGW32__
#define PREFIX "wutils\"
#define TEMPDIR "temp\"
#define RM "del"
#define DEVNULL "nul"
#define RB "rb"
#define WB "wb"
#else
#define PREFIX ""
#define TEMPDIR "temp/"
#define RM "rm"
#define DEVNULL "/dev/null"
#define RB "r"
#define WB "w"
#endif
#define STAGE1P PREFIX "pdftoppm -gray -r 300 -f %d -l %d "%s" 2>"
DEVNULL " | " PREFIX "convert - -fuzz 1%% -trim +repage
-resize %d -bordercolor white -border 0x10 -bordercolor black
-border 0x5 -type GrayScale -depth 8 gray:- 2>" DEVNULL
#define STAGE1D PREFIX "ddjvu -format=pgm -scale=300 -page=%d "%s" 2>"
DEVNULL " | " PREFIX "convert - -fuzz 1%% -trim +repage
-resize %d -bordercolor white -border 0x10 -bordercolor black
-border 0x5 -type GrayScale -depth 8 gray:- 2>" DEVNULL
#define STAGE2 PREFIX "convert -size %dx%d -depth 8 gray:- -rotate %d +repage
-strip -type GrayScale -depth 4 -compress Zip -quality 100 "
TEMPDIR "temp%04d.pdf 2>" DEVNULL
#define STAGE3 PREFIX "pdftk " TEMPDIR "temp*.pdf cat output
"%s [eBook].pdf" && " RM " " TEMPDIR "temp*.pdf"
#define TUNE1 PREFIX "convert -size %dx%d -depth 8 gray:- +repage -strip
-type GrayScale -depth 4 -compress Zip -quality 100 "
TEMPDIR "tune.pgm"
#define TUNE2 PREFIX "convert " TEMPDIR "tune.pgm -crop 100x%d+0+0
-fill black -pointsize 20 -annotate +30+400 %d +repage -strip
-type GrayScale -depth 4 -compress Zip -quality 100 "
TEMPDIR "tune-V%04d.pdf"
#define TUNE3 PREFIX "convert " TEMPDIR "tune.pgm -crop %dx100+0+0
-fill black -pointsize 20 -annotate +280+50 %d +repage -strip
-type GrayScale -depth 4 -compress Zip -quality 100 "
TEMPDIR "tune-H%04d.pdf"
#define TUNE4 PREFIX "convert " TEMPDIR "tune.pgm +repage -strip
-type GrayScale -depth 4 -compress Zip -quality 100 "
TEMPDIR "tune-ZZZZ.pdf"
#define TUNE5 PREFIX "pdftk " TEMPDIR "tune-ZZZZ.pdf " TEMPDIR "tune*.pdf
cat output tune-%d-%d.pdf && " RM " " TEMPDIR "tune*.*"
// globals
int width, height, frame;
char *buffer;
char string[2048];
FILE *outbuf, *inbuf;
const char *getfilextension(const char *fullfilename) {
int size, index;
size = index = 0;
while (fullfilename[size]) {
if (fullfilename[size] == '.')
index = size;
size++;
}
if(size && index)
return fullfilename + index;
return NULL;
}
int tune(int steps) {
int i, j, px = 0;
printf("ngenerating...n");
buffer = malloc(frame+1);
buffer[frame] = 0;
for (i=0; i<width; i++) {
px = i % 2;
for (j=0; j<height; j++) {
px = !px;
buffer[i*height+j] = px*255;
}
}
sprintf(string, TUNE1, height, width);
inbuf = popen(string, WB);
setbuf(inbuf, NULL);
fwrite(buffer, 1, frame+1, inbuf);
pclose(inbuf);
printf("vertical...n");
for (i=width-steps+1; i<=width; i++) {
sprintf(string, TUNE2, i, i, i);
system(string);
}
printf("horizontal...n");
for (i=height-steps+1; i<=height; i++) {
sprintf(string, TUNE3, i, i, i);
system(string);
}
sprintf(string, TUNE4);
system(string);
sprintf(string, TUNE5, width, height);
system(string);
printf("done!n");
return 0;
}
int main(int argc, char const *argv[]) {
int page = 0, pages = 0, slide = 0;
int overlap, rotate;
char *start;
long bufsize = 0;
// pdf=1 djvu=2
int type = 0;
if (argc<5 || argc>6) {
printf("nUsage: ebook2pdf filename width height overlap rotate(R|L)n");
printf(" or: ebook2pdf tune width height stepsn");
return 0;
} else {
width = atoi(argv[2]);
height = atoi(argv[3]);
frame = width*height;
if (argc == 5 && !strcmp(argv[1], "tune")) {
tune(atoi(argv[4]));
return 0;
}
}
// starting
overlap = atoi(argv[4])*width;
rotate = argv[5][0]=='R' ? 90 : -90;
// getting file type
if (!strcmp(getfilextension(argv[1]), ".pdf") ||
!strcmp(getfilextension(argv[1]), ".PDF"))
type = 1;
else if (!strcmp(getfilextension(argv[1]), ".djvu") ||
!strcmp(getfilextension(argv[1]), ".DJVU"))
type = 2;
else {
printf("Error: only PDF and DJVU files are supportedn");
return 0;
}
// getting number of pages
if (type == 1) {
sprintf(string, "%spdfinfo "%s"", PREFIX, argv[1]);
outbuf = popen(string, "r");
while (fgets(string, sizeof string, outbuf))
if (start = strstr(string, "Pages:"))
pages = atoi(start+16);
} else if (type == 2) {
sprintf(string, "%sdjvm -l "%s"", PREFIX, argv[1]);
outbuf = popen(string, "r");
while (fgets(string, sizeof string, outbuf))
if (strstr(string, "PAGE"))
pages+=1;
}
pclose(outbuf);
// main processing
printf("npages = %dn", pages);
printf("starting...n");
start = buffer = malloc(frame+1);
buffer[frame] = 0;
for (page=1; page<=pages; page++) {
printf("page: %4dn", page);
if (type == 1)
sprintf(string, STAGE1P, page, page, argv[1], width);
else if (type == 2)
sprintf(string, STAGE1D, page, argv[1], width);
outbuf = popen(string, RB);
while (fread(start, width, 1, outbuf) > 0) {
if (bufsize == frame) {
sprintf(string, STAGE2, width, height, rotate, ++slide);
inbuf = popen(string, WB);
setbuf(inbuf, NULL);
fwrite(buffer, 1, frame+1, inbuf);
pclose(inbuf);
// move overlapping data from buffer end
memmove(buffer, buffer+frame-overlap, overlap);
bufsize=overlap;
}
start = buffer+bufsize;
bufsize+=width;
}
pclose(outbuf);
}
// last frame
sprintf(string, STAGE2, width, height, rotate, ++slide);
inbuf = popen(string, WB);
setbuf(inbuf, NULL);
buffer[bufsize-width] = 0;
fwrite(buffer, 1, bufsize-width+1, inbuf);
pclose(inbuf);
printf("finishing...n");
// exporting result
sprintf(string, STAGE3, argv[1]);
system(string);
printf("done!n");
return 0;
}
Автор: degorov