如何在shell脚本中分辨扫描的PDF?

时间:2009-01-17 02:02:19

标签: pdf shell

我有大量的文档扫描成PDF格式,我希望编写一个shell脚本,将每个文档转换为DjVu格式。一些文件在200dpi扫描,一些在300dpi扫描,一些在600dpi扫描。由于DjVu是一种基于像素的格式,我想确保在目标DjVu文件中使用与扫描时相同的分辨率。

有谁知道我可以运行什么程序,或者我如何编写程序,以确定用于生成扫描PDF的分辨率? (像素数也可能有效,因为几乎所有文档都是8.5 x 11英寸。)


回复后的澄清:我知道布列塔尼突出的困难,我愿意承认这个问题总体上是不合适的,但我不是在问一般 PDF文件。我的特定文件来自扫描仪。它们每页包含一个扫描图像,每页包含相同的分辨率。如果我将PDF转换为PostScript,我可以手动搜索并轻松找到像素尺寸;我可能会找到更多工作的图像尺寸。如果迫切需要我可以修改gs正在使用的字典堆栈;很久以前,我为PostScript Level 1写了一个翻译。

所有这些都是我想要避免的。


感谢收到的帮助,我在下面发布了一个答案:

  1. 使用identify从PDF中提取边界框,仅获取第一页的输出,并了解单位将是PostScript点,其中有72到1英寸。
  2. 使用pdfimages从第一页提取图像。
  3. 获取图像的高度和宽度。这一次identify会给出像素数。
  4. 添加图片的总区域以获得平方的点数。
  5. 要获得分辨率,请计算以英寸为单位的边界框区域,将平方除以平方英寸,取平方根,并舍入到最接近的10的倍数。
  6. 脚本的完整答案如下。我在实弹射击中使用它并且效果很好。感谢Harlequin为pdfimages和Spiffeah提供关于每页多个图像的警报(这很少见,但我发现了一些)。

7 个答案:

答案 0 :(得分:5)

如果通过扫描创建了pdf,那么每个页面应该只有一个图像。您可以通过使用iText(Java)或iTextSharp(.net端口)库轻松解析pdf来查找每个页面图像的每个图像分辨率。

如果您想要使用自己的实用程序来执行此操作,请在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();

这里针对每个页面,我们通过子类型Image的每个XObject读取并获取WIDTH和HEIGHT值。这将是扫描仪嵌入pdf中的图像的像素分辨率。

请注意,缩放此图像以匹配页面分辨率(如在Acrobat中呈现的页面大小 - A4,Letter等)在页面内容流中单独执行,表示为postscript的子集,如果不解析postscript就很难找到。

请注意,有些扫描仪会将扫描图像嵌入为较小图像的网格(我假设某种尺寸优化)。因此,如果您看到每个页面突然出现50个小图像,那可能就是原因。

希望如果你必须推出自己的实用程序,这会有所帮助。

答案 1 :(得分:5)

pdfimages有一个-list选项,用于提供高度宽度(以像素为单位)以及y-ppix-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%

答案 2 :(得分:4)

我猜扫描作为图像包含在PDF中,因此您可以先使用pdfimages提取它们。然后,identify应该能够找到正确的数据。

答案 3 :(得分:4)

以下是这个答案的要素:

  • 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

答案 4 :(得分:2)

发表评论的时间太长,但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像素,不出所料,这是在600dpi下扫描的8.5×11。 ImageMagic的输出非常不专业。

没有downvotes,因为你试图提供帮助,但*Magick不起作用。

答案 5 :(得分:0)

PDF是一种独立于分辨率的格式,这是一个荒谬的问题。您可能已经扫描了特定分辨率的某些位图,并且这些位图分别嵌入在pdf中,但PDF本身可能包含多个分辨率的图像,以及独立于分辨率的矢量图形。没有破解打开pdf并检查其中的每个对象都无法知道。

编辑以继续解释问题:

你可能已经幸运了,你用来扫描文档的软件嵌入了一些有关此内容的元数据,但不要赌它。这样的元数据不太可能是标准的。就解析pdf而言,你需要一个预先编写的库 - 例如ghostscript。问题在于,PDF实际上并不是一种格式,因为它是PostScript编程语言的指定子集,并且是一种压缩/编译此子集以及一些二进制文件的商定方式。因此,阅读PDF比其他类型的图像格式更复杂,因为它涉及编写语言解释器 - 不是那么简单。

最好的办法是要么放弃你的手放弃,要么真的很努力地看看ghostscript,看看你能不能告诉你答案。

答案 6 :(得分:0)