Pregunta

Dado un conjunto de archivos PDF entre los cuales algunas páginas son en color y las restantes son en negro & amp; blanco, ¿hay algún programa para descubrir entre las páginas dadas cuáles son color y cuáles son negro & amp; ¿blanco? Esto sería útil, por ejemplo, para imprimir una tesis y gastar más para imprimir las páginas en color. Puntos de bonificación para alguien que tiene en cuenta la impresión a doble cara y envía una página en blanco y negro apropiada a la impresora en color si es seguida por una página en color en el lado opuesto.

¿Fue útil?

Solución

¡Esta es una de las preguntas más interesantes que he visto! Estoy de acuerdo con algunas de las otras publicaciones en que renderizar en un mapa de bits y luego analizar el mapa de bits será la solución más confiable. Para archivos PDF simples, aquí hay un enfoque más rápido pero menos completo.

  1. Analiza cada página PDF
  2. Busque directivas de color (g, rg, k, sc, scn, etc.)
  3. Busque imágenes incrustadas, analice el color

Mi solución a continuación es # 1 y la mitad de # 2. La otra mitad del # 2 sería hacer un seguimiento con el color definido por el usuario, lo que implica buscar las entradas / ColorSpace en la página y decodificarlas; contácteme sin conexión si esto es interesante para usted, ya que es muy factible pero no en 5 minutos.

Primero el programa principal:

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";
   }
}

Y aquí está el renderizador auxiliar que maneja las directivas de color en cada página:

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(@_); }

Otros consejos

Las versiones más recientes de Ghostscript (versión 9.05 y posterior) incluyen un " dispositivo " ; llamado inkcov. Calcula la cobertura de tinta de cada página (no para cada imagen) en valores Cian (C), Magenta (M), Amarillo (Y) y Negro (K), donde 0.00000 significa 0% y 1.00000 significa 100% (ver < em> Detectando todas las páginas que contienen color ).

Por ejemplo:

$ 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

Si los valores CMY no son 0, entonces la página es de color.

Para imprimir solo las páginas que contienen colores, use este práctico 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'

Es posible utilizar la herramienta Image Magick identify. Si se usa en páginas PDF, primero convierte la página en una imagen ráster. Si la página contenía color, puede probarse usando la opción -format "%[colorspace]", que para mi PDF imprimió Gray o RGB. En mi humilde opinión pdfinfo (o cualquier herramienta que use en segundo plano; ¿Ghostscript?) Elige el espacio de color dependiendo de los presentes de color.

Un ejemplo es:

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

donde PAGE es la página que comienza desde 0, no 1. Si no se utiliza la selección de página, todas las páginas se contraerán en una, que no es lo que desea.

Escribí el siguiente script BASH que usa pdftk para obtener el número de páginas y luego recorrerlas. Salida de las páginas que están en color. También agregué una función para documentos a doble cara en los que también podría necesitar una página trasera sin color.

Usando la lista separada por espacios de salida, las páginas PDF coloreadas se pueden extraer usando <=>:

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

El guión de Martin Scharrer es genial. Contiene un error menor: cuenta dos páginas que contienen color y son directamente consecutivas dos veces. Lo arreglé Además, el script ahora cuenta las páginas y enumera las páginas en escala de grises para la impresión a doble página. También imprime las páginas separadas por comas, por lo que la salida se puede usar directamente para imprimir desde un visor de PDF. He agregado el código, pero puede descargarlo aquí también .

Saludos, Timeshift

#!/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 tiene algunos métodos integrados para la comparación de imágenes.

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

Hay algunas API de Perl para ImageMagick, por lo que tal vez si combina inteligentemente estos con un convertidor de PDF a imagen, puede encontrar una manera de hacer su amplificador negro &; prueba de blanco.

Intentaría hacerlo así, aunque podría haber otras soluciones más fáciles, y tengo curiosidad por escucharlas, solo quiero intentarlo:

  1. Recorrer todas las páginas
  2. Extraer las páginas a una imagen
  3. Verifique la gama de colores de la imagen

Para el recuento de páginas, probablemente pueda traducir eso sin demasiado esfuerzo para Perl . Básicamente es una expresión regular. También es dijo que:

  

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

     

Simplemente tienes que contar cuántos   veces se produce esta expresión regular   en el archivo PDF, menos las veces que   encuentre la cadena " < > "   (edades vacías que no se representan).

Para extraer la imagen, puede usar ImageMagick para hacer eso . O vea esta pregunta .

Finalmente, para saber si es en blanco y negro, depende si quiere decir literalmente en blanco y negro o en escala de grises. Para blanco y negro, solo debe tener, bueno, blanco y negro en toda la imagen. Si desea ver en escala de grises, ahora, realmente no es mi especialidad, pero supongo que podría ver si los promedios del rojo, el verde y el azul están cerca uno del otro o si la imagen original y un escala de grises convertidos uno está cerca uno del otro.

Espero que te dé algunos consejos para ayudarte a llegar más lejos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top