Skip to content

Commit

Permalink
Bug 1832955 - Support font-relative units when parsing letterSpacing/…
Browse files Browse the repository at this point in the history
…wordSpacing for offscreen canvas. r=gfx-reviewers,lsalzman,emilio

Differential Revision: https://phabricator.services.mozilla.com/D184938
  • Loading branch information
jfkthame committed Aug 1, 2023
1 parent 13b3e78 commit 99602c8
Show file tree
Hide file tree
Showing 19 changed files with 137 additions and 37 deletions.
30 changes: 29 additions & 1 deletion dom/canvas/CanvasRenderingContext2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
#include "mozilla/layers/WebRenderUserData.h"
#include "mozilla/layers/WebRenderCanvasRenderer.h"
#include "WindowRenderer.h"
#include "GeckoBindings.h"

#undef free // apparently defined by some windows header, clashing with a
// free() method in SkTypes.h
Expand Down Expand Up @@ -2738,6 +2739,32 @@ void CanvasRenderingContext2D::SetWordSpacing(const nsACString& aWordSpacing) {
CurrentState().wordSpacingStr);
}

static GeckoFontMetrics GetFontMetricsFromCanvas(void* aContext) {
auto* ctx = static_cast<CanvasRenderingContext2D*>(aContext);
auto* fontGroup = ctx->GetCurrentFontStyle();
if (!fontGroup) {
// Shouldn't happen, but just in case... return plausible values for a
// 10px font (canvas default size).
return {Length::FromPixels(5.0),
Length::FromPixels(5.0),
Length::FromPixels(8.0),
Length::FromPixels(10.0),
Length::FromPixels(8.0),
Length::FromPixels(10.0),
0.0f,
0.0f};
}
auto metrics = fontGroup->GetMetricsForCSSUnits(nsFontMetrics::eHorizontal);
return {Length::FromPixels(metrics.xHeight),
Length::FromPixels(metrics.zeroWidth),
Length::FromPixels(metrics.capHeight),
Length::FromPixels(metrics.ideographicWidth),
Length::FromPixels(metrics.maxAscent),
Length::FromPixels(fontGroup->GetStyle()->size),
0.0f,
0.0f};
}

