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

时间:2009-03-13 04:07:13

标签: parsing pdf colors automation printers

给定一组PDF文件,其中一些页面是彩色的,其余的是黑色和白色,是否有任何程序可以在给定的页面中找出颜色,哪些是黑色和&白色?例如,这可以用于打印论文,并且仅花费额外的费用来打印彩色页面。考虑到双面打印的人的奖励积分,并向彩色打印机发送适当的黑白页面(如果后面是对面的彩色页面)。

7 个答案:

答案 0 :(得分:29)

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

  1. 解析每个PDF页面
  2. 寻找颜色指示(g,rg,k,sc,scn等)
  3. 查找嵌入图像,分析颜色
  4. 我的解决方案是#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(@_); }
    

答案 1 :(得分:16)

较新版本的Ghostscript(版本9.05及更高版本)包含&#34;设备&#34;叫墨水。它计算青色(C),品红色(M),黄色(Y)和黑色(K)值中每页(不是每个图像)的墨水覆盖率,其中0.00000表示0%,而1.00000表示100%(参见< EM> Detecting all pages which contain color 的)。

例如:

$ 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'

答案 2 :(得分:15)

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

一个例子是:

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

其中PAGE是从0开始的页面,而不是1.如果未使用页面选择,则所有页面都将折叠为1,这不是您想要的。

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

使用输出的空格分隔列表,可以使用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

答案 3 :(得分:3)

Martin Scharrer的剧本很棒。它包含一个小错误:它包含两个包含颜色的页面,并且直接连续两次。我修好了。此外,脚本现在计算页面并列出双页打印的灰度页面。它还以逗号分隔打印页面,因此输出可以直接用于从PDF查看器打印。我已添加了代码,但您也可以下载here

干杯, 时移

#!/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

答案 4 :(得分:2)

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

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

ImageMagick有一些Perl API,所以如果你巧妙地将它们与PDF到图像转换器结合起来,你可以找到一种方法来做你的黑&amp;白色测试。

答案 5 :(得分:2)

我会尝试这样做,虽然可能还有其他更简单的解决方案,而且我很想听到它们,我只是想尝试一下:

  1. 遍历所有页面
  2. 将页面提取为图像
  3. 验证图像的颜色范围
  4. 对于页数,您可以在不费力的情况下将that翻译成Perl。它基本上是一个正则表达式。它也是said

      

    R “(/类型)\ S(/页)[/&GT; \ S]?”

         

    你只需计算多少   这个正则表达式出现的次数   在PDF文件中,减去你的次数   找到字符串“&lt;&gt;”   (未呈现的空白年龄)。

    要提取图片,您可以使用ImageMagick执行that。或者查看this question

    最后,要确定它是黑白还是白色,这取决于你的字面意思是黑白还是灰度。对于黑色和白色,您应该只在所有图像中都有黑色和白色。如果你想看灰度,现在,它真的不是我的专长,但我想你可以看到红色,绿色和蓝色的平均值是否彼此接近,或者原始图像和grayscale converted一个相互接近。

    希望它能提供一些帮助你走得更远的提示。

答案 6 :(得分:0)

这是Windows的ghostscript解决方案,需要GnuWin(http://gnuwin32.sourceforge.net/packages/grep.htm)的grep:

单色(黑白)页面:

gswin64c -q -o - -sDEVICE=inkcov DOCUMENT.pdf | grep "^ 0.00000 0.00000 0.00000" | find /c /v ""

彩色页面:

gswin64c -q -o - -sDEVICE=inkcov DOCUMENT.pdf | grep -v "^ 0.00000 0.00000 0.00000" | find /c /v ""

总页数(您可以从任何pdf阅读器中轻松获得这一页):

gswin64c -q -o - -sDEVICE=inkcov DOCUMENT.pdf | find /c /v ""