4

I am writing software to extract text sections from PDFs and render on screen for analysis; I need to display the text on screen in the colour from the document.

When the PDF contains CMYK colours at the moment I am using the simple formula widely found on the web (eg here) which is:

R = 255 × (1-C) × (1-K)

And similarly for the other colours.

However this is giving poor results. For example, one sample I have is specified in the PDF document with the command "0.62 0 0.05 0 k" (ie those are the CMYK components on a 0-1 scale). The formula above converts this to an RGB of #60fff2. However Adobe Reader renders this text using RGB #3dc6ea. Not only do these look very different, my colour is so bright that the text is hard to read. I've tried this on several monitors, and although there is variation in the colours, the overall result is the same: PDF reader shows a light pastel or sky blue, whereas the above conversion gives a bright cyan.

To verify this wasn't due to something missed in the PDF I tried the same colour in Illustrator: entering a CMYK of (62%, 0%, 5%, 0%) rendered on screen as #3ec6ea. Changing the colour editing box to RGB showed values of #40c4e8; and this was also rendered if I changed the colour settings to "Color Monitor". That's 3 different results from Adobe - but the differences are tiny and should be imperceptable.

So, are there any feasible alternative conversions which would give results closer to the Adobe conversions? I'm aware that conversion between RGB and CMYK is problematic, but this case is somewhat helped in that:

  • the conversion is for on-screen display only. Outputs won't be printed.

  • Differences between monitors are not very important. If a colour is rendered poorly on a certain monitor in (for example) Adobe Reader then I'm not expecting to produce a good result.

  • An exact match is irrelevant for me: I just need a conversion that looks similar and is readable.

4
  • What programming language do you use? There might be a library that can do the HardWork™️ for you.
    – Ideogram
    May 29, 2020 at 8:21
  • 1
    We're using c#, but I think we have it solved well enough. I'll post what we now do as an answer Jun 4, 2020 at 10:42
  • Hi Stuart! Good to see you here. Have we, by any chance, worked together recently? :)
    – Vincent
    Jun 4, 2020 at 11:15
  • 1
    @Vincent - if you were producing some logos for us that I needed in an older version of Illustrator, then yes :) Jun 4, 2020 at 11:30

4 Answers 4

3

What you are doing is a very naive version of the conversion. You should never ever do that. Because its not at all how it works. But beware, what you ask for is in fact incredibly complicated, more complicated than you can appreciate right now. I could quite literally wite a hundred pages on this and not be done.

Either you resort to tricks that are easy or you need to do the full work.

There is no one formula

You need a color management system, no options. What you do is you need to fetch the color profile and rendering intent of the element in question. Then do a profile to profile conversion to your monitors profile.*

See the PDF contains half of the information on how to do the on screen simulation for your colors. However this information is in the form of a ICC profile, and ICC profiles are basically a mini programming lanuage for color conversion. So implementing your own would be quite much more work, than I expect most people have to spend.

Now your OS most probably has a color management system, you can either use that or use something like tiny CMS to do the conversion with. Doing a ad hoc system will not make you happy, but using a ready made CMS isnt all that hard you just ask the CMS to do the conversion for you.

* If you want accurate color than you need to periodically measure your monitor and the background lighing conditions of your room. And yes the RGB color is different on each monitor!

2
  • Thanks for the tiny CMS link - I'll come back to that if it turns out we do need a better solution. For the moment the situation as from your last para: "If you want accurate colour..." - is that we don't need accurate, just readable. So we'll hopefully manage with the quick solution Aug 28, 2018 at 14:44
  • @StuartWhitehouse problem is that you may not require but your users allmost certainly have an expectation of what they are seeing being correct. Monkey see monkey do...
    – joojaa
    Aug 28, 2018 at 14:58
2

A quick fix for readability can be reducing the colorfulness. For 25% reduction let G=0,25.

Calculate the average of your RGB numbers of the too bright conversion result. Let it be A.

For every RGB number X calculate the new with reduced colorfulness, let's call it Y.

Use formula Y=(1-G)X+GA

ADD due the comment:

You can take another ad-hoc number, the brightness reduction factor =F. Let F=0,8 to reduce the brightness to 80%. Multiply all RGB numbers with it.

Be sure that these are no replacement for real color management math.

