Доброго времени суток, уважаемые форумчане!
Обозначу проблему: Часто по работе приходится заниматься сжатием документов pdf. Типовая задача: имеем отсканированный файл в приличном качестве с кому-то-очень-нужными-данными-сию-секунду, в котором чуть менее чем совсем туча страниц, и надо все это отправить по электронной почте, а вложения более 5 Мб почтовый сервер отбрасывает. По сути, выручает пакет ImageMagic с его замечательным convert. Однако, практика показывает, что для достижения приемлемого для чтения качества в купе с малым размером недостаточно просто выполнить
convert исходный_большой_файл.pdf -quality 30% -resize 30% -colorspace Gray маленький_файл.pdf
страницы файла на выходе получаются замыленными вплоть до полной нечитабельности в отдельных случаях.
Пример страницы из PDF-документа до обработок (1.6 Мб, конфиденциальное я замылил, - символы оставил лишь для оценки их читаемости):
Пример этой же страницы после обработки указанной выше командой (11 кБ, страница не читаема от слова "совсем", - ничего не исправлял):
Пример этой же страницы после обработки в соответствии с принятым в данном топике алгоритмом (146 кБ, страница читаема, конфиденциальное замазано):
Гораздо лучшее качество на практике дает такой алгоритм:
- Извлечь изображения из PDF-документа
- Каждое изображение в отдельности обработать при помощи convert, приведя к jpg (дает малый размер выходного pdf-файла) и применив к каждому изображению параметры сжатия
- Все изображения склеить в новый pdf-документ, который переименовать по аналогии с старым
Будучи человеком от природы ленивым, проделав все эти манипуляции пару раз, я пришел к выводу о необходимости составления скрипта, который будет выполнять это автоматически. Кроме того, с поправкой на используемое DE (Kubuntu 18.04), скрипт должен быть "интегрирован" в оболочку, дабы можно было вызывать его прямо в файловом менеджере из контекстного меню.
Предлагаемое решение не блещет ни оригинальностью, ни изяществом (поясняю: я не программист - мне главное чтоб работало), вместе с тем работоспособно, а потому предлагаю его вниманию сообщества, дабы страждущие вроде меня могли его "нагуглить" и пользовать в свое удовольствие. Для установки необходимо и достаточно создать два текстовых файла и присвоить им права. В случае если зависимости удовлетворены (см. коментарий скрипта) работа сразу возможна; если не удовлетворены - выполнить команду из комментария скрипта .sh и далее проблем быть не должно).
#!/bin/bash
# Скрипт для выполнения сжатия pdf-файлов, состоящих из изображений
# на pdf из текста не работает, - текст игнорируется,
# распаковываются и берутся в работу только изображения
# Зависимости [второй пакет присутствует в системе по-умолчанию]:
# sudo apt install imagemagick poppler-utils
# Работа проверялась на KUbuntu 18.04.1
# Возможные режимы работы:
# 1. вызов из командной строки (есть своего рода консольный интерфейс)
# 2. вызов с параметром командной строки (в качестве параметра передать имя обрабатываемого файла)
#Глобальные переменные:
img_format='jpg' #формат, через который делаем сжатие. Варианты: jpg, png
img_quality=30 #качество выходных изображений (1-100)
img_resize='30%' #размер выходных изображений в процентах от исходных (1%-100%)
img_colorspace='Gray' #цветовое пространство выходных изображений: RGB, HSL, CMYK, Gray, ...
fName="" #имя обрабатываемого файла
#Главная процедура скрипта
MainSub() {
if [ -z "$fName" ]; then #если имя файла пустое - показываем интерфейс, где даем возможность ввести имя файла
while true; do
clear #экран выбора pdf-файла
echo "Введите полный путь к PDF-файлу, который Вы хотите сжать"
echo "Примечание: 1. клавиша TAB выполняет автодополнение ввода"
echo " 2. для выхода наберите exit"
read -e newFileName
#проверка ввода пользователя:
#1. Ввод не пустой
if [ -z "$newFileName" ]; then
echo -e "[!!!] Пустой ввод. Вы должны ввести полный путь к файлу!"
read -rsn1 -p "Для продолжения нажмите любую клавишу..."
else
#если набрал 'exit' то выходим
if [[ $newFileName == "exit" ]]; then
echo "Выход по инициативе пользователя"
exit 0
break
fi
#2. Файл существует
if [ ! -f "$newFileName" ]; then
echo -e "[!!!] Некорректное имя файла. Заданный файл не существует, или не может быть найден!"
read -rsn1 -p "Для продолжения нажмите любую клавишу..."
else
#3. Файл действительно pdf
newFileFileType=`xdg-mime query filetype "$newFileName"`
if [[ $newFileFileType == "application/pdf" ]]; then
#все условия выполнены - можем продолжить работу
fName=$newFileName
break
else
echo -e "[!!!] Заданный файл не является файлом PDF!"
read -rsn1 -p "Для продолжения нажмите любую клавишу..."
fi; fi; fi
done
else #если имя файла не пустое - принимаем за имя файла переданный параметр, и работаем с ним
newFileFileType=`xdg-mime query filetype "$fName"`
if [[ $newFileFileType != "application/pdf" ]]; then
echo "[!!!] НЕ PDF-файл"
if [[ $XDG_CURRENT_DESKTOP = "KDE" ]]; then kdialog --icon application-pdf --title "Ошибка" --passivepopup "Исходный файл: `basename "$fName"`\nНе является файлом PDF!"; fi
exit 1
break
else
echo "PDF-файл"
fi
fi
#с этого места начинаем работать с файлом
pathname=`dirname "$fName"` #получаем имя каталога из полного имени файла
if [[ $pathname == "." ]]; then pathname=`pwd`; fi
justname=`basename "$fName"` #получаем имя файла из полного его имени
mkdir -p "$pathname/BufImgDir" #создаем буферную папку
pdfimages "$fName" "$pathname/BufImgDir/page" #Распаковываем изображения
echo "$fName"
echo "$pathname/BufImgDir/page"
SAVEIFS=$IFS #сохраняем текущий системный разделитель IFS
IFS=$'\n' #заменяем системный разделитель на символ перевода строки (используется в выводе find)
image_list=$( find -L "$pathname" -maxdepth 2 -name "*.ppm" | sort ) #обнаруживаем созданные файлы
image_list=( $(echo -e "$image_list") ) #представляем все массивом
#если в pdf не нашлось изображений:
echo "${#image_list[@]}"
if [[ ${#image_list[@]} == 0 ]]; then
echo "[!!!] Распаковка заданного файла не дает изображений"
if [[ $XDG_CURRENT_DESKTOP = "KDE" ]]; then kdialog --icon emblem-error --title "Ошибка" --passivepopup "Исходный файл: `basename "$fName"`\nНе состоит из изображений!"; fi
rm -rf "$pathname/BufImgDir" #удаляем буферную папку
exit
fi
for (( i=0; i<${#image_list[@]}; i++ ))
do
curfile=$(basename -- "${image_list[i]}") #имя текущего файла
newname=${curfile/'.ppm'/".$img_format"} #имя файла после преобразования
curfile="$pathname/BufImgDir/$curfile" #подставляем пути...
newname="$pathname/BufImgDir/$newname"
echo $newname
convert $curfile -quality $img_quality -resize $img_resize -colorspace $img_colorspace "$newname"
done
IFS=$SAVEIFS #возвращаем системный разделитель в исходное состояние
#конвертируем полученные сжатые изображения в pdf:
convert "$pathname/BufImgDir/*.$img_format" -colorspace $img_colorspace "$pathname/BufImgDir/output.pdf"
justname="compress_$justname" #вычисляем имя для созданного файла
mv "$pathname/BufImgDir/output.pdf" "$pathname/$justname" #переименовываем созданный файл
rm -rf "$pathname/BufImgDir" #удаляем буферную папку
oldSize=`du -sh "$fName" | awk '{print $1}'`
newSize=`du -sh "$pathname/$justname" | awk '{print $1}'`
#В KDE пользователю будет показано уведомление следующего вида:
if [[ $XDG_CURRENT_DESKTOP = "KDE" ]]; then kdialog --icon application-pdf --title "Сгенерирован файл" --passivepopup "Исходный файл: `basename "$fName"` [ $oldSize ]\n\nСозданный файл: $justname [ $newSize ]"; fi
}
#проверяем - переданы ли параметры при вызове скрипта
if [ -f "$1" ]; then
fName=$1
MainSub
else
MainSub
fi
exit 0
Для добавления пункта в контекстное меню dolphin был создан еще один файл:
[Desktop Action compress_pdf]
Exec=~/.local/share/kservices5/ServiceMenus/pdf_comptress.sh %f
Icon=application-pdf
Name=Сжать PDF-файл [набор изображений]
[Desktop Entry]
Actions=compress_pdf;
Encoding=UTF-8
Icon=application-pdf
MimeType=application/pdf;
Type=Service
X-KDE-ServiceTypes=KonqPopupMenu/Plugin
Файлу ~/.local/share/kservices5/ServiceMenus/pdf_comptress.sh следует дать права на исполнение.
Файлу ~/.local/share/kservices5/ServiceMenus/compress_pdf.desktop права не требуются.
Результат применения: в файловом менеджере находим интересующий нас PDF-файл, кликаем на нем правой кнопкой мышки, ищем пукнт меню "Действия" -> "Сжать PDF-файл [набор изображений]". Кликаем. Ожидаем окончания работы скрипта. В KDE по завершении будет выведено уведомление.
Вызываем, сделав ПКМ на файле PDF:
Финал работы:
Для примера взял один из своих недавно сканированных файлов. На входе 18 Мб, на выходе 1,4 Мб. В почту пролезает без проблем и читается неплохо.