С чего всё началось
Началось всё с лени. Лень бывает продуктивная и не очень, в моём случае, похоже — первое.
Недавно прикупил на «Литресе» книжку Нассима Талеба «Антихрупкость». Скачал, закинул на читалку, да так до неё и не добрался, то жена утащит своё почитать, то ребёнок… А тут в рассылке прилетела вот эта статья «Лучший подарок – книга. Делаем красивый переплет». Посмотрел, всё вроде красиво, только вот под рукой не оказалось машины с «Форточками», ну не пользуемся мы ими, а запускать эти WordPage-ы через прокладку типа Wine или в Qemu как-то некомильфо. Однако у меня имеется возможность распечатать книженцию и нормально обрезать (мини-типография на первом этаже дома, в котором я живу) в нужный формат. Сие значит, что следует использовать возможности подручных средств.
Идея
У себя в конторе я уже с полгодика использую небольшой bash-скрипт для сборки разрозненных PDF-ников воедино и формирования единой структуры проектной документации. Значит, один кандидат на запчасти есть.
Собственно идея состояла в том, чтобы в определённой последовательности разложить отдельные странички по тетрадкам произвольного объёма, кратного 4. Если внимательно посмотреть, то один тетрадный лист содержит именно 4, а не три или пять страниц, причём страницы расположены в определённом порядке на листе, правда, это я потом допетрил, а пока было так, как есть, а именно (для 48-страничной тетради):
| Пары страниц | зависимость |
|---|---|
| 1-48 | m=(1) n=(48) |
| 2-47 | m=(1+1) n=(48-1) |
| 3-46 | m=(1+2) n=(48-2) |
| ... | ... |
| 24-25 | m=(1+23) n=(48-23) |
Сие значит, что, во-первых, следует книгу разбить на i страниц, затем на j тетрадей по k страниц каждая, затем каждую тетрадь разбить на пары страниц, которые разместить на листах, которые собрать в единые файлы тетрадей, который разместить рядом с исходным PDF-ником. Ф-ф-фу, еле сформулировал…
В общем-то зависимость простая, первая страница листа — это a+b, а последняя — k-b, где a — порядковый номер тетради, причём 0<a<j, а b — порядковый номер парной страницы, причём 0<b<k/2. Т.е. это цикл for ((b=0; b<k/2; b++)) в котором и следует обрабатывать все эти странички.
Сей цикл мы запихиваем в другой, который пробегает по тетрадкам for ((a=0; a<j; a++)).
Далее используем пакеты GhostScript, ImageMagic и djvulibre, ну и кое-какие встроенные команды bash и Linux.
В процессе проработки выяснилось, что часть кода лучше вынести в отдельные функции, да и bash делит всё нацело, а это значит, что нужно дорабатывать сборку остатков страниц, что оказалось примитивно:
echo "Выполнена сборка страниц основного набора, теперь можно собрать дополнительную тетрадь."
letsgo
let "LAST_W_BOOK=$W_BOOK+1" #Создаём переменную указывающую номер последней тетради
if [ "`expr $TAIL_PAGES % 4`" -eq "0" ] #Если количество страниц в последней тетради кратно 4
then
echo "Всё нормально, продолжаем создание последней тетради"
assembly $LAST_W_BOOK $TAIL_PAGES
else #Если количество страниц в последней тетради НЕ кратно 4
echo "В тетради недостаёт `expr 4 - $TAIL_PAGES % 4` страниц(ы)"
echo "Следует добавить их. Вы можете их добавить вручную или позволить это сделать программе."
letsgo
#добавляем необходимое количество пустых файлов после чего приступаем к сборке
fi
На закуску выяснилось, что порядок страниц на листах не совсем тот, как был в табличке выше, а вот такой:
| Пары страниц | зависимость |
|---|---|
| 48-1 | n=(48) m=(1) |
| 2-47 | m=(1+1) n=(48-1) |
| 46-3 | n=(48-2) m=(1+2) |
| ... | ... |
Что решилось довольно просто:
if [ "`expr $b % 2`" -gt "0" ]
then #Если ПАРА страниц нечётная
extract $ba first.bmp
extract $bb second.bmp
echo "Собираем странички $ba - $bb"
else #Если ПАРА страниц чётная
extract $bb first.bmp
extract $ba second.bmp
echo "Собираем странички $bb - $ba"
fi
Для вытягивания страниц из исходного PDF-ника использовался GhostScript в виде команды:
gs -q -sDEVICE=bmpmono -r$DPI -dFirstPage=$1 -dLastPage=$1 -sOutputFile="$TMP_DIR/$2" -dNOPAUSE -dBATCH *pdf
Поскольку у меня исходный PDF-ник был без полей (для читалки так удобнее), я добавил ImageMagic-ом поля вокруг страницы:
convert "$TMP_DIR/$2" -define bordercolor=#ffffff -border 10% "$TMP_DIR/$2"
Далее всё примитивно, используем программы пакета djvulibre:
if [ "$b" -eq "0" ]
then
cjb2 -lossy "$TMP_DIR/$ba-$bb".pbm "$2".djvu
else
cjb2 -lossy "$TMP_DIR/$ba-$bb".pbm "$TMP_DIR/$ba-$bb".djvu
djvm -i "$2".djvu "$TMP_DIR/$ba-$bb".djvu
fi
Создаём первую страничку файла тетради, затем последовательно цепляем к ней остальные странички до окончания цикла.
Сам скрипт
#!/bin/bash
letsgo(){
echo "Продолжим? (Yes/[No])"
read CONTINUE
if [ -n "$CONTINUE" ] && [[ "$CONTINUE" == y* ]] || [[ "$CONTINUE" == Y* ]] #Строка не пустая и начинается с "y" или "Y"
then echo "Ну что-ж, продолжаем..."
else
rm -R $TMP_DIR
exit
fi
}
extract(){ #FirstPage и LastPage=$1, OutputFile=$2 - БЕЗ ПРЕФИКСА!
gs -q -sDEVICE=bmpmono -r$DPI -dFirstPage=$1 -dLastPage=$1 -sOutputFile="$TMP_DIR/$2" -dNOPAUSE -dBATCH *pdf
convert "$TMP_DIR/$2" -define bordercolor=#ffffff -border 10% "$TMP_DIR/$2" #Добавляем рамку белого цвета
}
assembly(){
sub_a(){
for ((b=0; b<$1/2; b++)) do
let "ba=$3+$b" #номер первой страницы на листе
let "bb=$4-$b" #номер второй страницы на листе
if [ "`expr $b % 2`" -gt "0" ]
then #Если ПАРА страниц нечётная
extract $ba first.bmp
extract $bb second.bmp
echo "Собираем странич��и $ba - $bb"
else #Если ПАРА страниц чётная
extract $bb first.bmp
extract $ba second.bmp
echo "Собираем странички $bb - $ba"
fi
convert +append $TMP_DIR/first.bmp $TMP_DIR/second.bmp "$TMP_DIR/$ba-$bb".pbm
if [ "$b" -eq "0" ]
then
cjb2 -lossy "$TMP_DIR/$ba-$bb".pbm "$2".djvu
else
cjb2 -lossy "$TMP_DIR/$ba-$bb".pbm "$TMP_DIR/$ba-$bb".djvu
djvm -i "$2".djvu "$TMP_DIR/$ba-$bb".djvu
fi
rm "$TMP_DIR/$ba-$bb".*
done
}
if [ "$1" -gt "`expr $FILE_COUNT / $PAGES_IN_W_BOOK`" ]
then #сборка последней тетради
for ((a=0; a<1; a++)) do
let "aa=$1" #номер тетради
let "ab=$FILE_COUNT/$PAGES_IN_W_BOOK*$PAGES_IN_W_BOOK+1" #номер первой страницы в тетради
let "ac=$FILE_COUNT" #номер последней страницы в тетради
echo "Собираем тетрадь $aa состоящую из страниц $ab...$ac"
sub_a $2 $aa $ab $ac
done
else # нормальное выполнение
for ((a=0; a<$1; a++)) do
let "aa=$a+1" #номер итерации в текущем цикле он же - номер тетради
let "ab=$a*$2+1" #номер первой страницы в тетради
let "ac=$a*$2+$2" #номер последней страницы в тетради
echo "Собираем тетрадь $aa состоящую из страниц $ab...$ac"
sub_a $2 $aa $ab $ac
done
fi
}
mkdir /tmp/brochure_converter #Создаём временный каталог
TMP_DIR="/tmp/brochure_converter" #Назначаем переменную отражающую путь во временный каталог
FILE_COUNT=`pdfinfo *pdf | awk '/Pages/ {print $2}'` #Подсчитываем количество страниц в документе
echo "Укажите количество страниц в одной тетради кратное 4, например \"8, 12, 16\" и т.д."
read PAGES_IN_W_BOOK #Считываем переменную количества страниц в тетради
let "W_BOOK=$FILE_COUNT/$PAGES_IN_W_BOOK" #Вычисляем количество страниц в одной тетради
echo "Будет сформировано $W_BOOK полных тетрадей по $PAGES_IN_W_BOOK страниц в каждой"
let "TAIL_PAGES = $FILE_COUNT%$PAGES_IN_W_BOOK" #Вычисляем количество страниц в последней тетради
echo "Остаётся неполная тетрадь на $TAIL_PAGES страниц"
echo "Укажите требуемое разрешение изображений (DPI), например 72"
read DPI #Назначаем переменную разрешения изображений будущего документа
letsgo
assembly $W_BOOK $PAGES_IN_W_BOOK #Передаём в процедуру позиционные параметры количества тетрадей и количества страниц
echo "Выполнена сборка страниц основного набора, теперь можно собрать дополнительную тетрадь."
letsgo
let "LAST_W_BOOK=$W_BOOK+1" #Создаём переменную указывающую номер последней тетради
if [ "`expr $TAIL_PAGES % 4`" -eq "0" ] #Если количество страниц в последней тетради
then
echo "Всё нормально, продолжаем создание последней тетради"
assembly $LAST_W_BOOK $TAIL_PAGES
else
echo "В тетради недостаёт `expr 4 - $TAIL_PAGES % 4` страниц(ы)"
echo "Следует добавить их. Вы можете их добавить вручную или позволить это сделать программе."
letsgo
#добавляем необходимое количество пустых файлов после чего приступаем к сборке
fi
rm -R $TMP_DIR
Проблемы
gs -q -sDEVICE=bmpmono -r2x2 -sOutputFile=$TMP_DIR/%d.bmp -dNOPAUSE -dBATCH *pdf #Извлекаем страницы из PDF в малом разрешении FILE_COUNT=`ls -l $TMP_DIR/*bmp | grep ^- | wc -l` #Подсчитываем количество страниц в документе посредством подсчёта количества файлов в каталоге
Благодарю klirichek, теперь это выглядит так:
FILE_COUNT=`pdfinfo *pdf | awk '/Pages/ {print $2}'`Заключение
Скриптик сырой. Не доделана обработка PDF-ника, содержащего количество страниц не кратное 4, публиковаться изначально здесь не планировал, ибо чукча — читатель.
Планирую добавить запуск из командной строки с указанием параметров и пакетную обработку серии файлов, пока сие творение обрабатывает только один, хоть и указано "*pdf".
Первый опыт, просьба сильно не пинать.
За идеи и поправки заранее благодарен.