如何知道PDF页面是彩色还是黑白?

给定一组PDF文件,其中一些页面是彩色的,其余的是黑白的,在给定的页面中是否有任何程序可以查找是彩色的还是黑白的? 例如,在打印一篇论文时,这将是有用的,并且只花费额外的费用来打印彩色页面。 对于考虑到双面打印的人员的奖励分数,并且如果在彩色打印机的对面跟着彩页,则发送适当的黑白页面到彩色打印机。

这是我见过的最有趣的问题之一! 我同意其他一些职位,渲染到位图,然后分析位图将是最可靠的解决scheme。 对于简单的PDF,这是一个更快但不太完整的方法。

  1. parsing每个PDF页面
  2. 寻找颜色指令(g,rg,k,sc,scn等)
  3. 寻找embedded的图像,分析颜色

下面我的解决scheme是#1和#2的一半。 #2的另一半将跟随用户定义的颜色,这涉及查找页面中的/ ColorSpace条目并解码它们 – 如果这对你很有趣,请联系我,因为它非常可行,但不是5分钟。

首先是主程序:

use CAM::PDF; my $infile = shift; my $pdf = CAM::PDF->new($infile); PAGE: for my $p (1 .. $pdf->numPages) { my $tree = $pdf->getPageContentTree($p); if (!$tree) { print "Failed to parse page $p\n"; next PAGE; } my $colors = $tree->traverse('My::Renderer::FindColors')->{colors}; my $uncertain = 0; for my $color (@{$colors}) { my ($name, @rest) = @{$color}; if ($name eq 'g') { } elsif ($name eq 'rgb') { my ($r, $g, $b) = @rest; if ($r != $g || $r != $b) { print "Page $p is color\n"; next PAGE; } } elsif ($name eq 'cmyk') { my ($c, $m, $y, $k) = @rest; if ($c != 0 || $m != 0 || $y != 0) { print "Page $p is color\n"; next PAGE; } } else { $uncertain = $name; } } if ($uncertain) { print "Page $p has user-defined color ($uncertain), needs more investigation\n"; } else { print "Page $p is grayscale\n"; } } 

然后,这是处理每个页面上的颜色指令的助手渲染器:

 package My::Renderer::FindColors; sub new { my $pkg = shift; return bless { colors => [] }, $pkg; } sub clone { my $self = shift; my $pkg = ref $self; return bless { colors => $self->{colors}, cs => $self->{cs}, CS => $self->{CS} }, $pkg; } sub rg { my ($self, $r, $g, $b) = @_; push @{$self->{colors}}, ['rgb', $r, $g, $b]; } sub g { my ($self, $gray) = @_; push @{$self->{colors}}, ['rgb', $gray, $gray, $gray]; } sub k { my ($self, $c, $m, $y, $k) = @_; push @{$self->{colors}}, ['cmyk', $c, $m, $y, $k]; } sub cs { my ($self, $name) = @_; $self->{cs} = $name; } sub cs { my ($self, $name) = @_; $self->{CS} = $name; } sub _sc { my ($self, $cs, @rest) = @_; return if !$cs; # syntax error if ($cs eq 'DeviceRGB') { $self->rg(@rest); } elsif ($cs eq 'DeviceGray') { $self->g(@rest); } elsif ($cs eq 'DeviceCMYK') { $self->k(@rest); } else { push @{$self->{colors}}, [$cs, @rest]; } } sub sc { my ($self, @rest) = @_; $self->_sc($self->{cs}, @rest); } sub SC { my ($self, @rest) = @_; $self->_sc($self->{CS}, @rest); } sub scn { sc(@_); } sub SCN { SC(@_); } sub RG { rg(@_); } sub G { g(@_); } sub K { k(@_); } 

可以使用Image Magick工具identify 。 如果在PDF页面上使用,它将首先将页面转换为光栅图像。 如果页面包含的颜色可以使用-format "%[colorspace]"选项进行testing,该选项为我的PDF打印GrayRGB 。 恕我直言identify (或在背景中使用什么工具; Ghostscript?)确实根据颜色的呈现select颜色空间。

一个例子是:

 identify -format "%[colorspace]" $FILE.pdf[$PAGE] 

其中PAGE是从0开始的页面,而不是1.如果不使用页面select,则所有页面将被折叠为1,这不是你想要的。

我写了下面的BASH脚本,它使用pdfinfo来获取页面的数量,然后遍历它们。 输出彩色的页面。 我还为双面文档添加了一个function,您可能还需要一个无色的背面页面。

使用输出的空格分隔列表可以使用pdftk提取彩色的PDF页面:

 pdftk $FILE cat $PAGELIST output color_${FILE}.pdf 

 #!/bin/bash FILE=$1 PAGES=$(pdfinfo ${FILE} | grep 'Pages:' | sed 's/Pages:\s*//') GRAYPAGES="" COLORPAGES="" DOUBLECOLORPAGES="" echo "Pages: $PAGES" N=1 while (test "$N" -le "$PAGES") do COLORSPACE=$( identify -format "%[colorspace]" "$FILE[$((N-1))]" ) echo "$N: $COLORSPACE" if [[ $COLORSPACE == "Gray" ]] then GRAYPAGES="$GRAYPAGES $N" else COLORPAGES="$COLORPAGES $N" # For double sided documents also list the page on the other side of the sheet: if [[ $((N%2)) -eq 1 ]] then DOUBLECOLORPAGES="$DOUBLECOLORPAGES $N $((N+1))" #N=$((N+1)) else DOUBLECOLORPAGES="$DOUBLECOLORPAGES $((N-1)) $N" fi fi N=$((N+1)) done echo $DOUBLECOLORPAGES echo $COLORPAGES echo $GRAYPAGES #pdftk $FILE cat $COLORPAGES output color_${FILE}.pdf 

较新版本的Ghostscript (版本9.05和更高版本)包括名为inkcov的“设备”。 它以青色(C),品红色(M),黄色(Y)和黑色(K)值计算每页(不是每个图像)的油墨覆盖率,其中0.00000表示0%,1.00000表示100% 所有包含颜色的页面 )。

