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 инструмента для достижения своей цели:

  1. ImageMagick-х compare команда
  2. pdftk утилита (если у вас есть многостраничные PDF-файлы)
  3. Ghostscript (необязательно)
  4. 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: Пожалуйста, свяжитесь со мной по электронной почте, указанной в сценарии, чтобы я мог отдать вам должное!

Другие вопросы по тегам