# Detecting all pages which contain color

In an larger LaTeX document there are often only some pages with color content (mainly figures) and the remaining ones are only black and white. Because printing costs for color pages are much higher than for black and white it would be good to be able to extract all pages with color and print them separately. The first step for this is to be able to detect if a page contains color or not. This could be in a form of an text list of page number suitable to be read by a PDF page extraction script (using e.g. pdftk).

A simple solution sufficient for many people would be to detect all pages which contain a figure and assume that only these have color. However, a general solution would be nice. Only color elements which are printed should be taken into account, while e.g. the color frames around link by hyperref should not. It is OK that the solution would disable these for the detection.

-
You have probably already found this, anyway: How do I know if PDF pages are color or black-and-white? – gcedo Apr 26 '12 at 20:32
I would still say it's easiest to analyse the PDF, like suggested in the referred question. I don't know whether there are free tools to color-separate a PDF, but pdftoppm and then ImageMagic to check the colors should be easy to do. Trying to hook into \color you'll face enormous problems. To name two: (a) you need to identify colors which are really gray. (b) what happens with pages where colored text has been broken to by a page break? They won't contain a color change (to black at most). And then you haven't even covered images. – Stephan Lehmke Apr 26 '12 at 21:00
@StephanLehmke: Good points. I like Image Magick most, so I had a look. Its identify tools seems to be suitable. It converts PDF pages to raster images and seems to select the required color space itself (e.g. Gray or RGB). My first try: for N in seq 1 $PAGES; do echo -n "$N: "; identify -format "%[colorspace]" $FILE.pdf[$((N-1))]; done. Prints either Gray or RGB for each page depending if there are colors on it. Ignores hyperref color borders and possible other PDF annotations. – Martin Scharrer Apr 26 '12 at 21:29
@StephanLehmke: I now wrote a small script for this and posted for the original SO question: How do I know if PDF pages are color or black-and-white? – Martin Scharrer Apr 26 '12 at 22:13
Could you self-answer this question? I keep getting it in "Unanswered" :-) – Stephan Lehmke Apr 29 '12 at 5:01

For the general case it seem to be indeed better to use an external tool to test for all pages which contain colors. This is the topic of the mentioned SO question How do I know if PDF pages are color or black-and-white?. I now wrote an answer to it which includes small script for this.

However, it is much easier to get a list of all pages containing figures. Here I use the zref-abspage package to get an absolute page counter. The normal \write command can be used which will expand its content when the surrounding content is really placed on a page. Therefore the page counters will have the correct value. Then the end-macro of figure can simply be patched to hold this code.

\documentclass{book}
\usepackage{mwe}

\usepackage{zref-abspage}% absolute page counter
\newwrite\figpages
\openout\figpages=\jobname.fpg
\makeatletter
% Write absolute page number and page label to file
% Do not use \immediate!
\write\figpages{\number\value{abspage}: \thepage}%
}
\makeatother

\newcount\mycount% for example loop
\begin{document}
\frontmatter
\Blindtext

\begin{figure}
\centering
\includegraphics[width=.8\textwidth,height=5cm]{example-image}
\caption{Some caption}
\end{figure}

\mainmatter
\Blindtext

\loop% keep MWE small by using a loop

\begin{figure}
\centering
\includegraphics[width=.8\textwidth,height=5cm]{example-image}
\caption{Some caption}
\end{figure}

{\Blindtext}

\ifnum\mycount<20\relax
\repeat

\backmatter
\appendix
\Blindtext

\begin{figure}
\centering
\includegraphics[width=.8\textwidth,height=5cm]{example-image}
\caption{Some caption}
\end{figure}

\end{document}

This generates a .fpg file (for figure pages) which looks like:

2: ii
4: 2
5: 3
7: 5
8: 6
10: 8
11: 9
13: 11
14: 12
16: 14
18: 16
19: 17
21: 19
22: 20
24: 22
25: 23
27: 25
28: 26
30: 28
31: 29
33: 31
38: 36

The format can be changed if required.

-

Newer versions of Ghostscript (version 9.05 and later) include a "device" called inkcov. It calculates the ink coverage of each page (not for each image) in Cyan (C), Magenta (M), Yellow (Y) and Black (K) values, where 0.00000 means 0%, and 1.00000 means 100%.

Example commandline:

gs -o - -sDEVICE=inkcov /path/to/your.pdf

Example output:

Page 1
0.00000  0.00000  0.00000  0.02230 CMYK OK
Page 2
0.02360  0.02360  0.02360  0.02360 CMYK OK
Page 3
0.02525  0.02525  0.02525  0.00000 CMYK OK
Page 4
0.00000  0.00000  0.00000  0.01982 CMYK OK

You can see here that the pages 1+4 are using no color, while pages 2+3 do. This case is particularly 'nasty' for people who want to save on color ink: because all the respective C, M, Y (and K) values are exactly the same for each of the pages 2+3, they possibly could appear to the human eye not as color pages, but as ("rich") grayscale anyway (if each single pixel is mixed with these color values).

Ghostscript can also convert color into grayscale. Example commandline:

gs                                \
-o grayscale.pdf                \
-sDEVICE=pdfwrite               \
-sColorConversionStrategy=Gray  \
-sProcessColorModel=/DeviceGray \
/path/to/your.pdf

Checking for the ink coverage distribution again (note how the addition of -q to the parameters slightly changes the output format):

