How to improve color consistency of bitmap pictures from native format to target pdf file?

Disclaimer: I know next to nothing about colour management, but find the subject interesting. I'm confused by some of the stuff below. Maybe someone with more knowledge can shed some light...

By googling, I found two advices on this issue.

Update: Based on michal-h21s suggestion that pdftex supports adding a color profile to an embedded image, I could work out another solution. See section Attach Color Profile to Embedded Image below.

Convert to PDF

See http://webstaff.itn.liu.se/~karlu20/div/howto/LaTeX_colour_management.php

There, the advice is basically to convert the image (including a profile) to PDF and embed the PDF.

To test, I downloaded the example image with ProPhoto profile.

identify -verbose butterfly_ProPhoto.png gives

Profile-icc: 566 bytes
  Description: SCARSE: Kodak ProPhoto RGB
  Manufacturer: SCARSE: Kodak ProPhoto RGB
  Model: SCARSE: Kodak ProPhoto RGB
  Copyright: Copyright (C) 1999-2005 Scarse Project

so apparently the profile is found by ImageMagick. Then I converted the image with convert butterfly_ProPhoto.png butterfly_ProPhoto.pdf. From the docs ImageMagick can cope with color profiles, and identify -verbose butterfly_ProPhoto.pdf gives

Profile-icc: 2576 bytes
  Description: Artifex Software sRGB ICC Profile
  Manufacturer: Artifex Software sRGB ICC Profile
  Model: Artifex Software sRGB ICC Profile
  Copyright: Copyright Artifex Software 2011

So apparently the PDF contains a profile, but it was converted (why?).

Testing the profile embedding with

\documentclass[a4paper]{article}

\usepackage{graphicx}
\begin{document}

\noindent
\includegraphics[width=\linewidth]{butterfly_ProPhoto.png}

\noindent
\includegraphics[width=\linewidth]{butterfly_ProPhoto.pdf}

\end{document}

and viewing with acrobat reader, I don't see a difference:

enter image description here

Furthermore, the result on my screen looks completely different from the example PDF given on the linked page, so apparently I did something wrong by just using convert.

A further hint convert doesn't cut it comes from this example containing an image with a GBR profile.

Converting Peppers_withGBRprofile.jpg to PDF and viewing in acrobat reader like above gives

enter image description here

The same is displayed in Acrobat Professional, btw.

So apparently it is indeed impossible to use convert to successfully convert profiled images to PDF for embedding with pdftex, though the ImageMagic doc and identify results seem to suggest otherwise. Could someone elaborate?

Second try: Convert to PDF with Acrobat Professional

Disappointed with no success at all trying to convert to PDF with ImageMagick, I tried the same procedure converting the test images (PNG and JPG) to PDF with the "create PDF" feature of Acrobat professional, and voilá:

enter image description here

enter image description here

So I can conclude that converting a profiled image into a PDF with embedded profile indeed works, but not with the ImageMagick based toolchain I've used on my Ubuntu Linux system.

The page I linked to in the beginning mentions that apparently the only tool capable of doing this under Linux is Scribus which I have not tried here.

Embedding a colour profile in the PDF

See http://compgroups.net/comp.text.tex/making-a-cmyk-pdf/153995

This advice basically is to embed a colour profile in the PDF.

So, taking the GBR example above, I extracted the ICC profile with

convert Peppers_withGBRprofile.jpg gbr.icm

and embedded the profile into the PDF with the following test file:

\documentclass[a4paper]{article}

\usepackage{graphicx}

\immediate\pdfobj stream attr{/N 4}  file{gbr.icm}
\pdfcatalog{%
  /OutputIntents [ <<
  /Type /OutputIntent
  /S/GTS_PDFA1
  /DestOutputProfile \the\pdflastobj\space 0 R
  /OutputConditionIdentifier (Adobe GBR (2004))
  /Info(Adobe GBR (2004))
 >> ]
}

\begin{document}

