Bug 1892089: Support font-relative line-height units use in canvas via SVG filter. r=jfkthame
Differential Revision: https://phabricator.services.mozilla.com/D207871
This commit is contained in:
@@ -972,6 +972,7 @@ CanvasRenderingContext2D::ContextState::ContextState(const ContextState& aOther)
|
||||
textRendering(aOther.textRendering),
|
||||
letterSpacing(aOther.letterSpacing),
|
||||
wordSpacing(aOther.wordSpacing),
|
||||
fontLineHeight(aOther.fontLineHeight),
|
||||
letterSpacingStr(aOther.letterSpacingStr),
|
||||
wordSpacingStr(aOther.wordSpacingStr),
|
||||
shadowColor(aOther.shadowColor),
|
||||
@@ -2884,10 +2885,16 @@ void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
|
||||
class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
|
||||
public:
|
||||
CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
|
||||
const StyleLineHeight& aLineHeight,
|
||||
RefPtr<nsAtom> aFontLanguage,
|
||||
bool aFontExplicitLanguage,
|
||||
const ComputedStyle* aCanvasStyle,
|
||||
nsPresContext* aPresContext)
|
||||
: mSize(aSize),
|
||||
mFont(aFont),
|
||||
mLineHeight(aLineHeight),
|
||||
mFontLanguage(std::move(aFontLanguage)),
|
||||
mFontExplicitLanguage(aFontExplicitLanguage),
|
||||
mCanvasStyle(aCanvasStyle),
|
||||
mPresContext(aPresContext) {}
|
||||
|
||||
@@ -2912,7 +2919,20 @@ class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
|
||||
float GetLineHeight(Type aType) const override {
|
||||
// This is used if a filter is added through `url()`, and if the SVG
|
||||
// filter being referred to is using line-height units.
|
||||
// TODO(dshin): Implement
|
||||
switch (aType) {
|
||||
case Type::This: {
|
||||
const auto wm = GetWritingModeForType(aType);
|
||||
const auto lh = ReflowInput::CalcLineHeightForCanvas(
|
||||
mLineHeight, mFont, mFontLanguage, mFontExplicitLanguage,
|
||||
mPresContext, wm);
|
||||
return nsPresContext::AppUnitsToFloatCSSPixels(lh);
|
||||
}
|
||||
case Type::Root: {
|
||||
return SVGContentUtils::GetLineHeight(
|
||||
mPresContext->Document()->GetRootElement());
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
@@ -2951,6 +2971,9 @@ class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
|
||||
|
||||
gfx::IntSize mSize;
|
||||
const nsFont& mFont;
|
||||
StyleLineHeight mLineHeight;
|
||||
RefPtr<nsAtom> mFontLanguage;
|
||||
bool mFontExplicitLanguage;
|
||||
RefPtr<const ComputedStyle> mCanvasStyle;
|
||||
nsPresContext* mPresContext;
|
||||
};
|
||||
@@ -3006,8 +3029,10 @@ void CanvasRenderingContext2D::UpdateFilter(bool aFlushIfNeeded) {
|
||||
CurrentState().filter = FilterInstance::GetFilterDescription(
|
||||
mCanvasElement, CurrentState().filterChain.AsSpan(),
|
||||
CurrentState().autoSVGFiltersObserver, writeOnly,
|
||||
CanvasUserSpaceMetrics(GetSize(), CurrentState().fontFont, canvasStyle,
|
||||
presContext),
|
||||
CanvasUserSpaceMetrics(
|
||||
GetSize(), CurrentState().fontFont, CurrentState().fontLineHeight,
|
||||
CurrentState().fontLanguage, CurrentState().fontExplicitLanguage,
|
||||
canvasStyle, presContext),
|
||||
gfxRect(0, 0, mWidth, mHeight), CurrentState().filterAdditionalImages);
|
||||
CurrentState().filterSourceGraphicTainted = writeOnly;
|
||||
}
|
||||
@@ -4048,6 +4073,7 @@ bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
|
||||
CurrentState().fontFont.size = fontStyle->mSize;
|
||||
CurrentState().fontLanguage = fontStyle->mLanguage;
|
||||
CurrentState().fontExplicitLanguage = fontStyle->mExplicitLanguage;
|
||||
CurrentState().fontLineHeight = data.mStyle->StyleFont()->mLineHeight;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -4256,6 +4282,8 @@ bool CanvasRenderingContext2D::SetFontInternalDisconnected(
|
||||
CurrentState().fontFont.variantCaps = fontStyle.variantCaps;
|
||||
CurrentState().fontLanguage = nullptr;
|
||||
CurrentState().fontExplicitLanguage = false;
|
||||
// We don't have any computed style, assume normal height.
|
||||
CurrentState().fontLineHeight = StyleLineHeight::Normal();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1051,6 +1051,8 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
|
||||
|
||||
gfx::Float letterSpacing = 0.0f;
|
||||
gfx::Float wordSpacing = 0.0f;
|
||||
mozilla::StyleLineHeight fontLineHeight =
|
||||
mozilla::StyleLineHeight::Normal();
|
||||
nsCString letterSpacingStr;
|
||||
nsCString wordSpacingStr;
|
||||
|
||||
|
||||
@@ -2744,14 +2744,15 @@ static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) {
|
||||
}
|
||||
|
||||
static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
|
||||
const nsStyleFont& aRelativeToFont,
|
||||
const nsFont& aFont, nsAtom* aLanguage,
|
||||
bool aExplicitLanguage,
|
||||
nsPresContext* aPresContext,
|
||||
bool aIsVertical, nscoord aBlockBSize,
|
||||
float aFontSizeInflation) {
|
||||
if (aLh.IsLength()) {
|
||||
nscoord result = aLh.AsLength().ToAppUnits();
|
||||
if (aFontSizeInflation != 1.0f) {
|
||||
result = NSToCoordRound(result * aFontSizeInflation);
|
||||
result = NSToCoordRound(static_cast<float>(result) * aFontSizeInflation);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2760,8 +2761,7 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
|
||||
// For factor units the computed value of the line-height property
|
||||
// is found by multiplying the factor by the font's computed size
|
||||
// (adjusted for min-size prefs and text zoom).
|
||||
return aRelativeToFont.mFont.size
|
||||
.ScaledBy(aLh.AsNumber() * aFontSizeInflation)
|
||||
return aFont.size.ScaledBy(aLh.AsNumber() * aFontSizeInflation)
|
||||
.ToAppUnits();
|
||||
}
|
||||
|
||||
@@ -2770,13 +2770,21 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
|
||||
return aBlockBSize;
|
||||
}
|
||||
|
||||
auto size = aRelativeToFont.mFont.size;
|
||||
auto size = aFont.size;
|
||||
size.ScaleBy(aFontSizeInflation);
|
||||
|
||||
if (aPresContext) {
|
||||
RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor(
|
||||
aPresContext, aIsVertical, &aRelativeToFont, size,
|
||||
/* aUseUserFontSet = */ true);
|
||||
nsFont font = aFont;
|
||||
font.size = size;
|
||||
nsFontMetrics::Params params;
|
||||
params.language = aLanguage;
|
||||
params.explicitLanguage = aExplicitLanguage;
|
||||
params.orientation =
|
||||
aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal;
|
||||
params.userFontSet = aPresContext->GetUserFontSet();
|
||||
params.textPerf = aPresContext->GetTextPerfMetrics();
|
||||
params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
|
||||
RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params);
|
||||
return GetNormalLineHeight(fm);
|
||||
}
|
||||
// If we don't have a pres context, use a 1.2em fallback.
|
||||
@@ -2828,8 +2836,9 @@ nscoord ReflowInput::CalcLineHeight(
|
||||
nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent,
|
||||
nscoord aBlockBSize, float aFontSizeInflation) {
|
||||
nscoord lineHeight =
|
||||
ComputeLineHeight(aLh, aRelativeToFont, aPresContext, aIsVertical,
|
||||
aBlockBSize, aFontSizeInflation);
|
||||
ComputeLineHeight(aLh, aRelativeToFont.mFont, aRelativeToFont.mLanguage,
|
||||
aRelativeToFont.mExplicitLanguage, aPresContext,
|
||||
aIsVertical, aBlockBSize, aFontSizeInflation);
|
||||
|
||||
NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
|
||||
|
||||
@@ -2839,8 +2848,9 @@ nscoord ReflowInput::CalcLineHeight(
|
||||
// have a line-height smaller than 'normal'.
|
||||
if (!aLh.IsNormal()) {
|
||||
nscoord normal = ComputeLineHeight(
|
||||
StyleLineHeight::Normal(), aRelativeToFont, aPresContext, aIsVertical,
|
||||
aBlockBSize, aFontSizeInflation);
|
||||
StyleLineHeight::Normal(), aRelativeToFont.mFont,
|
||||
aRelativeToFont.mLanguage, aRelativeToFont.mExplicitLanguage,
|
||||
aPresContext, aIsVertical, aBlockBSize, aFontSizeInflation);
|
||||
if (lineHeight < normal) {
|
||||
lineHeight = normal;
|
||||
}
|
||||
@@ -2850,6 +2860,17 @@ nscoord ReflowInput::CalcLineHeight(
|
||||
return lineHeight;
|
||||
}
|
||||
|
||||
nscoord ReflowInput::CalcLineHeightForCanvas(const StyleLineHeight& aLh,
|
||||
const nsFont& aRelativeToFont,
|
||||
nsAtom* aLanguage,
|
||||
bool aExplicitLanguage,
|
||||
nsPresContext* aPresContext,
|
||||
mozilla::WritingMode aWM) {
|
||||
return ComputeLineHeight(aLh, aRelativeToFont, aLanguage, aExplicitLanguage,
|
||||
aPresContext, aWM.IsVertical() && !aWM.IsSideways(),
|
||||
NS_UNCONSTRAINEDSIZE, 1.0f);
|
||||
}
|
||||
|
||||
bool SizeComputationInput::ComputeMargin(WritingMode aCBWM,
|
||||
nscoord aPercentBasis,
|
||||
LayoutFrameType aFrameType) {
|
||||
|
||||
@@ -741,6 +741,13 @@ struct ReflowInput : public SizeComputationInput {
|
||||
const nsIContent* aContent, nscoord aBlockBSize,
|
||||
float aFontSizeInflation);
|
||||
|
||||
static nscoord CalcLineHeightForCanvas(const StyleLineHeight& aLh,
|
||||
const nsFont& aRelativeToFont,
|
||||
nsAtom* aLanguage,
|
||||
bool aExplicitLanguage,
|
||||
nsPresContext* aPresContext,
|
||||
mozilla::WritingMode aWM);
|
||||
|
||||
static constexpr float kNormalLineHeightFactor = 1.2f;
|
||||
|
||||
mozilla::LogicalSize ComputeContainingBlockRectangle(
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="test-wait">
|
||||
<title>HTML Canvas reference: SVG filter using CSS font-relative line-height units</title>
|
||||
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
|
||||
<style>
|
||||
:root {
|
||||
font: 20px Ahem;
|
||||
}
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.r {
|
||||
background: red;
|
||||
}
|
||||
</style>
|
||||
<div><div class="r" style="width: 48px;"></div></div><br>
|
||||
<div><div class="r" style="width: 24px;"></div></div>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="test-wait">
|
||||
<title>HTML Canvas test: SVG filter using CSS font-relative line-height units</title>
|
||||
<link rel="match" href="svg-filter-lh-rlh-expected.html"/>
|
||||
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
|
||||
<style>
|
||||
:root {
|
||||
font: 20px Ahem;
|
||||
}
|
||||
</style>
|
||||
<svg width="0" height="0">
|
||||
<!-- https://html.spec.whatwg.org/multipage/canvas.html#working-with-externally-defined-svg-filters -->
|
||||
<use href="./svg-filter.svg#filter-lh" />
|
||||
<use href="./svg-filter.svg#filter-rlh" />
|
||||
</svg
|
||||
><canvas id="canvas-lh" width="100" height="100"></canvas><br
|
||||
><canvas id="canvas-rlh" width="100" height="100"></canvas>
|
||||
<script>
|
||||
function use_filter(canvas, filter_id) {
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.font = "40px Ahem";
|
||||
ctx.filter = `url(./svg-filter.svg#${filter_id})`;
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fillRect(0, 0, 100, 100);
|
||||
}
|
||||
window.addEventListener("load", async () => {
|
||||
use_filter(document.getElementById("canvas-lh"), "filter-lh");
|
||||
use_filter(document.getElementById("canvas-rlh"), "filter-rlh");
|
||||
document.documentElement.classList.remove('test-wait');
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<filter id="filter-lh">
|
||||
<feFlood result="floodFill" x="1lh" y="0" width="100" height="100" flood-color="green" />
|
||||
<feBlend in="SourceGraphic" in2="floodFill" mode="color-dodge" />
|
||||
</filter>
|
||||
<filter id="filter-rlh">
|
||||
<feFlood result="floodFill" x="1rlh" y="0" width="100" height="100" flood-color="green" />
|
||||
<feBlend in="SourceGraphic" in2="floodFill" mode="color-dodge" />
|
||||
</filter>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 479 B |
Reference in New Issue
Block a user