PDF сравнить в командной строке Linux
Я ищу инструмент командной строки Linux для сравнения двух файлов PDF и сохранения различий в файл PDF. Инструмент должен создавать diff-pdf в пакетном процессе. PDF-файлы - это планы строительства, поэтому чистое сравнение текста не работает.
Что-то вроде:
<tool> file1.pdf file2.pdf -o diff-out.pdf
Большинство инструментов, которые я нашел, преобразовывают PDF-файлы в изображения и сравнивают их, но только с графическим интерфейсом.
Любое другое решение также приветствуется.
5 ответов
Совершено в 2 строки с (всемогущим) imagemagick и pdftk:
compare -verbose -debug coder $PDF_1 $PDF_2 -compose src $OUT_FILE.tmp
pdftk $OUT_FILE.tmp background $PDF_1 output $OUT_FILE
Опции -verbose и -debug являются необязательными.
- сравнить создает PDF-файл с diff в виде красных пикселей.
- pdftk объединяет diff-pdf с фоном PDF_1
Я написал свой собственный сценарий, который делает что-то похожее на то, что вы просите. Скрипт использует 4 инструмента для достижения своей цели:
- ImageMagick-х
compare
команда pdftk
утилита (если у вас есть многостраничные PDF-файлы)- Ghostscript (необязательно)
md5sum
(необязательный)
Должно быть довольно легко перенести это на .bat
командный файл для DOS/Windows.
Но сначала обратите внимание: это хорошо работает только для PDF-файлов с одинаковым размером страницы / носителя. Сравнение выполняется попиксельно между двумя входными PDF-файлами. Результирующий файл представляет собой изображение, показывающее "diff" следующим образом:
- Каждый пиксель, который остается неизменным, становится белым.
- Каждый измененный пиксель окрашен в красный цвет.
Это разностное изображение сохраняется в виде нового PDF-файла, чтобы сделать его более доступным на разных платформах ОС.
Я использую это, например, чтобы обнаружить минимальные различия в отображении страницы, когда в игру вступает подмена шрифта при обработке PDF.
Может случиться, что между вашими PDF нет видимой разницы, хотя они различаются по хэшам MD5 и / или размеру файла. В этом случае PDF-страница вывода diff будет полностью белой. Вы можете автоматически обнаружить это условие, поэтому вам нужно только визуально исследовать небелые PDF-файлы, автоматически удаляя полностью белые.
Вот строительные блоки:
Pdftk
Используйте эту утилиту командной строки, чтобы разбить многостраничные PDF-файлы на несколько одностраничных PDF-файлов:
pdftk file_1.pdf burst output somewhere/file_1---page_%03d.pdf
pdftk file_2.pdf burst output somewhere/file_2---page_%03d.pdf
Если вы сравниваете только одностраничные PDF-файлы, этот строительный блок не является обязательным. Поскольку вы говорите о "планах строительства", это, скорее всего, так.
сравнить
Используйте эту утилиту командной строки из ImageMagick для создания PDF-страницы "diff" для каждой из страниц:
compare \
-verbose \
-debug coder \
-log "%u %m:%l %e" \
somewhere/file_1---page_001.pdf \
somewhere/file_2---page_001.pdf \
-compose src \
somewhereelse/file_1--file_2---diff_page_001.pdf
Ghostscript
Из-за автоматически вставленных метаданных (таких как текущая дата + время) вывод PDF не работает хорошо для сравнений файлов на основе MD5hash.
Если вы хотите автоматически обнаружить все случаи, когда diff PDF состоит из чисто белой страницы, вам следует преобразовать страницу PDF в формат точечных рисунков без метаданных, используя bmp256
устройство вывода. Вы можете сделать это так:
Сначала выясните, какой формат страницы у вас в PDF. Опять эта маленькая утилита identify
поставляется как часть любой установки ImageMagick:
identify \
-format "%[fx:(w)]x%[fx:(h)]" \
somewhereelse/file_1--file_2---diff_page_001.pdf
Вы можете сохранить это значение в переменной окружения следующим образом:
export my_size=$(identify \
-format "%[fx:(w)]x%[fx:(h)]" \
somewhereelse/file_1--file_2---diff_page_001.pdf)
Теперь Ghostscript вступает в игру, используя командную строку, которая включает обнаруженный выше размер страницы, который хранится в переменной:
gs \
-o somewhereelse/file_1--file_2---diff_page_001.ppm \
-sDEVICE=ppmraw \
-r72 \
-g${my_size} \
somewhereelse/file_1--file_2---diff_page_001.pdf
Это дает вам PPM (Portable PixMap) с разрешением 72 точек на дюйм от оригинальной страницы PDF. 72 dpi обычно достаточно для того, что мы хотим... Затем создайте чисто белую страницу PPM с таким же размером страницы:
gs \
-o somewhereelse/file_1--file_2---whitepage_001.ppm \
-sDEVICE=ppmraw \
-r72 \
-g${my_size} \
-c "showpage"
-c "showpage"
part - это команда PostScript, которая указывает Ghostscript выдавать только пустую страницу.
Сумма MD5
Используйте хеш MD5 для автоматического сравнения исходного PPM с PPM для белой страницы. В случае, если они одинаковы, вы можете предположить, что между PDF-файлами нет различий, и поэтому переименуйте или удалите diff-PDF:
MD5_1=$(md5sum somewhereelse/file_1--file_2---diff_page_001.ppm | awk '{print $1}')
MD5_2=$(md5sum somewhereelse/file_1--file_2---whitepage_001.ppm | awk '{print $1}')
if [ "x${MD5_1}" == "x${MD5_2}" ]; then
mv \
somewhereelse/file_1--file_2---diff_page_001.pdf \
somewhereelse/file_1--file_2---NODIFFERENCE_page_001.pdf # rename all-white PDF
rm \
somewhereelse/file_1--file_2---*_page_001.ppm # delete both PPMs
fi
Это избавляет вас от необходимости визуально проверять "различные PDF-файлы", которые не имеют никаких различий.
Вот взломать, чтобы сделать это.
pdftotext file1.pdf
pdftotext file2.pdf
diff file1.txt file2.txt
В 2022 году ответы, основанные на применении непосредственно к файлам PDF, у меня не работают. Кажется, что эта команда больше не обрабатывает PDF-файлы должным образом.
Однако работает применительно к файлам PNG.
Я взял кусочки из предыдущих ответов, чтобы написать другой сценарий. По сути, это два разных скрипта, делающих немного разные вещи: и , которые должны выполняться в командной строке. Оба сценария перечислены в конце этого ответа.
Некоторые предостережения
Эти два скрипта сравнивают два PDF-файла постранично, и каждая пара страниц сравнивается чисто визуально (поскольку страницы конвертируются в PNG). Поэтому скрипты чувствительны только к плоскому тексту и плоской графике. Если единственное различие между двумя файлами PDF касается какого-либо другого содержимого PDF, например элементов логической структуры, аннотаций, полей форм, слоев, видео, 3D-объектов (U3D или PRC) и т. д., оба сценария, тем не менее, сообщат, что два PDF-файла одинаковы.
Я не пытался сравнивать PDF-файлы конкретно по части этого «дополнительного» контента.
Как узнать, имеют ли два файла (PDF или нет) полностью идентичное содержимое
Единственный другой вид сравнения, который я умею делать, — это тот, который позволяет нам узнать, полностью ли содержимое двух PDF-файлов идентично во всех отношениях, включая различные встроенные метаданные , такие как дата создания, заголовок документа (который не имеет ничего общего с каким-либо заголовком, отображаемым на первой странице), программа, используемая для создания PDF, и так далее.
Это тот же метод, который можно использовать для проверки того, идентичны ли любые два файла (PDF или нет).
Для этого все, что вам нужно сделать, это вычислить и сравнить контрольные суммы для двух файлов. Я также включаю сценарий для этого, который называется . Он указан в самом конце этого вопроса. Вот как это использовать.
Предположим, что два файла называются «my_first_PDF_file.pdf» и «another_PDF_file.pdf». Затем, как только вы выполните следующее в командной строке, выходной текст будет читаться как «одинаковый» или «различный» в зависимости от того, одинаковы ли два файла или разные.
AreIdentical.sh my_first_PDF_file.pdf another_PDF_file.pdf
Обратите внимание, что такая информация, как имя файла, не учитывается при вычислении контрольных сумм. Причина в том, что имя файла хранится не в самом файле, а в записи каталога файла. Таким образом, два файла могут оказаться идентичными, даже если их имена файлов различаются; см. этот вопрос . Точно так же дата создания, возвращенная
ls -l
(в отличие от встроенных метаданных PDF) также не учитывается при вычислении контрольных сумм по той же причине.
Как пользоваться скриптами и
Мы предполагаем, что два сравниваемых (чисто визуально) pdf-файла, file1.pdf и file2.pdf, находятся в рабочем каталоге.
В качестве примера предположим, что у них обоих по 4 страницы и что все страницы идентичны, кроме страницы 3.
Чтобы сделать именно то, о чем просил ОП,
в командной строке выполняем
ComparePdfs2.sh file1.pdf file2.pdf dif_in_files.pdf
где я выбрал конкретное имя для выходного файла. Выполнение занимает немного времени, потому что для обоих входных PDF-файлов каждая отдельная страница должна быть преобразована в PNG. Текущая обрабатываемая страница печатается в терминале. В конце в рабочем каталоге скрипт создаст файл
dif_in_files.pdf
, который содержит страницы различий для всех страниц. Любые различия выделены красным цветом.
Если нам интересно только увидеть разные страницы или только узнать , отличаются ли они , то мы используем .
В командной строке выполняем
ComparePdfs.sh file1.pdf file2.pdf
В терминале скрипт выведет следующее:
page_001: same
page_002: same
page_003: different
page_004: same
Для тех страниц, которые получились разными, и только для этих страниц скрипт создаст файлы, выделяющие отличия. В приведенном выше примере сценарий сгенерирует только один файл с именем
difference_page_003.png
.
Как работает
Для каждого из двух файлов PDF мы используем pdftk, чтобы разбить его на отдельные страницы, а затем преобразовать каждую страницу в PNG. Теперь рассмотрим PNG первых страниц двух файлов. Мы создаем контрольную сумму для каждого (я решил использовать
b2sum
сделать это).
Если контрольные суммы совпадают, мы считаем, что первые страницы двух файлов одинаковы.
Если контрольные суммы разные, мы берем первые страницы двух файлов разными и используем
compare
чтобы сгенерировать для них различный PNG-файл.
Повторяем это для каждой страницы. В конце мы стираем все файлы .pdf и .png отдельных страниц, кроме файлов различий.
Скрипты
Вот
ComparePdfs2.sh
.
#!/bin/bash
file_1="$1"
file_2="$2"
outfile="$3"
# here we set the DPI resolution for the pdftoppm command, which will convert PDF to PNG
resolution=150
# bursting the files into individual pages
pdftk $file_1 burst output ${file_1%.*}---page_%03d.pdf
pdftk $file_2 burst output ${file_2%.*}---page_%03d.pdf
# this will be a string variable in which we collect that names of .png files to be converted to a single .pdf file
DiffFiles=""
# we loop over the individual pages of the first file
for f1 in `echo ${file_1%.*}---`*.pdf
do
# f2 is the name of the PDF of the corresponding page of the second file
f2="${f1/${file_1%.*}/${file_2%.*}}"
# 'b' is an auxilliary varable used to create the variable 'page'
b="${f1/${file_1%.*}---/""}"
# 'page' hold the current page number, e.g. 'page_003'
page="${b/.pdf/}"
# print the current page being processed
echo -n "$page "
# convert the individual page PDFs to PNGs
pdftoppm "$f1" "${f1%.*}" -png -r $resolution
pdftoppm "$f2" "${f2%.*}" -png -r $resolution
# 'g1' and 'g2' are the names of the two PNG files we just created
g1=${f1%.*}-1.png
g2=${f2%.*}-1.png
# create the difference file for this page
compare "$g1" "$g2" ${outfile%.*}_"$page".png
# add the latest name of the difference .png file to the DiffFiles variable
DiffFiles=$DiffFiles""${outfile%.*}_"$page".png" "
done
echo
# convert the .png difference files to a single .pdf file
convert $DiffFiles $outfile
# clean up
rm -f `echo ${file_1%.*}---page_`* `echo ${file_2%.*}---page_`* `echo ${outfile%.*}_page_`* doc_data.txt
Вот
ComparePdfs.sh
#!/bin/bash
file_1="$1"
file_2="$2"
# here we set the DPI resolution for the pdftoppm command, which will convert PDF to PNG
resolution=150
# bursting the files into individual pages
pdftk $file_1 burst output ${file_1%.*}---page_%03d.pdf
pdftk $file_2 burst output ${file_2%.*}---page_%03d.pdf
# we loop over the individual pages of the first file
for f1 in `echo ${file_1%.*}---`*.pdf
do
# f2 is the name of the PDF of the corresponding page of the second file
f2="${f1/${file_1%.*}/${file_2%.*}}"
# 'b' is an auxilliary varable used to create the variable 'page'
b="${f1/${file_1%.*}---/""}"
# 'page' hold the current page number, e.g. 'page_003'
page="${b/.pdf/}"
# convert the individual page PDFs to PNGs
pdftoppm "$f1" "${f1%.*}" -png -r $resolution
pdftoppm "$f2" "${f2%.*}" -png -r $resolution
# 'g1' and 'g2' are the names of the two PNG files we just created
g1=${f1%.*}-1.png
g2=${f2%.*}-1.png
# create the checksums for the two PNG files
B2S_1=$(b2sum "$g1" | awk '{print $1}')
B2S_2=$(b2sum "$g2" | awk '{print $1}')
# now we compare the checksums
if [ "$B2S_1" = "$B2S_2" ]; then
echo "$page: same";
else
echo "$page: different";
# if the checksums are different, create a difference PNG image
compare "$g1" "$g2" difference_"$page".png
fi
done
# clean up
rm -f `echo ${file_1%.*}---page_`* `echo ${file_2%.*}---page_`* doc_data.txt
Наконец, вот
AreIdentical.sh
:
#!/bin/bash
file_1="$1"
file_2="$2"
B2S_1=$(b2sum $file_1 | awk '{print $1}')
B2S_2=$(b2sum $file_2 | awk '{print $1}')
if [ "$B2S_1" = "$B2S_2" ]; then echo "same"; else echo "different"; fi
Вот готовый скрипт «cmppdf», основанный на коде Linguisticturn, плюс поддержка сравнения текста в PDF-файлах и некоторые доработки:
https://abhweb.org/jima/cmppdf
Документация:
NAME
cmppdf -- Compare the visual appearance or text of PDF files
SYNOPSIS
cmppdf [-o BASEPATH] [-q] [-d] FILE1 FILE2
cmppdf --text [-o BASEPATH] [-q] [-d] FILE1 FILE2
EXIT STATUS
0 if no differences found
1 if differences found
2+ if trouble
OPTIONS
-t, --text Compare the text in the PDFs, ignoring grapical appearance.
-o, --output BASEPATH
With this option a "difference file" named BASEPATH_page_NNN.png
or .txt is created for each page which has differences.
With visual comparison (the default), the files will be .png images with
changed parts highlighted in RED. With text comparison (--text option),
the files will contain output from the 'diff' command, or if BASEPATH
is '-' then all diffs are written to stdout.
--diff diff-option1,diff-option2, ...
Specify options to pass to the 'diff' command, separated by commas.
The default is '-u'. --text is implied by --diff.
-q, --quiet Suppress all progress messages
-d, --debug Show detailed information about commands run
@linguisticturn: Пожалуйста, свяжитесь со мной по электронной почте, указанной в сценарии, чтобы я мог отдать вам должное!