1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 00:17:46 +00:00

LibPDF+Meta: Use a CMYK ICC profile to convert CMYK to RGB

CMYK data describes which inks a printer should use to print a color.
If a screen should display a color that's supposed to look similar
to what the printer produces, it results in a color very different
to what Color::from_cmyk() produces. (It's also printer-dependent.)

There are many ICC profiles describing printing processes. It doesn't
matter too much which one we use -- most of them look somewhat
similar, and they all look dramatically better than Color::from_cmyk().

This patch adds a function to download a zip file that Adobe offers
on their web site. They even have a page for redistribution:
https://www.adobe.com/support/downloads/iccprofiles/icc_eula_win_dist.html

(That one leads to a broken download though, so this downloads the
end-user version.)

In case we have to move off this download at some point, there are also
a whole bunch of profiles at https://www.color.org/registry/index.xalter
that "may be used, embedded, exchanged, and shared without restriction".

The adobe zip contains a whole bunch of other useful and fun profiles,
so I went with it.

For now, this only unzips the USWebCoatedSWOP.icc file though, and
installs it in ${CMAKE_BINARY_DIR}/Root/res/icc/Adobe/CMYK/. In
Serenity builds, this will make it to /res/icc/Adobe/CMYK in the
disk image. And in lagom build, after #23016 this is the
lagom res staging directory that tools can install via
Core::ResourceImplementation. `pdf` and `MacPDF` already do that,
`TestPDF` now does it too.

The final piece is that LibPDF then loads the profile from there
and uses it for DeviceCMYK color conversions.

(Doing file access from the bowels of a library is a bit weird,
especially in a system that has sandboxing built in. But LibGfx does
that in FontDatabase too already, and LibPDF uses that, so it's not a
new problem.)
This commit is contained in:
Nico Weber 2024-01-30 21:04:37 -05:00 committed by Andrew Kaster
parent f840fb6b4e
commit 9c762b9650
6 changed files with 65 additions and 7 deletions

View file

@ -134,8 +134,23 @@ Vector<float> DeviceRGBColorSpace::default_decode() const
return { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f };
}
static RefPtr<Gfx::ICC::Profile> s_default_cmyk_profile;
static RefPtr<Core::Resource> s_default_cmyk_resource;
static ErrorOr<void> load_default_cmyk_profile()
{
auto resource = TRY(Core::Resource::load_from_uri("resource://icc/Adobe/CMYK/USWebCoatedSWOP.icc"sv));
auto profile = TRY(Gfx::ICC::Profile::try_load_from_externally_owned_memory(resource->data()));
s_default_cmyk_resource = move(resource);
s_default_cmyk_profile = move(profile);
return {};
}
ErrorOr<NonnullRefPtr<DeviceCMYKColorSpace>> DeviceCMYKColorSpace::the()
{
if (s_default_cmyk_profile.is_null())
TRY(load_default_cmyk_profile());
static auto instance = adopt_ref(*new DeviceCMYKColorSpace());
return instance;
}
@ -143,11 +158,17 @@ ErrorOr<NonnullRefPtr<DeviceCMYKColorSpace>> DeviceCMYKColorSpace::the()
PDFErrorOr<ColorOrStyle> DeviceCMYKColorSpace::style(ReadonlySpan<float> arguments) const
{
VERIFY(arguments.size() == 4);
auto c = arguments[0];
auto m = arguments[1];
auto y = arguments[2];
auto k = arguments[3];
return Color::from_cmyk(c, m, y, k);
u8 bytes[4];
bytes[0] = static_cast<u8>(arguments[0] * 255.0f);
bytes[1] = static_cast<u8>(arguments[1] * 255.0f);
bytes[2] = static_cast<u8>(arguments[2] * 255.0f);
bytes[3] = static_cast<u8>(arguments[3] * 255.0f);
auto pcs = TRY(s_default_cmyk_profile->to_pcs(bytes));
Array<u8, 3> output;
TRY(ICCBasedColorSpace::sRGB()->from_pcs(*s_default_cmyk_profile, pcs, output.span()));
return Color(output[0], output[1], output[2]);
}
Vector<float> DeviceCMYKColorSpace::default_decode() const