void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
float* aValue,
nsACString& aNormalized) {
Expand All @@ -2751,7 +2778,8 @@ void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
return;
}
float value;
if (!Servo_ParseAbsoluteLength(&normalized, &value)) {
if (!Servo_ParseLengthWithoutStyleContext(&normalized, &value,
GetFontMetricsFromCanvas, this)) {
if (!GetPresShell()) {
return;
}
Expand Down
5 changes: 3 additions & 2 deletions dom/canvas/CanvasRenderingContext2D.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,11 +926,12 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,

// text

public:
gfxFontGroup* GetCurrentFontStyle();

protected:
enum class TextDrawOperation : uint8_t { FILL, STROKE, MEASURE };

gfxFontGroup* GetCurrentFontStyle();

/**
* Implementation of the fillText, strokeText, and measure functions with
* the operation abstracted to a flag.
Expand Down
1 change: 1 addition & 0 deletions layout/style/GeckoBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext,
ToLength(NS_round(metrics.capHeight * d2a)),
ToLength(NS_round(metrics.ideographicWidth * d2a)),
ToLength(NS_round(metrics.maxAscent * d2a)),
ToLength(NS_round(fontGroup->GetStyle()->size * d2a)),
scriptPercentScaleDown,
scriptScriptPercentScaleDown};
}
Expand Down
1 change: 1 addition & 0 deletions layout/style/GeckoBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ struct GeckoFontMetrics {
mozilla::Length mCapHeight; // negatives indicate not found.
mozilla::Length mIcWidth; // negatives indicate not found.
mozilla::Length mAscent;
mozilla::Length mComputedEmSize;
float mScriptPercentScaleDown; // zero is invalid or means not found.
float mScriptScriptPercentScaleDown; // zero is invalid or means not found.
};
Expand Down
1 change: 1 addition & 0 deletions layout/style/ServoStyleConstsForwards.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct nsCSSValueSharedList;

class gfxFontFeatureValueSet;
struct gfxFontFeature;
struct GeckoFontMetrics;
namespace mozilla {
namespace gfx {
struct FontVariation;
Expand Down
23 changes: 23 additions & 0 deletions servo/components/style/values/computed/length_percentage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
//! our expectations.
use super::{Context, Length, Percentage, ToComputedValue};
use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::calc::CalcUnits;
Expand Down Expand Up @@ -885,6 +886,28 @@ impl specified::CalcLengthPercentage {
}
}

/// Compute the value into pixel length as CSSFloat, using the get_font_metrics function
/// if provided to resolve font-relative dimensions.
pub fn to_computed_pixel_length_with_font_metrics(
&self,
get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
) -> Result<CSSFloat, ()> {
use crate::values::specified::calc::Leaf;
use crate::values::specified::length::NoCalcLength;

match self.node {
calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::FontRelative(ref l))) => {
if let Some(getter) = get_font_metrics {
l.to_computed_pixel_length_with_font_metrics(getter)
} else {
Err(())
}
},
_ => Err(()),
}
}

/// Compute the calc using the current font-size (and without text-zoom).
pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {
self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
Expand Down
55 changes: 53 additions & 2 deletions servo/components/style/values/specified/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use super::{AllowQuirks, Number, Percentage, ToComputedValue};
use crate::computed_value_flags::ComputedValueFlags;
use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
use crate::gecko_bindings::structs::GeckoFontMetrics;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{self, CSSPixelLength, Context};
use crate::values::generics::length as generics;
Expand Down Expand Up @@ -157,6 +158,23 @@ impl FontRelativeLength {
(reference_size * length).finite()
}

/// Computes the length, given a GeckoFontMetrics getter to resolve font-relative units.
pub fn to_computed_pixel_length_with_font_metrics(
&self,
get_font_metrics: impl Fn() -> GeckoFontMetrics,
) -> Result<CSSFloat, ()> {
let metrics = get_font_metrics();
Ok(match *self {
Self::Em(v) => v * metrics.mComputedEmSize.px(),
Self::Ex(v) => v * metrics.mXSize.px(),
Self::Ch(v) => v * metrics.mChSize.px(),
Self::Cap(v) => v * metrics.mCapHeight.px(),
Self::Ic(v) => v * metrics.mIcWidth.px(),
// 'rem' unsupported as we have no context for it
Self::Rem(_) => return Err(()),
})
}

/// Return reference font size.
///
/// We use the base_size flag to pass a different size for computing
Expand Down Expand Up @@ -788,7 +806,9 @@ impl ContainerRelativeLength {
if context.for_non_inherited_property {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
context.builder.add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
context
.builder
.add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);

let size = context.get_container_size_query();
let (factor, container_length) = match *self {
Expand Down Expand Up @@ -1080,7 +1100,7 @@ impl NoCalcLength {
}
}

/// Get a px value without context.
/// Get a px value without context (so only absolute units can be handled).
#[inline]
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
match *self {
Expand All @@ -1089,6 +1109,26 @@ impl NoCalcLength {
}
}

/// Get a px value without a full style context; this can handle either
/// absolute or (if a font metrics getter is provided) font-relative units.
#[inline]
pub fn to_computed_pixel_length_with_font_metrics(
&self,
get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
) -> Result<CSSFloat, ()> {
match *self {
Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
Self::FontRelative(fr) => {
if let Some(getter) = get_font_metrics {
fr.to_computed_pixel_length_with_font_metrics(getter)
} else {
Err(())
}
},
_ => Err(()),
}
}

/// Get an absolute length from a px value.
#[inline]
pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
Expand Down Expand Up @@ -1356,6 +1396,17 @@ impl Length {
Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
}
}

/// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.
pub fn to_computed_pixel_length_with_font_metrics(
&self,
get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,
) -> Result<CSSFloat, ()> {
match *self {
Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),
}
}
}

impl Parse for Length {
Expand Down
1 change: 1 addition & 0 deletions servo/ports/geckolib/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ renaming_overrides_prefixing = true
"nsresult" = "nsresult"
"gfxFontFeature" = "gfxFontFeature"
"gfxFontFeatureValueSet" = "gfxFontFeatureValueSet"
"GeckoFontMetrics" = "GeckoFontMetrics"
"SeenPtrs" = "SeenPtrs"
"gfxFontVariation" = "gfxFontVariation"
"URLExtraData" = "URLExtraData"
Expand Down
21 changes: 19 additions & 2 deletions servo/ports/geckolib/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
use style::gecko_bindings::structs;
use style::gecko_bindings::structs::GeckoFontMetrics;
use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
use style::gecko_bindings::structs::gfxFontFeatureValueSet;
use style::gecko_bindings::structs::ipc::ByteBuf;
Expand Down Expand Up @@ -7802,9 +7803,25 @@ where
}

#[no_mangle]
pub extern "C" fn Servo_ParseAbsoluteLength(len: &nsACString, out: &mut f32) -> bool {
// Parse a length without style context (for canvas2d letterSpacing/wordSpacing attributes).
// This accepts absolute lengths, and if a font-metrics-getter function is passed, also
// font-relative ones, but not other units (such as percentages, viewport-relative, etc)
// that would require a full style context to resolve.
pub extern "C" fn Servo_ParseLengthWithoutStyleContext(
len: &nsACString,
out: &mut f32,
get_font_metrics: Option<unsafe extern "C" fn(*mut c_void) -> GeckoFontMetrics>,
getter_context: *mut c_void
) -> bool {
let metrics_getter = if let Some(getter) = get_font_metrics {
Some(move || -> GeckoFontMetrics {
unsafe { getter(getter_context) }
})
} else {
None
};
let value = parse_no_context(unsafe { len.as_str_unchecked() }, specified::Length::parse)
.and_then(|p| p.to_computed_pixel_length_without_context());
.and_then(|p| p.to_computed_pixel_length_with_font_metrics(metrics_getter));
match value {
Ok(v) => {
*out = v;
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[2d.text.drawing.style.letterSpacing.change.font.html]
[Set letter spacing and word spacing to font dependent value and verify it works after font change.]
expected: FAIL
expected:
if (os == "linux"): PASS
FAIL
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[2d.text.drawing.style.letterSpacing.change.font.worker.html]
[Set letter spacing and word spacing to font dependent value and verify it works after font change.]
expected: FAIL
expected:
if (os == "linux"): PASS
FAIL

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 99602c8

Please sign in to comment.