例如:

 $ gs -q -o - -sDEVICE=inkcov file.pdf 0.11264 0.11605 0.11605 0.09364 CMYK OK 0.11260 0.11601 0.11601 0.09360 CMYK OK 

如果CMY值不是0,那么页面是彩色的。

Martin Scharrer的剧本非常棒。 它包含一个小错误:它包含两个包含颜色的页面,并且直接连续两次。 我解决了这个问题 此外,脚本现在对页面进行计数并列出双页面打印的灰度页面。 此外,它打印逗号分隔的页面,所以输出可以直接用于从PDF阅读器打印。 我已经添加了代码,但是也可以在这里下载。

欢呼声,时间转换

 #!/bin/bash if [ $# -ne 1 ] then echo "USAGE: This script needs exactly one paramter: the path to the PDF" kill -SIGINT $$ fi FILE=$1 PAGES=$(pdfinfo ${FILE} | grep 'Pages:' | sed 's/Pages:\s*//') GRAYPAGES="" COLORPAGES="" DOUBLECOLORPAGES="" DOUBLEGRAYPAGES="" OLDGP="" DOUBLEPAGE=0 DPGC=0 DPCC=0 SPGC=0 SPCC=0 echo "Pages: $PAGES" N=1 while (test "$N" -le "$PAGES") do COLORSPACE=$( identify -format "%[colorspace]" "$FILE[$((N-1))]" ) echo "$N: $COLORSPACE" if [[ $DOUBLEPAGE -eq -1 ]] then DOUBLEGRAYPAGES="$OLDGP" DPGC=$((DPGC-1)) DOUBLEPAGE=0 fi if [[ $COLORSPACE == "Gray" ]] then GRAYPAGES="$GRAYPAGES,$N" SPGC=$((SPGC+1)) if [[ $DOUBLEPAGE -eq 0 ]] then OLDGP="$DOUBLEGRAYPAGES" DOUBLEGRAYPAGES="$DOUBLEGRAYPAGES,$N" DPGC=$((DPGC+1)) else DOUBLEPAGE=0 fi else COLORPAGES="$COLORPAGES,$N" SPCC=$((SPCC+1)) # For double sided documents also list the page on the other side of the sheet: if [[ $((N%2)) -eq 1 ]] then DOUBLECOLORPAGES="$DOUBLECOLORPAGES,$N,$((N+1))" DOUBLEPAGE=$((N+1)) DPCC=$((DPCC+2)) #N=$((N+1)) else if [[ $DOUBLEPAGE -eq 0 ]] then DOUBLECOLORPAGES="$DOUBLECOLORPAGES,$((N-1)),$N" DPCC=$((DPCC+2)) DOUBLEPAGE=-1 elif [[ $DOUBLEPAGE -gt 0 ]] then DOUBLEPAGE=0 fi fi fi N=$((N+1)) done echo " " echo "Double-paged printing:" echo " Color($DPCC): ${DOUBLECOLORPAGES:1:${#DOUBLECOLORPAGES}-1}" echo " Gray($DPGC): ${DOUBLEGRAYPAGES:1:${#DOUBLEGRAYPAGES}-1}" echo " " echo "Single-paged printing:" echo " Color($SPCC): ${COLORPAGES:1:${#COLORPAGES}-1}" echo " Gray($SPGC): ${GRAYPAGES:1:${#GRAYPAGES}-1}" #pdftk $FILE cat $COLORPAGES output color_${FILE}.pdf 

ImageMagick有一些内置的图像比较方法。

http://www.imagemagick.org/Usage/compare/#type_general

ImageMagick有一些Perl API,所以如果你巧妙地将这些与PDF转换成图像转换器,你可以find一个方法来做你的黑白testing。

我会尽力做到这一点,虽然可能有其他更简单的解决scheme,我很好奇听到他们,我只是想试试看:

  1. 遍历所有页面
  2. 将页面解压缩到图像
  3. validation图像的颜色范围

对于页数,你可以把它转换成Perl。 这基本上是一个正则expression式。 还有人说 :

R “(/types)\ S?(/页)[/> \ S]”

您只需要计算这个正则expression式在PDF文件中出现的次数,减去findstring“<>”(未渲染的空的年龄)的次数。

要提取图像,您可以使用ImageMagick来做到这一点 。 或者看到这个问题 。

最后,要判断它是否是黑白的,取决于是否意味着黑白或灰度。 对于黑色和白色,你只能在所有的图像中有黑白的。 如果你想看到灰度,现在,这不是我的专长,但我想你可以看到红色,绿色和蓝色的平均值是否彼此接近,或者如果原始图像和灰度转换的接近彼此。

希望它提供一些提示,以帮助您进一步。