3
  • An interesting suggestion; I've given it a quick try and it does improve it to an extent; but still leaves the text very light - one of the differences between the RGB that Adobe is rendering and the simple formula gives me, is that Adobe's is darker in all 3 channels of the RGB. Aug 27, 2018 at 13:27
  • @StuartWhitehouse you can not simultaneously get easy and same results as a full fledged CMS. It is either good enough or if you are more pedantic than that then it needs full on color management
    – joojaa
    Aug 27, 2018 at 14:42
  • I've accepted this as the answer, because it looks like the suggestion of reducing colourfulness is probably good enough for us. I'd already considered reducing the brightness as per the addition - although that's risky if the text is light on a dark background. If it turns out later this bodge isn't enough we'll have to go the long way round and do the ColorSpaces properly Aug 28, 2018 at 14:42
2

R = 255 × (1-C) × (1-K)

I have no idea on what you just said... :o)

You probably need to understand that CMYK or RGB are not absolute values.

My classic example is a Cyan marker. Use it on a magazine paper and on newspaper. It is the same Cyan value, the same ink... different color.

To render a CMYK color on an RGB screen you need to take one color profile for what the CMYK values supposed to mean (glossy paper? Newspaper? uncoated paper? and then display it on an RGB screen.

This RGB screen is normally assuming to have a sRGB profile, but this can also have an additional profile specific for the screen.

In my opinion, it is better for you to study how to apply and convert between color profiles, more than making a 16 million individual conversions table.


Edited based on the comment.

All output here is on screen only

No. A CMYK value of C100, as my previous example, is 100% in both cases, but it is not the same color.

An RGB output is not an RGB output, it is a simulation of the conditions that are supposed to have in a CMYK print. That is why you need to define what would that CMYK print be in order to simulate it.


Been said that you probably want to stick to the most optimum print quality you can have, probably that is using some coated paper, to narrow it down, study how Fogra 39 works.


And yes, R = 255 × (1-C) × (1-K) means that you are not using Cyan, or (K) black, therefore, you are actually using the other two left components on a 4 ink printing system, Yellow, and Magenta. But it is funny how it implies to mention what you are not using instead of defining what you are actually using.

This could be problematic especially if your system actually uses more than 4 inks. There was an Hexachrome system that used 6. CMYK+GO


Edited 2.

One direct conversion method could be using the inverse of the color used as your formula states.

Red is the absence of Cyan. But this discards all black information.

Color profiles are not linear or have a smooth curve either.

Black is not added to a color from start, pastel colors do not have any black, and when the color is darker you are not only adding black but also diminishing the rest of the colors for some paper-absorption stuff.

All that information is present on the color profile.


Also, take a look at this. There are some explanations. CMYK values keep changing in RGB/8 colormode, CS6

1
  • The formula you quoted is for the red channel of RGB, based on the C and K from the CMYK colour. Regarding the magazine paper and newspaper... I understand colours give different results in different contexts, however all output here is on screen only - indeed on the same monitor(s). It is different software giving radically different results Aug 27, 2018 at 13:15
2

The answer by user287001 did provide some improvement, but was still problematic in many cases. I'm posting our eventual solution in case it helps others.

Disclaimer: this is not "correct" in any way. The coefficients below are ones we made up. (In fact we made a spreadsheet of desired inputs and outputs and just solved for them) As others have commented conversion depends on the usage of the CMYK. However in some cases we have only the information ColorSpace = "DeviceCMYK", components = c,m,y,k. No other information is available and this needs to be rendered on screen. In which specific situation the following c# code works for us...

// inputs are c,m,y,k components on a 0-1 scale
// work with INVERSE of CMYK values, on a 0-255 scale
float C = 255 * (1 - c);
float M = 255 * (1 - m);
float Y = 255 * (1 - y);
float K = 255 * (1 - k);

float r = 80 + 0.5882f * C - 0.3529f * M - 0.1373f * Y + 0.00185f * C * M + 0.00046f * Y * C; // no YM
float g = 66 - 0.1961f * C + 0.2745f * M - 0.0627f * Y + 0.00215f * C * M + 0.00008f * Y * C + 0.00062f * Y * M;
float b = 86 - 0.3255f * C - 0.1569f * M + 0.1647f * Y + 0.00046f * C * M + 0.00123f * Y * C + 0.00215f * Y * M;
// (for non c# readers, the 'f' suffix on the numbers just indicates the precision of the data type to hold the value)
r = r * K / 255;
g = g * K / 255;
b = b * K / 255;
// b,g,r are now the RGB components on a 0-255 scale, but will be out of bounds sometimes and need to be truncated to 0 and 255
Color RGB = Color.FromArgb(255, ((int)r).LimitTo(0, 255), ((int)g).LimitTo(0, 255), ((int)b).LimitTo(0, 255));
// LimitTo is an extension defined elsewhere - the usage should be obvious!

This produces results which are indistinguishable from standard PDF readers for our test cases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.