Как узнать, являются ли страницы PDF цветными или черно-белыми?

StackOverflow https://stackoverflow.com/questions/641427

Вопрос

Учитывая набор 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 и более поздние) включают в себя " устройство " ; называется инков. Он рассчитывает покрытие чернилами каждой страницы (не для каждого изображения) в значениях Cyan (C), Magenta (M), Yellow (Y) и Black (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, то страница является цветной.

    Чтобы просто выводить страницы, содержащие цвета, используйте этот удобный инструмент:

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

    Можно использовать Image Magick инструмент identify. При использовании на страницах PDF он сначала преобразует страницу в растровое изображение. Если страница содержала цвет, можно проверить с помощью параметра -format "%[colorspace]", который для моего PDF-файла печатается либо Gray, либо RGB. ИМХО pdfinfo (или какой-нибудь инструмент, который он использует в фоновом режиме; Ghostscript?) Выбирает цветовое пространство в зависимости от присутствия цвета.

    Пример:

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

    где PAGE - страница, начинающаяся с 0, а не 1. Если выбор страницы не используется, все страницы будут свернуты в одну, а это не то, что вам нужно.

    Я написал следующий BASH-скрипт, который использует 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
    

    Сценарий Мартина Шаррера великолепен.Он содержит небольшую ошибку:Он считает две страницы, содержащие цвет и дважды идущие подряд.Я это исправил.Кроме того, скрипт теперь подсчитывает страницы и выводит список страниц в оттенках серого для двухстраничной печати.Кроме того, он печатает страницы, разделенные запятыми, поэтому выходные данные можно напрямую использовать для печати из программы просмотра 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
    

    Я бы попробовал сделать это так, хотя могут быть и другие более простые решения, и мне интересно их услышать, я просто хочу попробовать:

    1. Пролистнуть все страницы
    2. Извлеките страницы в изображение
    3. Проверьте цветовой диапазон изображения

    Что касается количества страниц, вы, вероятно, можете перевести что без особых усилий с Perl.По сути, это регулярное выражение.Это также сказал что:

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

    Нужно просто посчитать, сколько раз это регулярное выражение в PDF-файле, за вычетом раз, когда вы Найдите строку "<>" (пустые эпохи, которые не отображаются).

    Чтобы извлечь изображение, вы можете использовать ИзображениеМагия делать что.Или посмотреть этот вопрос.

    Наконец, чтобы понять, является ли изображение черно-белым, зависит от того, имеете ли вы в виду буквально черно-белое или оттенки серого.Что касается черно-белого изображения, то на всем изображении должно быть только черно-белое изображение.Если вы хотите увидеть оттенки серого, то это не моя специальность, но я думаю, вы могли бы увидеть, близки ли средние значения красного, зеленого и синего друг к другу или исходное изображение и преобразованный в оттенки серого один близко друг к другу.

    Надеюсь, что это даст некоторые подсказки, которые помогут вам идти дальше.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top