Как я могу определить разрешение отсканированного PDF-файла из сценария оболочки?

у меня есть большая коллекция документов, отсканированных в формате PDF, и я хочу написать shell-скрипт, который будет конвертировать каждый документ DjVu. Некоторые документы были отсканированы на 200dpi, некоторые на 300dpi, а некоторые на 600dpi. Поскольку DjVu-это пиксельный формат, я хочу быть уверен, что использую то же разрешение в целевом файле DjVu, что и при сканировании.

кто-нибудь знает, какую программу я могу запустить или как я могу написать программу, чтобы определить, какое разрешение использовался для создания отсканированного PDF? (Количество пикселей также может работать, так как почти все документы 8,5 на 11 дюймов.)


уточнение после ответов: я знаю о трудностях, выделенных Бретоном, и я готов признать, что проблема в целом плохо поставлена, но я не спрашиваю о общие PDF-документов. Мои документы были взяты со сканера. Они содержат одно отсканированное изображение на страницу, одинаковое разрешение каждой страницы. Если я преобразую PDF в PostScript я могу вручную копаться и легко находить размеры пикселей; вероятно, я мог бы найти размеры изображений с большей работой. И если в отчаянной необходимости я мог бы изменить стек словаря, который gs используется; давным-давно я написал интерпретатор для уровня PostScript 1.

все это то, чего я пытаюсь избежать.


благодаря полученной помощи я опубликовал ответ ниже:

  1. извлеките ограничивающую рамку из PDF с помощью identify, принимая только выходные данные для первой страницы и понимая, что единицы будут точками PostScript, из которых 72 на дюйм.
  2. извлечение изображений с первой страницы с помощью pdfimages.
  3. получить высоту и ширину изображения. На этот раз identify даст количество пикселей.
  4. добавить в общей сумме районах изображений, чтобы получить количество точек в квадрате.
  5. чтобы получить разрешение, вычислите области ограничивающей рамки в дюймах в квадрате, разделите точки в квадрате на дюймы в квадрате, возьмите квадратный корень и округлите до ближайшего кратного 10.

полный ответ со скриптом ниже. Я использую его в live fire, и он отлично работает. Спасибо Арлекину за pdfimages и Spiffeah для предупреждения о нескольких изображениях на странице (это редко, но я нашел некоторые).

7 ответов


Я думаю, что сканирование включено в качестве изображений в PDF, поэтому вы можете использовать pdfimages сначала извлечь их. Затем:identify должны быть в состоянии найти правильные данные.


Если pdf был создан путем сканирования, то должно быть только одно изображение, связанное с каждой страницей. Вы можете найти каждое разрешение изображения для каждого изображения страницы, проанализировав pdf с помощью iText(Java) или iTextSharp(порт .net) библиотеки легко.

Если вы хотите свернуть свою собственную утилиту для этого, сделайте что-то вроде следующего в iTextSharp :

PdfReader reader = new PdfReader(filename);
for (int i = 1; i <= reader.NumberOfPages; i++)
{
PdfDictionary pg = reader.GetPageN(i);
PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
PdfDictionary xobjs = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));
if (xobjs != null) 
{
    foreach (PdfName xObjectKey in xobjs.Keys)
    {
    PdfObject xobj = xobjs.Get(xObjectKey);
    PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(xobj);
    PdfName subtype = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
    if  (subtype.Equals(PdfName.IMAGE))
    {
        PdfNumber width = (PdfNumber)tg.Get(PdfName.WIDTH);
        PdfNumber height = (PdfNumber)tg.Get(PdfName.HEIGHT);
        MessageBox.Show("image on page [" + i + "] resolution=[" + width +"x" + height + "]");
    }
    }
}
}   
reader.Close();

здесь для каждой страницы мы читаем через каждый XObject изображения подтипа и получите значения ширины и высоты. Это будет пиксельное разрешение изображения, которое сканер встроил в pdf-файл.

обратите внимание, что масштабирование этого изображения в соответствии с разрешением страницы (как и в размере страницы, отображаемой в Acrobat - A4, Letter и т. д.) выполняется отдельно в потоке содержимого страницы, который представлен как подмножество postscript, и гораздо сложнее найти без разбора postscript.

