w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.36k stars 641 forks source link

[css-color] Separation / DeviceN color support #2023

Open faceless2 opened 6 years ago

faceless2 commented 6 years ago

EDIT: first few posts superceded, please skip to https://github.com/w3c/csswg-drafts/issues/2023#issuecomment-427946458


It's unclear how to define what are called "DeviceN" colors in PDF in css-colors-4, or even if it's possible. This is a concern for us as we develop our CSS to PDF converter, as ideally we would like to see css-colors cover the full gamut (ahem) of color spaces available in PDF.

The option to supply named strings to the color() function is, I presume, intended to enable spot colors. Without an example it's hard to see how this will work, but the current syntax

color() = color( [ <ident>? [ <number>+ | <string> ] [ / <alpha-value> ]? ]# , <color>? )

seems to imply something like this:

color: color(profilename "Pantone Reflex Blue CVC", lab(26.18 18.64 -59.95));

Only a single string is allowed, which means I am limited to using only one ink: from a PDF point-of-view I can define only a Separation color, not the more general DeviceN color. Also, I still have to specify the profile (or have it default to sRGB), which makes no sense in this context: If the device has the named ink, it will be used; if it doesn't, the fallback (which defines its own color space) will be used. Either way the ICC profile, defined here as "profilename", is unused.

Finally, I have no means of specifying how much ink. When specifying CMYK, for example, I can specify 80% cyan, 20% magenta. But this syntax doesn't allow that. I can name the ink, but that's it (and the quantity of ink is distinct from the alpha value; if we ignore overprinting, 0% black is not transparent; it's white).

I think this is easy to improve upon, but it might be better in a new function instead of overriding color(). A syntax like this:

color: separation("Pantone Reflex Blue CVC" 50%, lab(26.18 18.64 -59.95));

would allow you to specify the level of ink (50%), which the current syntax doesn't. You could also use multiple inks:

color: separation("Cyan" 0.8 "Magenta" 0.4 "Yellow" 0 "Black" 0 "Pantone Hexachrome Cyan C" 0.2 "PANTONE 1505 C" 0.4 "PANTONE 1355 C" 0 "PANTONE 1365 C" 0 PANTONE 7468 C" 0 PANTONE 476 C" 0, lab(80 -20 -40));

With nine inks that may look ridiculous, but it's an actual ColorSpace extracted from a PDF document in our test corpus. The syntax I had in mind here is:

separation ( [ string number-or-percentage ] {1,n} , color )

The concepts here directly translate to the DeviceN color space in PDF, which means that css-color-4 could represent any block color used in PDF.

(Also, by specifying the component values of all the inks, even the unused ones, it means using colors like this in a linear-gradient has a fairly well-defined solution, assuming the inks are the same on both sides of the gradient.)

Sorry if this has been discussed elsewhere, I didn't find mention of it here.

tabatkins commented 6 years ago

Also, I still have to specify the profile (or have it default to sRGB), which makes no sense in this context: If the device has the named ink, it will be used; if it doesn't, the fallback (which defines its own color space) will be used. Either way the ICC profile, defined here as "profilename", is unused.

The browser doesn't have any idea what the string means; it doesn't know that it's an ink name, and doesn't communicate it to the printer in any fashion that would allow it to know that directly. It just looks at the named profile and sees if it contains that named color in it, then returns that color.

@svgeesus, isn't this "extra ink colors" thing meant to be addressed by color profiles that just have those inks represented as some of the channels?

faceless2 commented 6 years ago

I think I see what's going on . If I can paraphrase what I think you were saying: for a named color, an ICC profile is referenced which maps that named color to a process color (more probably, to an exchange space like XYZ or Lab, but the effect is the same). So a "named color" in this context is treated exactly the same way as if you were using any other single-component ICC Profile, say a calibrated grey colorspace, and would theoretically look like this:

@color-profile pantonereflexblue {
    src: url(reflexblue.icc);
}
color: color(pantonereflexblue "Pantone Reflex Blue", #0000FF);

Is that what you meant? It's a very sensible approach. But it's not how PDF or PostScript typically do it!

Instead of an ICC profile, PDF and PostScript allow you to map a named color (or set of named colors) to a process color by defining a function. This is done entirely with PDF or PostScript primitives, so no ICC profile is involved.

For a single ink the function is often just linear: 100% ink maps to cmyk(100% 60% 20% 0), 0% ink maps to cmyk(0 0 0 0) with interpolation between. For more inks the function is MxN and often defined with a lookup table or via a PostScript function.

Effectively you're doing the same job as an ICC profile (mapping a range of color components in a custom colorspace to a range in a common colorspace), just via a different mechanism.

This was what I was hoping to cater for when I raised this issue. My suggestion would have done so for single colors in any colorspace and would cover linear gradients in a single ink. However it won't work for more inks when used in gradients.

So I think I need to revisit this.

If it's OK I'll leave this issue open but post a followup Monday when I have more time. I think a better solution (and a closer analogy to what is done in PDF/PostScript) is allowing a @color-profile to be defined using an alternative mechanism that's not an ICC profile - something like

@color-profile reflexblue {
    colors: "Pantone Reflex Blue CVC";
    color-space: device-cmyk;
    function: linear(0 0 0 0 100% 80% 60% 0);
}
faceless2 commented 6 years ago

Edit Oct 2018: This was a wrong-headed approach based on how the data was stored in PDF and PostScript, not on how it's actually specified. Please ignore this comment.

OK, here's an alternative suggestion for how to handle DeviceN and Separation colors in CSS in a way that maps directly to the approach used in PDF (and also PostScript Level 3, which I can confirm now I have reread the spec).

First, this involves no changes at all to the color() function, and no additional "separation" function as I initially suggested. All changes are local to the @color-profile rule.

Currently @color-profile can reference an ICC profile, which maps M color components in the colorspace being define to N components in a reference colorspace. It is essentially an MxN function. I'm proposing to add some new methods of doing this that don't make use of an ICC profile.

The reason for this is that specifying custom color-spaces in PDF and PostScript without reference to an ICC profile is common practice, and I am trying to ensure that css-color-4 can be used to do the same.

PDF and PostScript have several ways to do this, but the following apply to custom inks:

  1. Interpolation between two sets of values - for example from (0 0 0 0) to (1 0.89 0 0). Interpolation is exponential (like a gamma function) but will be linear if the exponent is 1.
  2. A lookup table
  3. A PostScript function
  4. A "stitching function" which uses two or more of the above, each applying to a different range of values (the same way the conversion from sRGB to XYZ is done)

As it's just a first proposal I'll demonstrate how you could do two of them.

The @color-profile rule would get three new declarations:

names : string {1,n}
fallback: ident
function: function-type [ , function-type ]*

with the following type definitions

function-type = interpolate( component* [ , gamma ] ) | url format
component = number-or-percentage
gamma = number

Here

  • "names" is the list of components (inks) in the colorspace
  • "fallback" is the fallback colorspace if those components are not available, which must be "srgb", "lab", "device-cmyk" or some other process color space which is known to the CSS engine (i.e. it's not also defined in a @color-profile rule)
  • "function" is a list of functions which map the components in this color space to the components in the fallback space. The CSS engine would use the first one that is able to process - it's the same model used to compute "src" in the @font-face rule, where the "url" and "format" syntax is also used
  • "component" is a value from 0..1 (and which could also be specified as 0..100%)

Some examples as to show how those would work:

Example 1

@color-profile reflexblue {
    names: "Pantone Reflex Blue";
    fallback: device-cmyk;
    function: interpolate(0 0 0 0 1 0.89 0 0, 1)
}
#logo { color: color(reflexblue 0.9, device-cmyk(90% 80% 0 0)) }

This defines a color-profile with a single component, "Pantone Reflex Blue". If the component is available in the output device (true if converting this CSS to PDF or PostScript, most certainly false otherwise), then it will be used directly. If not, the "fallback" and "function" object are used to convert the color into an equivalent in a known color-space.

Note: For single colors as shown here this is unnecessarily complex - color() already specified a fallback, so you could just use that. But for gradients, the intermediate values in the gradient need to be converted too, which requires evaluating the function.

"function" in this example is "interpolate", which takes N*2 arguments (where N is the number of components in the fallback colorspace), and an optional exponent with a default value of 1. A CSS engine aware of this syntax would take the value of the component specified in the color() function - 0.9 in this example - and use the interpolate function to convert it to the fallback color in the device-cmyk space:

c = (0.9 * (1-0) + 0) ^ 1;
m = (0.9 * (0.89-0) + 0) ^ 1
y = (0.9 * (0-0) + 0) ^ 1;
k = (0.9 * (0-0) + 0) ^ 1;

A CSS engine unaware of the syntax would simply use the fallback color specified in the color() function, device-cmyk(0.9 0.8 0 0)

Finally, a CSS engine that was able to use the specified color directly would do so. For reference, the above would translate into this syntax in PDF:

ColorSpace Resource R1: [/Separation /Pantone#20Reflex#Blue /DeviceCMYK <</C0 [0 0 0 0] /C1 [1 0.89 0 0] /FunctionType 2 /N 1 /Domain [0 1]>> ]
Stream: /R1 cs 0.9 scn

and PostScript

[ /Separation (Pantone Reflex Blue) /DeviceCMYK { dup 0.89 mul 0 0 } ] setcolorspace 0.9 setcolor

Example 2

I have trawled our collection of sample documents to make sure any DeviceN color-spaces they define could be described with this syntax. The most complex use gradients which shade over multiple inks simultaneously. This could be reproduced with this format:

@color-profile myink {
    names: "Pantone 5815" "Yellow" "Black";
    fallback: device-cmyk;
    function: url(myink.ps) format("application/postscript"), url(myink.js) format("text/javascript");
}

#block {
    background: linear-gradient(0deg, color(myink 1 0 0.4, device-cmyk(0 0 0.91 0.87)), color(myink 0 0.8 0.6, device-cmyk(0 0 0.8 0.6)))
}

The background for #block is defined as a linear gradient between colors in our custom color-space. Both color() objects specify a fallback color, so if the CSS engine is unable to parse the @color-profile rule for any reason, it can just calculate the gradient between the two fallback colors.

The @color-profile function converting our custom color-space to the fallback "device-cmyk" is defined by way of an external function, which is loaded from "myink.ps" if the CSS engine can handle the format "application/postscript", falling back to "myink.js" if the CSS engine can handle the format "text/javascript".

In both cases the function takes three arguments (the number of components in "names") and returns four values (the number of components in device-cmyk). The two files could look like this (incidentally this is a real-world PostScript function extracted from a PDF which I converted to JavaScript, so can be considered fairly typical)

myink.js

return function(c1,c2,c3) {
    return [ 0, 0, 1-((1-c2)*(1-(c1*0.91))), 1-((1-c3)*(1-(c1*0.79))) ];
}

myink.ps

1 4 1 roll 1 4 1 roll 1 index 1 cvr exch sub 4 1 roll 0 index 1 cvr exch sub 4 1 roll 7 -1 roll 3 index 0 mul 1 cvr exch sub mul 1 cvr exch sub 7 1 roll 6 -1 roll 3 index 0 mul 1 cvr exch sub mul 1 cvr exch sub 6 1 roll 5 -1 roll 3 index 0.91 mul 1 cvr exch sub mul 1 cvr exch sub 5 1 roll 4 -1 roll 3 index 0.790 mul 1 cvr exch sub mul 1 cvr exch sub 4 1 roll pop pop pop

To demonstrate how to calculate the color stops at the start of the gradient if the CSS engine can use a function defined in "text/javascript":

  1. Take the components specified in the color() function - 1, 0, 0.4
  2. Pass those components into the function returned from evaluating the "mygradient.js" script.
  3. Use the resulting values (0, 0, 0.2728, 0.3553) as components to the fallback space, "device-cmyk".

This approach is a bit unconventional but, if you want to be able to create the kind of color-spaces we see in real-world PDF documents, then I can't see how else to do it other than creating a function. Both PDF and PostScript will require the "application/postscript" format, and simply embed it in the generated file; it's impossible to convert from JS to PS and impractical to go the other way, so providing two representations of the same function struck me as a better option.

I'm presuming that if done this way, any JS would be limited to its local context only and have no access to the global document context. And if the function isn't evaluated (by user-agent choice, or because it's invalid), a reasonable approximation of the gradient will just interpolate between the two fallback colors.

Points to address

  1. If the overall idea gets approval I'm happy to draft additional function-type definitions which will allow a table-lookup to be used for a function, as is commonly seen in PDF.
  2. The existing syntax for color() function allows a "name" - @tabatkins implies this would be used when an ICC profile has a "named color". I'm not famiilar enough with ICC profile internals to know about that, but it does strike me that adding "named" to the @color-profile rule, as suggested here, might be a cleaner way to achieve the same thing.
  3. Given that a @color-profile no longer necessarily references an ICC profile, would @color-space be a better name for the rule?
  4. This approach is only focused on DeviceN and Separation colors, as these use custom inks and there's really no other way to do this - creating a custom ICC profile for each combination of inks is impractical. But PDF and PostScript have other methods of define color-spaces for process colors that don't use ICC profiles, e.g. CalRGB. Extending the @color-profile rule to handle these would be quite easy to do, although in this case an ICC profile would probably be available to do the same job.

Thanks for your consideration

faceless2 commented 5 years ago

My previous suggestion was bone-headed and while it reflected how the color-spaces were stored in PDF and PostScript, it didn't reflect how they were used or defined. This is much simpler and covers every use-case I have been able to find.

Problem

First, the existing syntax for "named" or "spot" colors requires them to be specified by means of an ICC "named color" profile, but these types of profile have poor industry support. They are not supported by any Adobe tools or Quark Express, and they are disallowed by the PDF language reference. There are virtually no examples of these ICC profiles available to download [1]

Second, the syntax for specifying a named color from a "named color" ICC profile prevents them from being used with other components. I can paint in 100% cyan, 20% black by specifying device-cmyk(1 0 0 0.2), but I cannot paint with 100% "PANTONE Warm Red" and 20% black, as the syntax does not allow it.

Proposal

Allow the definition of spot colors without reference to an ICC profile by:

This gives you the same sort of logical view on the colors as is used by Adobe Illustrator, and is flexible enough to cover all the practical uses of Separation and DeviceN ColorSpaces as used in PDF. It doesn't replace the existing syntax for loading named colors from an ICC profile, but it does provide another way to define them.

Proposed new attribute for @color-profile

components: <string> <number>+ [ , <string> <number+ ]*

  Specifies the components that make up this color space. If not specified, the
  default is the list of components defined in the ICC profile or color space
  referenced by the "src" attribute. Otherwise, the value is a comma-separated
  list of ink-names and their process-color values in the color space referenced
  by "src".

Proposed change of definition for "src" attribute in @color-profile:

src: <url> | device-cmyk | srgb | lab;

  The src descriptor specifies either the URL to retrieve the color-profile
  information from, or that the components are to be taken from one of
  the pre-defined color profiles.

Example 1: Define a single ink from the PANTONE range as cmyk(1, 0.723, 0, 0.02), with the CMYK values referencing the "swop.icc" profile.

@color-profile reflex-blue {
  src: url(swop.icc);
  components: "PANTONE Reflex Blue C" 1 0.723 0 0.02;
}
body {
  color: color(reflex-blue 1);
}

Example 2: As above, but the color is defined against the "device-cmyk" space.

@color-profile reflex-blue {
  src: device-cmyk;
  components: "PANTONE Reflex Blue C" 1 0.723 0 0.02;
}

Example 3: Define two inks from the PANTONE range and use them in a gradient (making our own custom duotone color-space).

@color-profile mygradient {
  src: device-cmyk;
  components: "PANTONE Reflex Blue C" 1 0.723 0 0.02, "PANTONE Warm Red C" 0 0.75 0.9 0;
}
body {
  background: linear-gradient(color(mygradient 0 1), color(mygradient 1 0));
}

Example 4: Recreate all the CMYK components and add an additional ink, creating a 5-component colorspace.

@color-profile cmykb {
  src: device-cmyk;
  components: "Cyan" 1 0 0 0,
              "Magenta" 0 1 0 0,
              "Yellow" 0 0 1 0,
              "Black" 0 0 0 1,
              "PANTONE Reflex Blue C" 1 0.723 0 0.02;
}
body {
  background: linear-gradient(color(cmykb 0 1 0 0 1), color(cmykb 1 0 0 1 0));
}

Example 5: With no components listed the syntax effectively defines an alias.

@color-profile cmyk {
  src: device-cmyk;
}
body {
  color: color(cmyk 0 0 0 1)
}

Rendering

If the output device has those components (which will be the case for PDF or PostScript, but nothing else), then the components can be used as defined. If not, then converting the named components into the components for the color profile referenced by "src" can be done with the formula

Xᵢ = 1 - ( (1-(C₀ Nᵢ₀)) (1-(C₁ Nᵢ₁)) ... (1-(Cⱼ Nᵢⱼ) )

where

This is a standard linear blend. It's what Adobe Illustrator does when creating a DeviceN ColorSpace from multiple spot colors, and I haven't been able to find an example in our corpus of test PDF documents of a DeviceN colorspace that isn't created this way. If there ever is a need, an additional attribute could define a different blend formula.

The formula works for any color space, providing the component values are normalized to 0..1

We've tested this in-house and it's working nicely: see output.pdf.

output

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Test linear gradient between Pantone named colors</title>
  <style type="text/css">
      @color-profile duotone {
          src: device-cmyk;
          components: "PANTONE Reflex Blue C" 1 0.723 0 0.02, "PANTONE Warm Red C" 0 0.75 0.9 0;
      }
      body {
          font: bold 18px sans-serif;
      }
      #left {
          color: color(duotone 1 0);
          float: left;
      }
      #right {
          color: color(duotone 0 1);
          float: right;
      }
      #gradient {
          background: linear-gradient(to right, color(duotone 1 0), color(duotone 0 1));
          height: 100px;
          clear: both;
      }
   </style>
 </head>
 <body>
   <div>
    <span id="left">PANTONE Reflex Blue C</span>
    <span id="right">PANTONE Warm Red C</span>
   </div>
   <div id="gradient"></div>
 </body>
</html>

[1] https://www.efi.com/en-gb/marketing/fiery-servers-and-software/downloads/pantone-library/ is the only one I've found.

Crissov commented 4 years ago

the existing syntax for "named" or "spot" colors requires them to be specified by means of an ICC "named color" profile, but these types of profile have poor industry support. They are not supported by any Adobe tools or Quark Express, and they are disallowed by the PDF language reference. There are virtually no examples of these ICC profiles available to download

Does this mean that closing #817 as invalid was a bit bold then?

faceless2 commented 4 years ago

@Crissov re #817 - well, with respect I'm not sure it was the best of all possible options for specifying spot colors :-) It would require browsers to ship with a list of named colors from various palettes - problematic as they are often trademarked; it doesn't allow custom colors to be specified, and it doesn't provide colorimetric information so they can be combined.

But, named color ICC profiles only solve the first of those problems, so I disagree with the reasons given for closing it too..

faceless2 commented 4 years ago

For clarification here's a worked evaluation of the "duotone" example above. I have two spot colors and I'm trying to perform a blend between them. I'm a browser, so I work in RGB. What do I do?

Lets evaluate the color three-quarters of the way from blue to red. At this point I have 75% of "PANTONE Reflex Blue C" - defined as device-cmyk(1 0.723 0 0.02), and 25% of "PANTONE Warm Red C" - defined as device-cmyk(0 0.75 0.9 0).

As I have neither of these colors on my output device, I must convert them to their fallback ColorSpace, device-cmyk.

var t0 = 0.75; // amount of color 0
var t1 = 0.25; // amount of color 1

var c = 1 - ( (1-(t0 * 1    )) * (1-(t1 * 0   )) );
var m = 1 - ( (1-(t0 * 0.723)) * (1-(t1 * 0.75)) );
var y = 1 - ( (1-(t0 * 0.0  )) * (1-(t1 * 0.9 )) );
var k = 1 - ( (1-(t0 * 0.02 )) * (1-(t1 * 0   )) );

// c=0.75 m=0.628 y=0.225 k=0.015

Conversion is now from CMYK to sRGB - a much simpler problem (let me refer you to #2022 for my thoughts on that)

Finally, there's one other justification for all this which I missed in my post above, and it's that spot colors are sometimes used in PDF output for things that aren't colors at all. We have a customer that does greeting cards - they use a separation in the output to mark score lines on the cards for folding or cutting. I believe they can also used for things like where to put glue or glitter, if you have such a machine.

These options would never be possible if spot colors were limited to those defined in ICC profiles, which would be a shame - who doesn't want more glitter on their printouts?

svgeesus commented 4 years ago

@faceless2 I know it has been a while but I am now coming back to this issue, re-reading the comments, and figuring out how to accommodate the desired functionality.

I agree that named color profiles are not well supported or widely used; and being specifically disallowed by PDF is also a big minus.

Looking at your more recent examples, how would they be written with the same duotone but if you had the actual spot inks, rather than process-color equivalents?

faceless2 commented 4 years ago

Thanks for looking at this Chris!

how would they be written with the same duotone but if you had the actual spot inks, rather than process-color equivalents?

You mean the HTML example from blue to red? So if it was defined instead as something a bit like this?

@color-profile --duotone {
    components: "PANTONE Reflex Blue C", "PANTONE Warm Red C"
}

or even

@color-profile duotone {
    components: color(--reflex-blue, 1), color(--warm-red, 1);
}

Currently I'm not proposing that at all. I would suggest that a "custom color-profile" is defined by reference to another color-profile which must be a process color-profile: device-cmyk, an ICC profile, or (mathematically possible, but with no practical purpose) RGB or Lab.

We did experiment with a syntax like the above but rejected it in the end:

  1. It's more complicated. Allowing a color-profile to somehow reference or build upon another color-profile means you need loop detection, dynamically updating when a profile loads is more complex, you need to reject invalid combinations etc.

  2. PDF is likely to be the only target media that doesn't immediately collapse these to RGB, and PDF requires that a) the inks defined in any custom color-profile are defined using process colors, and b) that the same color-profile is use for all inks. The syntax I proposed reflects this.

Consider if you had a syntax like the example earlier in this comment, and "reflex blue" is defined against device-cmyk and "warm red" was defined against FOGRA 39. Well, you're stuck: the only way to proceed without error is silently convert them to be in the same space.

It's more of a problem if you're defining a duotone from a spot color to a process color, eg. black, especially when you consider the cascade:

/*
Define "warm red" twice - first in device-cmyk, and then in our chosen
FORGRA39 profile. If the latter is not available, we'll fall back to the former.
*/
@color-profile -warm-red {
  src: device-cmyk;
  components: "PANTONE Warm Red C" 0 0.75 0.9 0;
}
@color-profile -warm-red {
  src: url(fogra39.icc);
  components: "PANTONE Warm Red C" 0 0.76 0.9 0.01;
}

/* Now define a duotone from black to the "warm red" we defined above. */
@color-profile duotone {
  components: black, color(--warm-red, 1); /* Nope! black is sRGB */
  components: device-cmyk(0 0 0 1), color(--warm-red, 1); /* Tsk, presuming warm-red is also device-cmyk */
}

Yes, it means you have to define a color twice if you're going to use it as a single spot-color and in a duotone. It's not the end of the world and variables mitigate this:

:root {
    --reflex-blue: "Pantone Reflex Blue C"  1 0.723 0 0.02;
    --warm-red: "Pantone Warm Red C" 0 0.75 0.9 0;;
}
@color-profile --reflex-blue {
    src: device-cmyk;
    components: var(--reflex-blue);
}
@color-profile --warm-red {
    src: device-cmyk;
    components: var(--wam-red);
}
@color-profile --blue-red-duotone {
    src: device-cmyk;
    components: var(--reflex-blue), var(--warm-red);
}

Having quite a few spot colors in a document is probably quite common - business graphics, corporate colors and so on. But I would think a gradient involving a spot color, while something that should be possible, is going to be a lot less common for sure. If you have N spot colors you're not going to need to define NxN gradients.

If you're still unconvinced let me know, and I will try and put some numbers on these statements.

I should finish by saying if there is some sort of syntactic sugar to mitigate this further I'm all for it, of course. But it shouldn't be much more than that in my opinion.

Again, thanks for looking at this.

svgeesus commented 4 years ago

Mike, thanks for helping me understand the complexities and constraints of PDF as a rendering target.

PDF is likely to be the only target media that doesn't immediately collapse these to RGB, and PDF requires that a) the inks defined in any custom color-profile are defined using process colors, and b) that the same color-profile is use for all inks. The syntax I proposed reflects this.

So to help me understand - suppose that there weren't any standards for 7-color printing and I was inventing FOGRA55 by creating tests in PDF to be printed. So Basically I start with FOGRA51 CMYK and then add three spot color inks:

I want those separations printed with the actual spot inks, and furthermore I can't express those as CMYK process colors because they are outside the gamut of my CMYK set (which is why they are added in the first place, to extend the gamut).

The only way I can wrap my head around that is to think that PDF requires the process color equivalents in the same way that CSS has optional fallback colors for device-cmyk - so that it can do something with them when a blend is required. But only as a fallback, the actual spot colors will be used when it prints, right? Otherwise there would be no way to apply spot "colors" like metallics, varnishes, embossing and folding.

Yes?

faceless2 commented 4 years ago

Yes, exactly.

When you create your N-color ColorSpace, you name all the inks. If your device has all those inks available¹, it will use them as specified. If not, the fallback (aka the process color equivalents) is there to give you an approximation so you can render it as best as you can, but the fallback isn't used for anything else.

I keep using "Pantone Reflex Blue C" for my examples because it, too, is notoriously difficult to reproduce with process CMYK (as you point out, if it wasn't, you wouldn't need it). So that single spot color is entirely defined by a) its name and b) a best-effort CMYK approximation. The "Device N" spaces are identical, just with more than one ink.

I'll scare both of us if we go much further than the above, but - as you brought it up - in your example going from FOGRA51 to FOGRA55 I understand you'd probably use an "NChannel" DeviceN rather than basic DeviceN in PDF, as you would only need to simulate 3 of the inks. But I'm hitting the limits of my knowledge here, and I'm not trying to reinvent the whole PDF color model (shudders). If we can get the same basic concepts as PDF in place, it can be expanded if required - and what's here is enough to convert any color in this model to sRGB with reasonable fidelity.

[1] qualifying this by saying I don't deal with the printing side at all, so know little of translating a PDF to paper. For PDF-people, what I'm describing is basic "DeviceN" colorspaces with linear tint transforms. Non-linear blends, NChannel, spectral information etc are not part of this proposal.

faceless2 commented 4 years ago

@color-profile --reflex-blue { src: device-cmyk; components: var(--reflex-blue); }

That's not going to work is it? @color-profile won't expand variables set on the root element because it's not a property declaration one one of its descendants. So strike that bit.

tabatkins commented 4 years ago

Yeah, invalid, tho potentially could be defined to be valid; there's been discussion in the past of allowing other at-rules to reference variables from the root element.

svgeesus commented 3 years ago

When you create your N-color ColorSpace, you name all the inks. If your device has all those inks available¹, it will use them as specified.

The "naming" part troubles me. How do we know that a device has an ink corresponding to "random string".

Orange: "Pantone Orange 021C" which is lab(60.8% 65.7 85.1)

The fact that I need to give a (poor) process equivalent to that orange, but can't give a colorimetric definition (like the Lab value) but only a string, also worries me. There is no way to reason about the resulting color. Its just as opaque as device-cmyk used to be (except we fixed that).

faceless2 commented 3 years ago

It's quite possible I've misunderstood your point, but I'll plunge in anyway. Feel free to correct me.

From a PDF perspective, we could work with the fallback color in almost any color-space you like. CMYK is by far the most common - these things are destined for print, after all - but I've seen Lab used. Also allowable in the PDF format is RGB, or any ICC-based colorspace - it doesn't have to be device-cmyk, it could be Fogra39 for example. Any one of these would give a colorimetric value, no?

@color-profile --orange-021c {
  src: Lab;
  components: "PANTONE Orange 021C" 60% 65.7 85.1;
}

@color-profile --warm-red {
  src: fogra39;
  components: "PANTONE Warm Red C" 0 0.76 0.9 0.01;
}

Of course interpolation on a device without the ink available will happen in the fallback space, and as we know this looks pretty rubbish with Lab. Lch would be ideal except it's not available in PDF. I can simulate a gradient between two Lch colors in Lab quite easily, by dividing the gradient up into sections until the delta-E is close enough. But when generating a PDF I can't do this with spot colors defined against Lch; the decision on whether to interpolate in the named-ink or the fallback-color doesn't happen until the device rendering the PDF.

All of which means that from the perspective of someone generating a PDF, it's possible to accurately convey both the above examples, although the Lab one wouldn't be great in a gradient. But if you specified the ink in Lch (or HSL, or anything not native to PDF), we'd have to convert it to something natively supported in PDF first. That would likely be CMYK as it interpolates better. But this doesn't seem a problem to me - I think an author using a named color will be aware of this.

From the perspective of someone trying to display these on screen, I think it's even easier isn't it? Provided a space other than device-cmyk was used for the fallback color, you can get an accurate colorimetric value at any point along a gradient.

faceless2 commented 3 years ago

Of course interpolation on a device without the ink available will happen in the fallback space, and as we know this looks pretty rubbish with Lab. Lch would be ideal except it's not available in PDF. I can simulate a gradient between two Lch colors in Lab quite easily, by dividing the gradient up into sections until the delta-E is close enough. But when generating a PDF I can't do this with spot colors defined against Lch; the decision on whether to interpolate in the named-ink or the fallback-color doesn't happen until the device rendering the PDF.

Actually I'm wrong about this. It is possible to interpolate in the fallback space if the fallback space is Lch. It won't accurately represent how the blending would work on paper if the device had the inks available, but I've just done it so it's definitely possible.

svgeesus commented 2 years ago

Re-tagging to CSS Color 5

svgeesus commented 1 year ago

Moving new feature requests to CSS Color 6, to stabilize CSS Color 5 which is about ready to move to CR.