\noindent
\includegraphics[width=\linewidth]{Peppers_withGBRprofile.jpg}

\noindent
\includegraphics[width=\linewidth]{Peppers_withGBRprofile.pdf}

\end{document}

Frustratingly, with acrobat reader on Ubuntu, the PDF displays exactly as (wrongly):

enter image description here

But in Acrobat Professional, I get the correct display:

enter image description here

So it seems that embedding a color profile in the PDF does work (on both JPG and PDF inclusions), but not with acrobat reader on Linux.

Furthermore, this "solution" means the embedded profile simply becomes the base profile for the whole document, so one profile will be applied to all images (which do not themselves contain a profile). So embedding both test images (both of which contain different profiles), the result is

enter image description here

In the color proofing dialog you can also see that the GBR profile is shown as the document profile. This is a further aspect that this advice isn't so useful, because I could assign any other profile right there in the Acrobat dialog (changing the color display of both images accordingly).

Attach Color Profile to Embedded Image

Based on the advice of michal-h21 in his answer I looked into the pdftex doc and found that the \pdfximage primitive indeed supports a keyword colorspace which will associate the corresponding object with the embedded image.

Based on this, I could make the following patch to the pdftex driver of the graphics package (including example):

\documentclass[a4paper]{article}

\usepackage{graphicx}

\usepackage{etoolbox}