имейте в виду, что есть некоторые сканеры, которые будут вставлять отсканированное изображение в виде сетки меньших изображений (для какой-то оптимизации размера я предполагаю). Поэтому, если вы видите что-то вроде 50 маленьких изображений, появляющихся на каждой странице, это может быть причиной.

надеюсь, это каким-то образом поможет, если вам нужно свернуть свою собственную утилиту.


вот элементы этого ответа:

  • pdfimages будет извлекать изображения, так что количество точек может быть обнаружено.
  • identify даст размер изображения в единицах PostScript точек (72 на дюйм)
  • поскольку некоторые сканеры могут разделить одну страницу на несколько изображений разных размеров и форм, ключ должен добавить районах всех изображений. Деление квадратных точек на квадратные дюймы и взятие квадрата корень дает ответ.

Ниже приведен сценарий Lua, который решает проблему. Вероятно, я мог бы использовать простую оболочку, но захват ширины и высоты был бы большей неприятностью.

#!/usr/bin/env lua

require 'osutil'
require 'posixutil'
require 'mathutil'

local function runf(...) return os.execute(string.format(...)) end

assert(arg[1], "no file on command line")

local function dimens(filename)
  local cmd = [[identify -format "return %w, %h\n" $file | sed 1q]]
  cmd = cmd:gsub('$file', os.quote(filename))
  local w, h = assert(loadstring(os.capture(cmd)))()
  assert(w and h)
  return w, h
end

