我如何知道 PDF 页面是彩色还是黑白?
-
22-07-2019 - |
题
给定一组 PDF 文件,其中一些页面是彩色的,其余页面是黑白的,是否有任何程序可以找出给定页面中哪些页面是彩色的,哪些页面是黑白的?例如,这在打印论文时很有用,并且只需花费额外的费用来打印彩页。对于考虑双面打印并将适当的黑白页面发送到彩色打印机(如果其反面接着有彩色页面)的人来说,这是加分项。
解决方案
这是我见过的最有趣的问题之一!我同意其他一些帖子的观点,即渲染位图然后分析位图将是最可靠的解决方案。对于简单的 PDF,这里有一个更快但不太完整的方法。
- 解析每个 PDF 页面
- 查找颜色指令(g、rg、k、sc、scn 等)
- 寻找嵌入的图像,分析颜色
我下面的解决方案完成了#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(@_); }
其他提示
较新的 Ghostscript的(9.05和更高版本)版本包括一个 “装置” 称为inkcov。它可以计算每个页面(未针对每个图像)在青色(C),品红色(M),黄色(Y)和黑色(K)的值,的油墨覆盖,其中0.00000手段0%,1.00000意味着100%(参见< EM> 检测包含颜色的所有网页 的)。
例如:
$ 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,则该页面的颜色。
要只输出包含颜色使用这个方便oneliner的网页:
$ gs -o - -sDEVICE=inkcov file.pdf |tail -n +4 |sed '/^Page*/N;s/\n//'|sed -E '/Page [0-9]+ 0.00000 0.00000 0.00000 / d'
有可能使用难懂的工具identify
。如果在PDF页面上用它首先将页面光栅图像。如果所包含的页面颜色可以使用-format "%[colorspace]"
选项,这对于我的PDF打印或者Gray
或RGB
进行测试。恕我直言identify
(或什么都工具,它使用的背景; Ghostscript的)。的确取决于颜色的礼物选择的色彩空间。
的一个例子是:
identify -format "%[colorspace]" $FILE.pdf[$PAGE]
其中PAGE是从0开始页开始,而不是1。如果不使用页选择所有页面将被折叠到一个,这是不是你想要的。
我写它使用pdfinfo
得到的页数,然后循环在他们下面的bash脚本。输出这是在色彩的页面。我还添加了双面文档功能,您可能需要一个非彩色背面页为好。
使用输出空间分隔的列表着色PDF页面可使用pdftk
提取:
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
这是马丁·沙勒的脚本是巨大的。它包含一个小错误:它计算包含颜色,并直接连续两次两页。我固定的。此外,该脚本现在计数的网页,并列出了双分页打印的灰度页面。还它打印逗号分隔的页,所以输出可以直接被用于从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一>
有一些Perl的API来ImageMagick的,所以也许如果你巧妙地用PDF格式的图像转换器结合了这些,你可以找到一种方法,做你的黑白测试。
我会尝试这样做,尽管可能还有其他更简单的解决方案,而且我很好奇听到它们,我只是想尝试一下:
- 循环浏览所有页面
- 将页面提取为图像
- 验证图像的颜色范围
对于页数,你可能可以翻译 那 无需为 Perl 付出太多努力。它基本上是一个正则表达式。这也是 说 那:
r"(/类型)\s?(/页面)[/>\s]"
您只需要计算该正则表达式在PDF文件中发生多少次,减去您找到字符串“ <>”的时间(未渲染的空年龄)。
要提取图像,您可以使用 图像魔术师 去做 那. 。或者看 这个问题.
最后,要确定它是否是黑白的,这取决于您的意思是字面意义上的黑白还是灰度。对于黑白,所有图像中应该只有黑白。如果你想看灰度,现在,这确实不是我的专长,但我想你可以看看红色、绿色和蓝色的平均值是否彼此接近,或者原始图像和 灰度转换 一个彼此很接近。
希望它能给您一些提示,帮助您走得更远。