\makeatletter
  \let\GPT@colorspacefile\ltx@empty
  \define@key{Gin}{colorspacefile}{\def\GPT@colorspacefile{#1}}%

  \patchcmd\Gread@@pdftex
  {\pdfximage\GPT@RuleAttr}
  {%
    \ifx\GPT@colorspacefile\ltx@empty
     \else
      \immediate\pdfobj stream attr{/N 4}  file{\GPT@colorspacefile}%
      \@tempcnta\the\pdflastobj
    \fi
    \pdfximage\GPT@RuleAttr
    \ifx\GPT@colorspacefile\ltx@empty
     \else
      colorspace \@tempcnta
    \fi
  }%
  {}{}
\makeatother

\begin{document}

\noindent
\includegraphics[width=.5\linewidth]{Peppers_withGBRprofile.jpg}\includegraphics[width=.5\linewidth,colorspacefile={gbr.icm}]{./Peppers_withGBRprofile.jpg}

\noindent
\includegraphics[width=.5\linewidth]{butterfly_ProPhoto.png}\includegraphics[width=.5\linewidth,colorspacefile={prophoto.icm}]{./butterfly_ProPhoto.png}

\end{document}

which gives

enter image description here

I got the profiles directly from the images with

convert Peppers_withGBRprofile.jpg gbr.icm
convert butterfly_ProPhoto.png prophoto.icm

To me, this seems to be the best solution because it allows to associate each image with its own color profile without needing a non-trivial conversion step.

Interestingly, you can see on the test image that it also works with PNG images, although the pdftex documentation claims this should not work.

Note that the "integration" given above is a proof-of-concept only and should not be used for production purposes. First, one should have some "caching" mechanism for color profiles to avoid embedding a profile more than once. Secondly, if the same file should be used with different profiles (as in this example), this could clash with the internal caching mechanism which will embed each image only once. Note the trick with using the ./ filename prefix to make the same file appear as two different files.

Conclusions

From this round of tests, I conclude that

  1. Converting images to PDF with embedded color profile works, but I couldn't get it to work with convert from ImageMagick (version 6.7.7-10). So the number of workable tools for Linux seems to be limited (the linked article mentions Scribus, which I did not try).
  2. Embedding an ICC profile in the PDF generated by pdftex demonstrably works, but it seems acrobat reader (version 9.5.5) on Ubuntu has problems interpreting such an embedded profile, while Acrobat Professional hasn't. Furthermore, this adds "only" a global profile to the whole PDF document, without a possibility to differentiate between different images.
  3. Using the colorspace keyword of \pdfximage, it is possible to attach a color profile to any embedded image.
  4. On the whole, the third alternative seems to be the best solution, because it also works with acrobat reader on Linux, every image can have its own profile, and no rare tool for converting images is required.

It seems that in PDF format doesn't suffice that included image contains ICC profile, but this profile must be included as a standalone object and the image must reference this object.

I think there is a low level support for this feature in pdftex (but only for jpeg images, regarding to the manual) and luatex, but the graphicx package doesn't have support for this.

More user friendly (at least for me :)) possibility to include a icc profile is using luatex's img library. I created small library called collorspaces.lua:

local m = {}

local colorspaces = {}
local images = {}

function create_profile(filename)
  local icc = pdf.immediateobj("streamfile", filename, [[
/N 4
/Alernate/DeviceRGB]])                   
  local profile = pdf.immediateobj("[/ICCBased "..icc.." 0 R]") 
  return profile
end

function load_image(filename, attributes)
  local exists =images[filename]
  if exists then return exists end
  local profile_file = filename:gsub("[%w]+$","icm")
  local command = "convert ".. filename .." " .. profile_file 
  local conversion_status = os.execute(command)
  local profile = nil
  local img_attr = {filename = filename}
  if conversion_status == 0 then
    local icc_file = io.open(profile_file,"r")
    local icc = icc_file:read("*all")
    icc_file:close()
    local hash = md5.sumhexa(icc)
    profile = colorspaces[hash] 
    if not profile then
      profile = create_profile(profile_file)
    end
    img_attr.colorspace= profile
    colorspaces[hash]=profile 
  end
  local image = img.scan(img_attr)
  images[filename] = image
  return image
end

local function write_image(image)
  return img.write(image)
end

local function include_image(image_name)
  local image = load_image(image_name)
  write_image(image)
end
--load_image("gbr.jpg")
--load_image("rgb.jpg")
m.load_image = load_image
m.write_image = write_image
m.include_image = include_image
return m

main function is load_image, where icc profile is extracted using imagemagick's convert utility, then hash of this file is calculated, to prevent multiple inclusions of same profile to the pdf file, and if the profile wasn't used yet, it is included using create_profile function. Then the image is loaded. There are two more functions, write_image will output the image to the output stream, and include_image which load and write image.

Now some sample document:

\documentclass{article}
\usepackage[]{graphicx}
\pdfcompresslevel=0 

\directlua{%
  cs = require "colorspaces"
}
\def\myinc#1{\mbox{\directlua{cs.include_image "#1"}}}

\begin{document}

\setlength\parindent{0pt}
Rgb file without profile\\ 
\myinc{rgb.jpg}

Rgb file with profile\\
\myinc{gbr.jpg}

Rgb file with profile and includegraphics\\
\includegraphics{gbr.jpg}

Resize image\\
    \resizebox{\linewidth}{!}{\myinc{gbr.jpg}}
\end{document}

You must run it with

 lualatex -shell-escape filename

otherwise convert command couldn't be run!

There are two images, gbr.jpg and rgb.jpg, these are peppers from Stephan's samples. First contains ICC profile, second doesn't. The result:

enter image description here

As you can see, there is slight difference in first two images, which were included using our function, third, using \includegraphics is completely wrong. Last example shows how to resize the image.

If you doesn't want to use lualatex, you can convert your images to pdf with this script imgtopdf.lua:

 #!/usr/bin/env texlua
local filename = arg[1]
if not filename then 
    print "Usage imgtopdf filename"
    return false
end

local jobname = filename:gsub("%.%w+$","")

local tpl = [[\documentclass{standalone}
\directlua{cs = require "colorspaces"}
\def\myinc#1{\directlua{cs.include_image "#1"}}
\begin{document}
\myinc{%s}
\end{document}
]]


local lualatex = io.popen("lualatex -shell-escape -jobname="..jobname,"w")
lualatex:write(string.format(tpl,filename))
lualatex:close()

Run with

texlua imgtopdf.lua imagename