【问题标题】:How do I know if PDF pages are color or black-and-white?我如何知道 PDF 页面是彩色的还是黑白的?
【发布时间】:2010-10-13 02:08:06
【问题描述】:

给定一组PDF文件,其中一些页面是彩色的,其余的都是黑白的,是否有任何程序可以在给定的页面中找出哪些是彩色的,哪些是黑白的?这将很有用,例如,在打印论文时,只需花费额外的费用来打印彩色页面。考虑到双面打印并在彩色打印机后面跟着彩色页面的另一面发送适当的黑白页面的人的奖励积分。

【问题讨论】:

标签: parsing pdf colors automation printers


【解决方案1】:

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

  1. 解析每个 PDF 页面
  2. 查找颜色指令(g、rg、k、sc、scn 等)
  3. 查找嵌入图像,分析颜色

我下面的解决方案是#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(@_); }

【讨论】:

【解决方案2】:

Ghostscript 的较新版本(9.05 及更高版本)包含一个名为 inkcov 的“设备”。它以青色 (C)、品红色 (M)、黄色 (Y) 和黑色 (K) 值计算每页(不是每个图像)的墨水覆盖率,其中 0.00000 表示 0%,1.00000 表示 100%(请参阅 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'

【讨论】:

  • 有一个错字,所以我不会更正,因为它只是一个字符的变化:s 应该是 gs。此外,此方法不一定按预期工作;例如,由 inkscape 创建的 pdf 文件始终具有非零 CMY,即使图像是纯灰度的。
  • 谢谢我修正了错字
  • 如果 CMY 为 0,则图像为黑白,如果 C=M=Y 而不是 0,则图像为灰度,如果 CMY,则图像为彩色。
  • @lisandro 我们必须区分页面的外观和打印方式。使用其他三种颜色打印的灰度页面将被视为彩色(因为它使用黑色以外的其他碳粉盒)。它比仅使用黑色打印成本更高。
  • 您可以将输出重定向到文件$ 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' > file.txt
【解决方案3】:

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

一个例子是:

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

其中 PAGE 是从 0 开始的页面,而不是 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

【讨论】:

  • 考虑这个 TeX 文件:hello world\byeidentify -format "%[colorspace]" "$FILE.pdf[0]" 的输出是sRGB,而gs -o - -sDEVICE=inkcov $FILE.pdf"(建议here)我得到0.00000 0.00000 0.00000 0.00020 CMYK OK,这更合理。
  • 您可以通过检查identify 是否在不存在的页面上退出来避免对pdfinfo 的依赖。 (发生这种情况时,它甚至会告诉您页数。)
  • ...它也在页面“-1”上执行。
  • 我得到identify: not authorized 'myfile.pdf' @ error/constitute.c/ReadImage/412,即使不应该以任何方式限制 pdf ...
  • @HagenvonEitzen 使用此页面上的解决方案允许 imagemagic 读取/写入 pdf:stackoverflow.com/questions/42928765/…
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

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

    有一些用于 ImageMagick 的 Perl API,所以如果您巧妙地将这些 API 与 PDF 到图像转换器结合起来,您可能会找到一种方法来进行黑白测试。

    【讨论】:

      【解决方案6】:

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

      1. 循环浏览所有页面
      2. 将页面提取为图像
      3. 验证图片的颜色范围

      对于页数,您可以将that 翻译成 Perl 而不需要太多努力。它基本上是一个正则表达式。也是said

      r"(/Type)\s?(/Page)[/>\s]"

      您只需计算有多少 此正则表达式出现的次数 在 PDF 文件中,减去您的次数 找到字符串“” (未渲染的空年龄)。

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

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

      希望能给你一些提示,帮助你走得更远。

      【讨论】:

      • 获取页数:perl -le'use CAM::PDF;打印 CAM::PDF->new("my.pdf")->numPages'
      【解决方案7】:

      这是适用于 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 ""

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-10
        • 1970-01-01
        • 2014-12-20
        • 2011-04-23
        • 2013-12-02
        • 1970-01-01
        • 1970-01-01
        • 2022-01-05
        相关资源
        最近更新 更多