[librsvg: 1/2] (#605): Compute 'bolder' and 'lighter' font-weight correctly
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 1/2] (#605): Compute 'bolder' and 'lighter' font-weight correctly
- Date: Fri, 12 Jun 2020 00:53:42 +0000 (UTC)
commit 88397d9f1cff6964ded0734790e72427a89ad940
Author: Federico Mena Quintero <federico gnome org>
Date: Wed Jun 10 20:05:58 2020 -0500
(#605): Compute 'bolder' and 'lighter' font-weight correctly
Previously, font-weight="bolder" or "lighter" would map
unconditionally to pango's Ultrabold and Light, which is wrong as
those are relative values that must be computed with respect to the
inherited value.
We now do this:
* First, support arbitrary numbers in the range [1, 1000] instead of
the fixed set 100/200/.../900, per css-fonts-4 -
https://drafts.csswg.org/css-fonts-4/#font-weight-prop
* Second, use the "relative weights" table from
https://drafts.csswg.org/css-fonts-4/#relative-weights
Note that this is different from
https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
which simply made "bolder" and "lighter" go up and down the 100..900
scale. That had the effect of not always performing a visible change
in weight, since most font families only have a few weights available
and do not cover the whole range.
Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/605
Cargo.lock | 1 +
rsvg_internals/Cargo.toml | 1 +
rsvg_internals/src/font_props.rs | 88 ++++++++++++++------
rsvg_internals/src/property_defs.rs | 14 +++-
rsvg_internals/src/text.rs | 16 +---
.../reftests/svg1.1/text-fonts-02-t-ref.png | Bin 0 -> 17168 bytes
tests/fixtures/reftests/svg1.1/text-fonts-02-t.svg | 92 +++++++++++++++++++++
7 files changed, 172 insertions(+), 40 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index bb117eb6..f7a71ada 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1318,6 +1318,7 @@ version = "0.0.1"
dependencies = [
"cairo-rs 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"criterion 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)",
"data-url 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/rsvg_internals/Cargo.toml b/rsvg_internals/Cargo.toml
index 08a73243..d95ef8b2 100644
--- a/rsvg_internals/Cargo.toml
+++ b/rsvg_internals/Cargo.toml
@@ -12,6 +12,7 @@ edition = "2018"
# librsvg_crate/src/lib.rs - toplevel example in the docs
cairo-rs = { version="0.8.0", features=["v1_16"] }
cairo-sys-rs = "0.9.0"
+cast = "0.2.3"
cssparser = "0.27.1"
data-url = "0.1"
encoding = "0.2.33"
diff --git a/rsvg_internals/src/font_props.rs b/rsvg_internals/src/font_props.rs
index e09061d9..3b00a6d8 100644
--- a/rsvg_internals/src/font_props.rs
+++ b/rsvg_internals/src/font_props.rs
@@ -1,5 +1,6 @@
//! CSS font properties.
+use cast::u16;
use cssparser::Parser;
use crate::drawing_ctx::ViewParams;
@@ -104,22 +105,14 @@ impl Parse for FontSizeSpec {
}
}
-// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
+// https://drafts.csswg.org/css-fonts-4/#font-weight-prop
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FontWeightSpec {
Normal,
Bold,
Bolder,
Lighter,
- W100,
- W200,
- W300,
- W400,
- W500,
- W600,
- W700,
- W800,
- W900,
+ Weight(u16),
}
impl Parse for FontWeightSpec {
@@ -137,22 +130,69 @@ impl Parse for FontWeightSpec {
.or_else(|_: ParseError| {
let loc = parser.current_source_location();
let i = parser.expect_integer()?;
- match i {
- 100 => Ok(FontWeightSpec::W100),
- 200 => Ok(FontWeightSpec::W200),
- 300 => Ok(FontWeightSpec::W300),
- 400 => Ok(FontWeightSpec::W400),
- 500 => Ok(FontWeightSpec::W500),
- 600 => Ok(FontWeightSpec::W600),
- 700 => Ok(FontWeightSpec::W700),
- 800 => Ok(FontWeightSpec::W800),
- 900 => Ok(FontWeightSpec::W900),
- _ => Err(loc.new_custom_error(ValueErrorKind::parse_error("parse error"))),
+ if (1..=1000).contains(&i) {
+ Ok(FontWeightSpec::Weight(u16(i).unwrap()))
+ } else {
+ Err(loc.new_custom_error(ValueErrorKind::value_error(
+ "value must be between 1 and 1000 inclusive",
+ )))
}
})
}
}
+impl FontWeightSpec {
+ #[rustfmt::skip]
+ pub fn compute(&self, v: &Self) -> Self {
+ use FontWeightSpec::*;
+
+ // Here, note that we assume that Normal=W400 and Bold=W700, per the spec. Also,
+ // this must match `impl From<FontWeightSpec> for pango::Weight`.
+ //
+ // See the table at https://drafts.csswg.org/css-fonts-4/#relative-weights
+
+ match *self {
+ Bolder => match v.numeric_weight() {
+ w if ( 1..100).contains(&w) => Weight(400),
+ w if (100..350).contains(&w) => Weight(400),
+ w if (350..550).contains(&w) => Weight(700),
+ w if (550..750).contains(&w) => Weight(900),
+ w if (750..900).contains(&w) => Weight(900),
+ w if 900 <= w => Weight(w),
+
+ _ => unreachable!(),
+ }
+
+ Lighter => match v.numeric_weight() {
+ w if ( 1..100).contains(&w) => Weight(w),
+ w if (100..350).contains(&w) => Weight(100),
+ w if (350..550).contains(&w) => Weight(100),
+ w if (550..750).contains(&w) => Weight(400),
+ w if (750..900).contains(&w) => Weight(700),
+ w if 900 <= w => Weight(700),
+
+ _ => unreachable!(),
+ }
+
+ _ => *self,
+ }
+ }
+
+ // Converts the symbolic weights to numeric weights. Will panic on `Bolder` or `Lighter`.
+ pub fn numeric_weight(self) -> u16 {
+ use FontWeightSpec::*;
+
+ // Here, note that we assume that Normal=W400 and Bold=W700, per the spec. Also,
+ // this must match `impl From<FontWeightSpec> for pango::Weight`.
+ match self {
+ Normal => 400,
+ Bold => 700,
+ Bolder | Lighter => unreachable!(),
+ Weight(w) => w,
+ }
+ }
+}
+
// https://www.w3.org/TR/css-text-3/#letter-spacing-property
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LetterSpacingSpec {
@@ -296,7 +336,7 @@ mod tests {
);
assert_eq!(
<FontWeightSpec as Parse>::parse_str("100"),
- Ok(FontWeightSpec::W100)
+ Ok(FontWeightSpec::Weight(100))
);
}
@@ -304,7 +344,9 @@ mod tests {
fn detects_invalid_font_weight() {
assert!(<FontWeightSpec as Parse>::parse_str("").is_err());
assert!(<FontWeightSpec as Parse>::parse_str("strange").is_err());
- assert!(<FontWeightSpec as Parse>::parse_str("314").is_err());
+ assert!(<FontWeightSpec as Parse>::parse_str("0").is_err());
+ assert!(<FontWeightSpec as Parse>::parse_str("-1").is_err());
+ assert!(<FontWeightSpec as Parse>::parse_str("1001").is_err());
assert!(<FontWeightSpec as Parse>::parse_str("3.14").is_err());
}
diff --git a/rsvg_internals/src/property_defs.rs b/rsvg_internals/src/property_defs.rs
index dc67eacc..61e7ad3e 100644
--- a/rsvg_internals/src/property_defs.rs
+++ b/rsvg_internals/src/property_defs.rs
@@ -304,13 +304,23 @@ make_property!(
"small-caps" => SmallCaps,
);
-// https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-weight
+// https://drafts.csswg.org/css-fonts-4/#font-weight-prop
make_property!(
ComputedValues,
FontWeight,
default: FontWeightSpec::Normal,
- inherits_automatically: true,
newtype_parse: FontWeightSpec,
+ property_impl: {
+ impl Property<ComputedValues> for FontWeight {
+ fn inherits_automatically() -> bool {
+ true
+ }
+
+ fn compute(&self, v: &ComputedValues) -> Self {
+ FontWeight(self.0.compute(&v.font_weight().0))
+ }
+ }
+ }
);
// https://www.w3.org/TR/SVG/text.html#LetterSpacingProperty
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 1fc14009..d93a9773 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -846,21 +846,7 @@ impl From<FontStretch> for pango::Stretch {
impl From<FontWeightSpec> for pango::Weight {
fn from(w: FontWeightSpec) -> pango::Weight {
- match w {
- FontWeightSpec::Normal => pango::Weight::Normal,
- FontWeightSpec::Bold => pango::Weight::Bold,
- FontWeightSpec::Bolder => pango::Weight::Ultrabold,
- FontWeightSpec::Lighter => pango::Weight::Light,
- FontWeightSpec::W100 => pango::Weight::Thin,
- FontWeightSpec::W200 => pango::Weight::Ultralight,
- FontWeightSpec::W300 => pango::Weight::Semilight,
- FontWeightSpec::W400 => pango::Weight::Normal,
- FontWeightSpec::W500 => pango::Weight::Medium,
- FontWeightSpec::W600 => pango::Weight::Semibold,
- FontWeightSpec::W700 => pango::Weight::Bold,
- FontWeightSpec::W800 => pango::Weight::Ultrabold,
- FontWeightSpec::W900 => pango::Weight::Heavy,
- }
+ pango::Weight::__Unknown(w.numeric_weight().into())
}
}
diff --git a/tests/fixtures/reftests/svg1.1/text-fonts-02-t-ref.png
b/tests/fixtures/reftests/svg1.1/text-fonts-02-t-ref.png
new file mode 100644
index 00000000..36cdc385
Binary files /dev/null and b/tests/fixtures/reftests/svg1.1/text-fonts-02-t-ref.png differ
diff --git a/tests/fixtures/reftests/svg1.1/text-fonts-02-t.svg
b/tests/fixtures/reftests/svg1.1/text-fonts-02-t.svg
new file mode 100644
index 00000000..ba294714
--- /dev/null
+++ b/tests/fixtures/reftests/svg1.1/text-fonts-02-t.svg
@@ -0,0 +1,92 @@
+<svg version="1.1" baseProfile="tiny" id="svg-root"
+ width="100%" height="100%" viewBox="0 0 480 360"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!--======================================================================-->
+ <!--= SVG 1.1 2nd Edition Test Case =-->
+ <!--======================================================================-->
+ <!--= Copyright 2009 World Wide Web Consortium, (Massachusetts =-->
+ <!--= Institute of Technology, European Research Consortium for =-->
+ <!--= Informatics and Mathematics (ERCIM), Keio University). =-->
+ <!--= All Rights Reserved. =-->
+ <!--= See http://www.w3.org/Consortium/Legal/. =-->
+ <!--======================================================================-->
+ <d:SVGTestCase xmlns:d="http://www.w3.org/2000/02/svg/testsuite/description/"
+ template-version="1.4" reviewer="SVGWG" author="Chris lilley" status="accepted"
+ version="$Revision: 1.5 $" testname="$RCSfile: text-fonts-02-t.svg,v $">
+ <d:testDescription xmlns="http://www.w3.org/1999/xhtml"
href="http://www.w3.org/TR/SVG11/text.html#FontPropertiesUsedBySVG">
+ <p>
+ Purpose of test is to determine if the font weight is being
+ correctly rendered. A number of font families are specified. The
+ numerical weight values (100 to 900) should show the lighter weights
+ on the lower numbers and the heavier weights on the larger numbers.
+ Heavier is defined to mean 'no lighter'.
+ </p>
+ <p>
+ If only one font weight is available, they should all display at the
+ same weight. The transition from black to green figures shows the
+ correct light to bold transition for the common case where two
+ weights are available. If three or more weights are available, see
+ the CSS2 specification for how these are allocated to the nine
+ weight numbers.
+ </p>
+ <p>
+ The absolute keywords 'normal' and bold' are tested
+ by the first two lines on the right hand side of the test,
+ the third line of text tests the to 'bolder'
+ relative keyword and the fourth tests the
+ 'lighter' relative keyword.
+ </p>
+ </d:testDescription>
+ <d:operatorScript xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ Run the test. No interaction required.
+ </p>
+ </d:operatorScript>
+ <d:passCriteria xmlns="http://www.w3.org/1999/xhtml">
+ <p>
+ The numerical weight values (100 to 900) should show the lighter weights on the
+ lower numbers and the heavier weights on the larger numbers. Heavier is defined
+ to mean 'no lighter'.
+ </p>
+ </d:passCriteria>
+ </d:SVGTestCase>
+ <title id="test-title">$RCSfile: text-fonts-02-t.svg,v $</title>
+ <defs>
+ <font-face font-family="SVGFreeSansASCII" unicode-range="U+0-7F">
+ <font-face-src>
+ <font-face-uri xlink:href="../resources/SVGFreeSans.svg#ascii"/>
+ </font-face-src>
+ </font-face>
+ </defs>
+ <g id="test-body-content" font-family="SVGFreeSansASCII,sans-serif" font-size="18">
+ <g font-family="Georgia,'Times New Roman',Times,'MS Mincho',serif" font-size="30">
+ <text font-weight="100" x="360" y="50">100</text>
+ <text font-weight="200" x="360" y="85">200</text>
+ <text font-weight="300" x="360" y="120">300</text>
+ <text font-weight="400" x="360" y="155">400</text>
+ <text font-weight="500" x="360" y="190">500</text>
+ <text fill="green" font-weight="600" x="360" y="225">600</text>
+ <text fill="green" font-weight="700" x="360" y="260">700</text>
+ <text fill="green" font-weight="800" x="360" y="295">800</text>
+ <text fill="green" font-weight="900" x="360" y="330">900</text>
+ <text font-weight="bold" x="60" y="80">This is bold</text>
+ <text font-weight="normal" x="60" y="130">This is normal</text>
+ <g font-weight="normal" fill="blue">
+ <text font-weight="bolder" x="60" y="180">Blue is bolder</text>
+ </g>
+ <g font-weight="bold" fill="blue">
+ <text font-weight="lighter" x="60" y="230">Blue is lighter</text>
+ </g>
+ </g>
+ </g>
+ <g font-family="SVGFreeSansASCII,sans-serif" font-size="32">
+ <text id="revision" x="10" y="340" stroke="none" fill="black">$Revision: 1.5 $</text>
+ </g>
+ <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/>
+ <!-- comment out this watermark once the test is approved -->
+ <!--<g id="draft-watermark">
+ <rect x="1" y="1" width="478" height="20" fill="red" stroke="black" stroke-width="1"/>
+ <text font-family="SVGFreeSansASCII,sans-serif" font-weight="bold" font-size="20" x="240"
+ text-anchor="middle" y="18" stroke-width="0.5" stroke="black" fill="white">DRAFT</text>
+ </g>-->
+</svg>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]