Формируем тетради для книги в автоматическом режиме

в 10:45, , рубрики: imagemagick, linux, PDF, графика, ненормальное программирование

С чего всё началось

Началось всё с лени. Лень бывает продуктивная и не очень, в моём случае, похоже — первое.

Недавно прикупил на «Литресе» книжку Нассима Талеба «Антихрупкость». Скачал, закинул на читалку, да так до неё и не добрался, то жена утащит своё почитать, то ребёнок… А тут в рассылке прилетела вот эта статья «Лучший подарок – книга. Делаем красивый переплет». Посмотрел, всё вроде красиво, только вот под рукой не оказалось машины с «Форточками», ну не пользуемся мы ими, а запускать эти 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++)).

Попутно выяснилось, что часть кода лучше вынести в отдельные функции, да и bash делит всё нацело, а это значит, что нужно дорабатывать сборку остатков страниц, что оказалось примитивно. На закуску выяснилось, что порядок страниц на листах не совсем тот, как был в табличке выше, а вот такой:

Пары страниц зависимость
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"

Мам скрипт

#!/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" #Назначаем переменную отражающую путь во временный каталог 
gs -q -sDEVICE=bmpmono -r2x2 -sOutputFile=$TMP_DIR/%d.bmp -dNOPAUSE -dBATCH *pdf #Извлекаем страницы из PDF в малом разрешении
FILE_COUNT=`ls -l $TMP_DIR/*bmp | grep ^- | wc -l` #Подсчитываем количество страниц в документе посредством подсчёта количества файлов в каталоге
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` #Подсчитываем количество страниц в документе посредством подсчёта количества файлов в каталоге

Поэтому буду благодарен за подсказку как узнать количество страниц в PDF-нике не распаковывая его.

Заключение

Скриптик сырой. Не доделана обработка PDF-ника, содержащего количество страниц не кратное 4, публиковаться изначально здесь не планировал, ибо чукча — читатель.

Первый опыт, просьба сильно не пинать.

За идеи и поправки заранее благодарен.

Автор: lexx_z

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js