gs -q  -o - -sDEVICE=inkcov grayscale.pdf
0.00000  0.00000  0.00000  0.02230 CMYK OK
0.00000  0.00000  0.00000  0.02360 CMYK OK
0.00000  0.00000  0.00000  0.02525 CMYK OK
0.00000  0.00000  0.00000  0.01982 CMYK OK
-
Why did you say that in the first example page 1 uses color? Was it a mistake? – giordano Sep 30 '13 at 17:10
Here's a quick command for folks who want to count the number of non-color pages in a PDF: gs -o - -sDEVICE=inkcov path/to/pdf.pdf | grep -e "0.00000 0.00000 0.00000 [01]." | wc And +1 to giordano. – igordcard Dec 10 '14 at 15:19
@giordano: Thanks for the hint in your comment -- didn't see it before. Of course you are right. I can't tell you anymore what happened. Maybe I pasted the wrong PDF file's output into the editing window. I'll correct the answer accordingly... – Kurt Pfeifle Feb 6 at 15:55
I only returned to this answer because SE notified me that it today has earned me the 'Populist' batch :-) – Kurt Pfeifle Feb 6 at 16:02

There's a rather useful python script at http://homepages.inf.ed.ac.uk/imurray2/code/hacks/pdfcolorsplit which uses pdftk to split into colour and b&w files, though it doesn't deal with the boxes around hyperrefs. If you have access to the LaTeX source, why not turn off the colour in hyperref anyway - I do it like this:

citecolor=black,
filecolor=black,
urlcolor=black]{hyperref}

IIRC if you just set [colorlinks=false] they're not clickable.

-
In fact, setting colorlinks=false only means that links are framed instead of colored. (Oh! and Welcome to TeX.SX!) – Sean Allred Apr 10 '13 at 15:41
Although I can't tell from the script itself: does this separate channels or pages? – Sean Allred Apr 10 '13 at 15:47
> 'colorlinks=false' only means that links are framed instead of colored. OK - I had forgotten the reason why I did this. And thanks for the welcome. It splits pages, depending on the options, you can assemble 1 PDF of colour pages, and another of B+W, and it can handle double sided. It's not my script, but it's been very useful for theses round here - colour pages cost ~6x as much to have printed. I've an idea to do something based on that for printing only the pages (of e.g. a scanned document) with more than some threshold of non-white - where white is also thresholded. – Chris H Apr 10 '13 at 17:55

Here is a MATLAB script which uses Kurt's answer to split a PDF into two files, one colour and one grayscale. The original file is preserved. It does not handle double-sided printing.

It is not bullet-proof and might need some debugging, but hopefully it will work out of the box.

You will need:

• Ghostscript (version 9.05 and later)
• MATLAB function ghostscript() and user_input() from here
• pdftk

Here is the script (you will need to change lines 4,5 and possibly 6):

clear all; close all; clc;

%Change these:
pathToFile = '/Users/nikos/Desktop/';
fName = 'thesis.pdf';
%you might need to change the path to pdftk (if in windows for example)
pdftkPath = '/usr/local/bin/pdftk';

disp('Reminder: you might want to set \hypersetup{colorlinks=false} in latex');
disp('Do you want to manually set as grayscale any pages that have (C == M == Y)?');
a = input('Otherwise they will be treated as colour! (y/n) ','s');

if (a~= 'y' && a~='Y')
manualMode = false;
else
manualMode = true;
end

[status, ret] = ghostscript(['-o - -sDEVICE=inkcov ',pathToFile,fName]);

inds = strfind(ret,'0.');
pages = length(inds)/4;

if (round(pages) ~= pages)
disp('Something went wrong');
disp('Check the variable ret');
disp('I am looking the the string ''0.'' which should only occur when listing CMYK values');
end

a = input(['Is your pdf ', num2str(pages), ' pages long (y/n) ?'],'s');

if (a ~= 'y' && a ~= 'Y')
break;
end

disp([num2str(pages), ' pages processed.']);
c = 1:4:length(inds);
m = 2:4:length(inds);
y = 3:4:length(inds);
k = 4:4:length(inds);

colorPages = '';
bwPages = '';
cpCounter = 0;
bwCounter = 0;
for i = 1:pages
C = str2num(ret(inds(c(i)):inds(c(i))+6));
M = str2num(ret(inds(m(i)):inds(m(i))+6));
Y = str2num(ret(inds(y(i)):inds(y(i))+6));
K = str2num(ret(inds(k(i)):inds(k(i))+6));

if (C == 0 && M == 0 && Y == 0)
bwPages = [bwPages, ' ',num2str(i)];
bwCounter = bwCounter+1;
elseif (C == M && C == Y && manualMode)
a = input(['Is page ', num2str(i), ' colour (C == M == Y) (y/n) ?'],'s');
if (a ~= 'y' && a ~= 'Y')
bwPages = [bwPages, ' ',num2str(i)];
bwCounter = bwCounter+1;
else
colorPages = [colorPages, ' ', num2str(i)];
cpCounter = cpCounter+1;
end
else
colorPages = [colorPages, ' ', num2str(i)];
cpCounter = cpCounter+1;
end
end

cName = [pathToFile, 'color_',fName];
bName = [pathToFile, 'bw_',fName];
disp([cName, ' (',num2str(cpCounter), ' pages)']);
disp([bName, ' (',num2str(bwCounter), ' pages)']);

system([pdftkPath, ' ', pathToFile, fName, ' cat ', colorPages,' output ', cName]);
system([pdftkPath, ' ', pathToFile, fName, ' cat ', bwPages,' output ', bName]);
-