summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--NOTICE3
-rw-r--r--crates/typst/Cargo.toml1
-rw-r--r--crates/typst/assets/CGATS001Compat-v2-micro.iccbin0 -> 8464 bytes
-rw-r--r--crates/typst/src/visualize/color.rs55
-rw-r--r--tests/ref/bugs/gradient-cmyk-encode.pngbin875 -> 1601 bytes
-rw-r--r--tests/ref/compiler/color.pngbin1344 -> 1407 bytes
-rw-r--r--tests/typ/compiler/methods.typ4
9 files changed, 63 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 842e9abe..df30f43d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1779,6 +1779,12 @@ dependencies = [
]
[[package]]
+name = "qcms"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edecfcd5d755a5e5d98e24cf43113e7cdaec5a070edd0f6b250c03a573da30fa"
+
+[[package]]
name = "quick-xml"
version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2554,6 +2560,7 @@ dependencies = [
"log",
"once_cell",
"palette",
+ "qcms",
"phf",
"rayon",
"regex",
diff --git a/Cargo.toml b/Cargo.toml
index b711b35f..9da3f904 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,6 +77,7 @@ pixglyph = "0.3"
proc-macro2 = "1"
pulldown-cmark = "0.9"
quote = "1"
+qcms = "0.3.0"
rayon = "1.7.0"
regex = "1"
resvg = { version = "0.38.0", default-features = false, features = ["raster-images"] }
diff --git a/NOTICE b/NOTICE
index 1291b0b3..129ee321 100644
--- a/NOTICE
+++ b/NOTICE
@@ -2,7 +2,8 @@ Licenses for third party components used by this project can be found below.
================================================================================
The Creative Commons Zero v1.0 Universal License applies to:
-* The ICC profiles found in `crates/typst/icc/*`
+* The ICC profiles found in `crates/typst-pdf/src/icc/*` and
+ `crates/typst/assets/*`.
CC0 1.0 Universal
diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml
index b9e3b494..650f53c3 100644
--- a/crates/typst/Cargo.toml
+++ b/crates/typst/Cargo.toml
@@ -41,6 +41,7 @@ lipsum = { workspace = true }
log = { workspace = true }
once_cell = { workspace = true }
palette = { workspace = true }
+qcms = { workspace = true }
phf = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
diff --git a/crates/typst/assets/CGATS001Compat-v2-micro.icc b/crates/typst/assets/CGATS001Compat-v2-micro.icc
new file mode 100644
index 00000000..b5a73495
--- /dev/null
+++ b/crates/typst/assets/CGATS001Compat-v2-micro.icc
Binary files differ
diff --git a/crates/typst/src/visualize/color.rs b/crates/typst/src/visualize/color.rs
index 90ac8c3d..735a922c 100644
--- a/crates/typst/src/visualize/color.rs
+++ b/crates/typst/src/visualize/color.rs
@@ -9,6 +9,7 @@ use palette::encoding::{self, Linear};
use palette::{
Darken, Desaturate, FromColor, Lighten, Okhsva, OklabHue, RgbHue, Saturate, ShiftHue,
};
+use qcms::Profile;
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::foundations::{
@@ -30,6 +31,36 @@ pub type Luma = palette::luma::Luma<encoding::Srgb, f32>;
/// Equivalent of [`std::f32::EPSILON`] but for hue angles.
const ANGLE_EPSILON: f32 = 1e-5;
+/// The ICC profile used to convert from CMYK to RGB.
+///
+/// This is a minimal CMYK profile that only contains the necessary information
+/// to convert from CMYK to RGB. It is based on the CGATS TR 001-1995
+/// specification. See
+/// https://github.com/saucecontrol/Compact-ICC-Profiles#cmyk.
+static CGATS001_COMPACT_PROFILE: Lazy<Box<Profile>> = Lazy::new(|| {
+ let bytes = include_bytes!("../../assets/CGATS001Compat-v2-micro.icc");
+ Profile::new_from_slice(bytes, false).unwrap()
+});
+
+/// The target sRGB profile.
+static SRGB_PROFILE: Lazy<Box<Profile>> = Lazy::new(|| {
+ let mut out = Profile::new_sRGB();
+ out.precache_output_transform();
+ out
+});
+
+static TO_SRGB: Lazy<qcms::Transform> = Lazy::new(|| {
+ qcms::Transform::new_to(
+ &CGATS001_COMPACT_PROFILE,
+ &SRGB_PROFILE,
+ qcms::DataType::CMYK,
+ qcms::DataType::RGB8,
+ // Our input profile only supports perceptual intent.
+ qcms::Intent::Perceptual,
+ )
+ .unwrap()
+});
+
/// A color in a specific color space.
///
/// Typst supports:
@@ -1691,6 +1722,8 @@ impl Cmyk {
Cmyk::new(l * 0.75, l * 0.68, l * 0.67, l * 0.90)
}
+ // This still uses naive conversion, because qcms does not support
+ // converting to CMYK yet.
fn from_rgba(rgba: Rgb) -> Self {
let r = rgba.red;
let g = rgba.green;
@@ -1709,11 +1742,23 @@ impl Cmyk {
}
fn to_rgba(self) -> Rgb {
- let r = (1.0 - self.c) * (1.0 - self.k);
- let g = (1.0 - self.m) * (1.0 - self.k);
- let b = (1.0 - self.y) * (1.0 - self.k);
-
- Rgb::new(r, g, b, 1.0)
+ let mut dest: [u8; 3] = [0; 3];
+ TO_SRGB.convert(
+ &[
+ (self.c * 255.0).round() as u8,
+ (self.m * 255.0).round() as u8,
+ (self.y * 255.0).round() as u8,
+ (self.k * 255.0).round() as u8,
+ ],
+ &mut dest,
+ );
+
+ Rgb::new(
+ dest[0] as f32 / 255.0,
+ dest[1] as f32 / 255.0,
+ dest[2] as f32 / 255.0,
+ 1.0,
+ )
}
fn lighten(self, factor: f32) -> Self {
diff --git a/tests/ref/bugs/gradient-cmyk-encode.png b/tests/ref/bugs/gradient-cmyk-encode.png
index 7bd82cce..5002442f 100644
--- a/tests/ref/bugs/gradient-cmyk-encode.png
+++ b/tests/ref/bugs/gradient-cmyk-encode.png
Binary files differ
diff --git a/tests/ref/compiler/color.png b/tests/ref/compiler/color.png
index a585bf30..69dbe5e8 100644
--- a/tests/ref/compiler/color.png
+++ b/tests/ref/compiler/color.png
Binary files differ
diff --git a/tests/typ/compiler/methods.typ b/tests/typ/compiler/methods.typ
index 262f1497..a3d8c658 100644
--- a/tests/typ/compiler/methods.typ
+++ b/tests/typ/compiler/methods.typ
@@ -143,8 +143,8 @@
#test(rgb(1, 2, 3).to-hex(), "#010203")
#test(rgb(1, 2, 3, 4).to-hex(), "#01020304")
#test(luma(40).to-hex(), "#282828")
-#test-repr(cmyk(4%, 5%, 6%, 7%).to-hex(), "#e4e1df")
-#test-repr(rgb(cmyk(4%, 5%, 6%, 7%)).components(), (89.28%, 88.35%, 87.42%, 100%))
+#test-repr(cmyk(4%, 5%, 6%, 7%).to-hex(), "#e0dcda")
+#test-repr(rgb(cmyk(4%, 5%, 6%, 7%)).components(), (87.84%, 86.27%, 85.49%, 100%))
#test-repr(rgb(luma(40%)).components(alpha: false), (40%, 40%, 40%))
#test-repr(cmyk(luma(40)).components(), (11.76%, 10.67%, 10.51%, 14.12%))
#test-repr(cmyk(rgb(1, 2, 3)), cmyk(66.67%, 33.33%, 0%, 98.82%))