assert(#arg == 1, "dpi of just one file")

for _, pdf in ipairs(arg) do
  local w, h = dimens(pdf)  -- units are points
  local insquared = w * h / (72.00 * 72.00)
  local imagedir = os.capture 'mktemp -d'
  assert(posix.isdir(imagedir))
  runf('pdfimages -f 1 -l 1 %s %s 1>&2', os.quote(pdf),
                                         os.quote(imagedir .. '/img'))
  local dotsquared = 0
  for file in posix.glob(imagedir .. '/img*') do
    local w, h = dimens(file)  -- units are pixels
    dotsquared = dotsquared + w * h
  end
  os.execute('rm -rf ' .. os.quote(imagedir))
  local dpi = math.sqrt(dotsquared / insquared)

  if true then
    io.stderr:write(insquared, " square inches\n")
    io.stderr:write(dotsquared, " square dots\n")
    io.stderr:write(dpi, " exact dpi\n")
    io.stderr:write(math.round(dpi, 10), " rounded dpi\n")
  end
  print(math.round(dpi, 10))
end

pdfimages есть -list опция, которая дает ширину высоты в пикселях, а также y-ppi и x-ppi.

 pdfimages -list tmp.pdf           
page   num  type   width height color comp bpc  enc interp  object ID x-ppi y-ppi size ratio
--------------------------------------------------------------------------------------------
   1     0 image    3300  2550  gray    1   1  ccitt  no       477  0   389   232  172K  17%
   2     1 image    3300  2550  gray    1   1  ccitt  no         3  0   389   232  103K  10%
   3     2 image    3300  2550  gray    1   1  ccitt  no         7  0   389   232  236K  23%
   4     3 image    3300  2550  gray    1   1  ccitt  no        11  0   389   232  210K  20%
   5     4 image    3300  2550  gray    1   1  ccitt  no        15  0   389   232  250K  24%
   6     5 image    3300  2550  gray    1   1  ccitt  no        19  0   389   232  199K  19%
   7     6 image    3300  2550  gray    1   1  ccitt  no        23  0   389   232  503K  49%
   8     7 image    3300  2550  gray    1   1  ccitt  no        27  0   389   232  154K  15%
   9     8 image    3300  2550  gray    1   1  ccitt  no        31  0   389   232 21.5K 2.1%
  10     9 image    3300  2550  gray    1   1  ccitt  no        35  0   389   232  286K  28%
  11    10 image    3300  2550  gray    1   1  ccitt  no        39  0   389   232 46.8K 4.6%
  12    11 image    3300  2550  gray    1   1  ccitt  no        43  0   389   232 55.5K 5.4%
  13    12 image    3300  2550  gray    1   1  ccitt  no        47  0   389   232 35.0K 3.4%
  14    13 image    3300  2550  gray    1   1  ccitt  no        51  0   389   232 26.9K 2.6%
  15    14 image    3300  2550  gray    1   1  ccitt  no        55  0   389   232 66.5K 6.5%
  16    15 image    3300  2550  gray    1   1  ccitt  no        59  0   389   232 73.9K 7.2%
  17    16 image    3300  2550  gray    1   1  ccitt  no        63  0   389   232 47.0K 4.6%
  18    17 image    3300  2550  gray    1   1  ccitt  no        67  0   389   232 30.1K 2.9%
  19    18 image    3300  2550  gray    1   1  ccitt  no        71  0   389   232 70.3K 6.8%
  20    19 image    3300  2550  gray    1   1  ccitt  no        75  0   389   232 46.0K 4.5%
  21    20 image    3300  2550  gray    1   1  ccitt  no        79  0   389   232 28.9K 2.8%
  22    21 image    3300  2550  gray    1   1  ccitt  no        83  0   389   232 72.7K 7.1%
  23    22 image    3300  2550  gray    1   1  ccitt  no        87  0   389   232 47.5K 4.6%
  24    23 image    3300  2550  gray    1   1  ccitt  no        91  0   389   232 30.1K 2.9%

слишком долго, чтобы поместить в комментарий, но ни ImageMagick, ни GraphicsMagic не подходят для этой работы;каждый ответ неправильный:

: nr@yorkie 1932 ; gm identify -format "x=%x y=%y w=%w h=%h" drh*rec*pdf
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792
x=0 y=0 w=612 h=792

: nr@yorkie 1933 ; identify -format "x=%x y=%y w=%w h=%h" drh*rec*pdf   
x=72 Undefined y=72 Undefined w=612 h=792x=72 Undefined y=72 Undefined     w=612 h=792x=72 Undefined y=72 Undefined w=612 h=792x=72 Undefined     y=72 Undefined w=612 h=792x=72 Undefined y=72 Undefined w=612     h=792x=72 Undefined y=72 Undefined w=612 h=792x=72 Undefined y=72     Undefined w=612 h=792x=72 Undefined y=72 Undefined w=612 h=792
: nr@yorkie 1934 ; 

правильными параметрами для этого документа является то, что каждая отсканированная страница имеет ширину 5100 пикселей и высоту 6600 пикселей, неудивительно, что это был 8.5-by-11, отсканированный на 600dpi. Выход из ImageMagic удивительно непрофессионально.

нет downvotes, потому что вы пытались быть полезными, но *Magick не работают.


PDF-это независимый от разрешения формат, это бессмысленный вопрос. Возможно, вы сканировали некоторые растровые изображения с определенным разрешением, и эти растровые изображения индивидуально встроены в pdf, но сам PDF может содержать изображения с несколькими разрешениями, а также независимую от разрешения векторную графику. Нет никакого способа узнать, не взломав pdf-файл и не изучив каждый объект внутри него.

редактирование для продолжения изложения проблемы:

вы можете повезло, и программное обеспечение, которое вы использовали для сканирования документов, внедрило некоторые метаданные об этом, но не ставьте на это. Такие метаданные вряд ли будут стандартными. Насколько парсинга PDF, вы хотели заготовки библиотеке - таких как Ghostscript. Проблема в том, что PDF-это не формат, а определенное подмножество языка программирования PostScript и согласованный способ сжатия/компиляции этого подмножества вместе с некоторыми двоичными файлами. Таким образом, чтение PDF сложнее чем другие типы форматов изображений, так как это предполагает написание интерпретатора языка - не так просто.

лучший подход - либо поднять руки и сдаться, либо действительно посмотреть на ghostscript и посмотреть, сможете ли вы получить ответ.


PDF-шпион Apago расскажет вам об остром разрешении изображений в PDF вместе с множеством других вещей. Это коммерческий продукт, но имеет 10-дневную демонстрацию.