Bug 1891710: part 2) Enable <Element-outerHTML.html> WPT for Trusted Types. r=smaug
[gecko.git] / gfx / src / nsFontMetrics.cpp
blob93fd1264b2838a4779381f4a3023c7ade27a8969
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsFontMetrics.h"
7 #include <math.h> // for floor, ceil
8 #include <algorithm> // for max
9 #include "gfxContext.h" // for gfxContext
10 #include "gfxFontConstants.h" // for NS_FONT_SYNTHESIS_*
11 #include "gfxPlatform.h" // for gfxPlatform
12 #include "gfxPoint.h" // for gfxPoint
13 #include "gfxRect.h" // for gfxRect
14 #include "gfxTextRun.h" // for gfxFontGroup
15 #include "gfxTypes.h" // for gfxFloat
16 #include "nsAtom.h" // for nsAtom
17 #include "nsBoundingMetrics.h" // for nsBoundingMetrics
18 #include "nsDebug.h" // for NS_ERROR
19 #include "nsDeviceContext.h" // for nsDeviceContext
20 #include "nsMathUtils.h" // for NS_round
21 #include "nsPresContext.h" // for nsPresContext
22 #include "nsString.h" // for nsString
23 #include "nsStyleConsts.h" // for StyleHyphens::None
24 #include "mozilla/Assertions.h" // for MOZ_ASSERT
25 #include "mozilla/UniquePtr.h" // for UniquePtr
27 class gfxUserFontSet;
28 using namespace mozilla;
30 namespace {
32 class AutoTextRun {
33 public:
34 typedef mozilla::gfx::DrawTarget DrawTarget;
36 AutoTextRun(const nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
37 const char* aString, uint32_t aLength) {
38 mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
39 reinterpret_cast<const uint8_t*>(aString), aLength, aDrawTarget,
40 aMetrics->AppUnitsPerDevPixel(), ComputeFlags(aMetrics),
41 nsTextFrameUtils::Flags(), nullptr);
44 AutoTextRun(const nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
45 const char16_t* aString, uint32_t aLength) {
46 mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
47 aString, aLength, aDrawTarget, aMetrics->AppUnitsPerDevPixel(),
48 ComputeFlags(aMetrics), nsTextFrameUtils::Flags(), nullptr);
51 gfxTextRun* get() const { return mTextRun.get(); }
52 gfxTextRun* operator->() const { return mTextRun.get(); }
54 private:
55 static gfx::ShapedTextFlags ComputeFlags(const nsFontMetrics* aMetrics) {
56 gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
57 if (aMetrics->GetTextRunRTL()) {
58 flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
60 if (aMetrics->GetVertical()) {
61 switch (aMetrics->GetTextOrientation()) {
62 case StyleTextOrientation::Mixed:
63 flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
64 break;
65 case StyleTextOrientation::Upright:
66 flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
67 break;
68 case StyleTextOrientation::Sideways:
69 flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
70 break;
73 return flags;
76 RefPtr<gfxTextRun> mTextRun;
79 class StubPropertyProvider final : public gfxTextRun::PropertyProvider {
80 public:
81 void GetHyphenationBreaks(
82 gfxTextRun::Range aRange,
83 gfxTextRun::HyphenType* aBreakBefore) const override {
84 NS_ERROR(
85 "This shouldn't be called because we never call BreakAndMeasureText");
87 mozilla::StyleHyphens GetHyphensOption() const override {
88 NS_ERROR(
89 "This shouldn't be called because we never call BreakAndMeasureText");
90 return mozilla::StyleHyphens::None;
92 gfxFloat GetHyphenWidth() const override {
93 NS_ERROR("This shouldn't be called because we never enable hyphens");
94 return 0;
96 already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() const override {
97 NS_ERROR("This shouldn't be called because we never enable hyphens");
98 return nullptr;
100 uint32_t GetAppUnitsPerDevUnit() const override {
101 NS_ERROR("This shouldn't be called because we never enable hyphens");
102 return 60;
104 void GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) const override {
105 NS_ERROR("This shouldn't be called because we never enable spacing");
107 gfx::ShapedTextFlags GetShapedTextFlags() const override {
108 NS_ERROR("This shouldn't be called because we never enable hyphens");
109 return gfx::ShapedTextFlags();
113 } // namespace
115 nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
116 nsPresContext* aContext)
117 : mFont(aFont),
118 mLanguage(aParams.language),
119 mPresContext(aContext),
120 mP2A(aContext->DeviceContext()->AppUnitsPerDevPixel()),
121 mOrientation(aParams.orientation),
122 mExplicitLanguage(aParams.explicitLanguage),
123 mTextRunRTL(false),
124 mVertical(false),
125 mTextOrientation(mozilla::StyleTextOrientation::Mixed) {
126 gfxFontStyle style(aFont.style, aFont.weight, aFont.stretch,
127 gfxFloat(aFont.size.ToAppUnits()) / mP2A, aFont.sizeAdjust,
128 aFont.family.is_system_font,
129 aContext->DeviceContext()->IsPrinterContext(),
130 aFont.synthesisWeight == StyleFontSynthesis::Auto,
131 aFont.synthesisStyle == StyleFontSynthesis::Auto,
132 aFont.synthesisSmallCaps == StyleFontSynthesis::Auto,
133 aFont.synthesisPosition == StyleFontSynthesis::Auto,
134 aFont.languageOverride);
136 aFont.AddFontFeaturesToStyle(&style, mOrientation == eVertical);
137 style.featureValueLookup = aParams.featureValueLookup;
139 aFont.AddFontVariationsToStyle(&style);
141 gfxFloat devToCssSize = gfxFloat(mP2A) / gfxFloat(AppUnitsPerCSSPixel());
142 mFontGroup = new gfxFontGroup(
143 mPresContext, aFont.family.families, &style, mLanguage, mExplicitLanguage,
144 aParams.textPerf, aParams.userFontSet, devToCssSize, aFont.variantEmoji);
147 nsFontMetrics::~nsFontMetrics() {
148 // Should not be dropped by stylo
149 MOZ_ASSERT(NS_IsMainThread());
150 if (mPresContext) {
151 mPresContext->FontMetricsDeleted(this);
155 void nsFontMetrics::Destroy() { mPresContext = nullptr; }
157 // XXXTODO get rid of this macro
158 #define ROUND_TO_TWIPS(x) (nscoord) floor(((x) * mP2A) + 0.5)
159 #define CEIL_TO_TWIPS(x) (nscoord) ceil((x) * mP2A)
161 static const gfxFont::Metrics& GetMetrics(
162 const nsFontMetrics* aFontMetrics,
163 nsFontMetrics::FontOrientation aOrientation) {
164 RefPtr<gfxFont> font =
165 aFontMetrics->GetThebesFontGroup()->GetFirstValidFont();
166 return font->GetMetrics(aOrientation);
169 static const gfxFont::Metrics& GetMetrics(const nsFontMetrics* aFontMetrics) {
170 return GetMetrics(aFontMetrics, aFontMetrics->Orientation());
173 nscoord nsFontMetrics::XHeight() const {
174 return ROUND_TO_TWIPS(GetMetrics(this).xHeight);
177 nscoord nsFontMetrics::CapHeight() const {
178 return ROUND_TO_TWIPS(GetMetrics(this).capHeight);
181 nscoord nsFontMetrics::SuperscriptOffset() const {
182 return ROUND_TO_TWIPS(GetMetrics(this).emHeight *
183 NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
186 nscoord nsFontMetrics::SubscriptOffset() const {
187 return ROUND_TO_TWIPS(GetMetrics(this).emHeight *
188 NS_FONT_SUBSCRIPT_OFFSET_RATIO);
191 void nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize) const {
192 aOffset = ROUND_TO_TWIPS(GetMetrics(this).strikeoutOffset);
193 aSize = ROUND_TO_TWIPS(GetMetrics(this).strikeoutSize);
196 void nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) const {
197 aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
198 aSize = ROUND_TO_TWIPS(GetMetrics(this).underlineSize);
201 // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
202 // text-decoration lines drawable area. See bug 421353.
203 // BE CAREFUL for rounding each values. The logic MUST be same as
204 // nsCSSRendering::GetTextDecorationRectInternal's.
206 static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
207 gfxFontGroup* aFontGroup) {
208 gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
209 gfxFloat size = NS_round(aMetrics.underlineSize);
210 gfxFloat minDescent = offset + size;
211 return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
214 static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics) {
215 return floor(aMetrics.maxAscent + 0.5);
218 nscoord nsFontMetrics::InternalLeading() const {
219 return ROUND_TO_TWIPS(GetMetrics(this).internalLeading);
222 nscoord nsFontMetrics::ExternalLeading() const {
223 return ROUND_TO_TWIPS(GetMetrics(this).externalLeading);
226 nscoord nsFontMetrics::EmHeight() const {
227 return ROUND_TO_TWIPS(GetMetrics(this).emHeight);
230 nscoord nsFontMetrics::EmAscent() const {
231 return ROUND_TO_TWIPS(GetMetrics(this).emAscent);
234 nscoord nsFontMetrics::EmDescent() const {
235 return ROUND_TO_TWIPS(GetMetrics(this).emDescent);
238 nscoord nsFontMetrics::MaxHeight() const {
239 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics(this))) +
240 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(this), mFontGroup));
243 nscoord nsFontMetrics::MaxAscent() const {
244 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics(this)));
247 nscoord nsFontMetrics::MaxDescent() const {
248 return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(this), mFontGroup));
251 nscoord nsFontMetrics::MaxAdvance() const {
252 return CEIL_TO_TWIPS(GetMetrics(this).maxAdvance);
255 nscoord nsFontMetrics::AveCharWidth() const {
256 // Use CEIL instead of ROUND for consistency with GetMaxAdvance
257 return CEIL_TO_TWIPS(GetMetrics(this).aveCharWidth);
260 nscoord nsFontMetrics::ZeroOrAveCharWidth() const {
261 return CEIL_TO_TWIPS(GetMetrics(this).ZeroOrAveCharWidth());
264 nscoord nsFontMetrics::SpaceWidth() const {
265 // For vertical text with mixed or sideways orientation, we want the
266 // width of a horizontal space (even if we're using vertical line-spacing
267 // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
268 return CEIL_TO_TWIPS(
269 GetMetrics(this,
270 mVertical && mTextOrientation == StyleTextOrientation::Upright
271 ? eVertical
272 : eHorizontal)
273 .spaceWidth);
276 int32_t nsFontMetrics::GetMaxStringLength() const {
277 const double x = 32767.0 / std::max(1.0, GetMetrics(this).maxAdvance);
278 int32_t len = (int32_t)floor(x);
279 return std::max(1, len);
282 nscoord nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
283 DrawTarget* aDrawTarget) const {
284 if (aLength == 0) {
285 return 0;
287 if (aLength == 1 && aString[0] == ' ') {
288 return SpaceWidth();
290 StubPropertyProvider provider;
291 AutoTextRun textRun(this, aDrawTarget, aString, aLength);
292 if (textRun.get()) {
293 return NSToCoordRound(
294 textRun->GetAdvanceWidth(gfxTextRun::Range(0, aLength), &provider));
296 return 0;
299 nscoord nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
300 DrawTarget* aDrawTarget) const {
301 if (aLength == 0) {
302 return 0;
304 if (aLength == 1 && aString[0] == ' ') {
305 return SpaceWidth();
307 StubPropertyProvider provider;
308 AutoTextRun textRun(this, aDrawTarget, aString, aLength);
309 if (textRun.get()) {
310 return NSToCoordRound(
311 textRun->GetAdvanceWidth(gfxTextRun::Range(0, aLength), &provider));
313 return 0;
316 // Draw a string using this font handle on the surface passed in.
317 void nsFontMetrics::DrawString(const char* aString, uint32_t aLength,
318 nscoord aX, nscoord aY,
319 gfxContext* aContext) const {
320 if (aLength == 0) {
321 return;
323 StubPropertyProvider provider;
324 AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
325 if (!textRun.get()) {
326 return;
328 gfx::Point pt(aX, aY);
329 gfxTextRun::Range range(0, aLength);
330 if (mTextRunRTL) {
331 if (mVertical) {
332 pt.y += textRun->GetAdvanceWidth(range, &provider);
333 } else {
334 pt.x += textRun->GetAdvanceWidth(range, &provider);
337 mozilla::gfx::PaletteCache paletteCache;
338 gfxTextRun::DrawParams params(aContext, paletteCache);
339 params.provider = &provider;
340 textRun->Draw(range, pt, params);
343 void nsFontMetrics::DrawString(
344 const char16_t* aString, uint32_t aLength, nscoord aX, nscoord aY,
345 gfxContext* aContext, DrawTarget* aTextRunConstructionDrawTarget) const {
346 if (aLength == 0) {
347 return;
349 StubPropertyProvider provider;
350 AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
351 if (!textRun.get()) {
352 return;
354 gfx::Point pt(aX, aY);
355 gfxTextRun::Range range(0, aLength);
356 if (mTextRunRTL) {
357 if (mVertical) {
358 pt.y += textRun->GetAdvanceWidth(range, &provider);
359 } else {
360 pt.x += textRun->GetAdvanceWidth(range, &provider);
363 mozilla::gfx::PaletteCache paletteCache;
364 gfxTextRun::DrawParams params(aContext, paletteCache);
365 params.provider = &provider;
366 textRun->Draw(range, pt, params);
369 static nsBoundingMetrics GetTextBoundingMetrics(
370 const nsFontMetrics* aMetrics, const char16_t* aString, uint32_t aLength,
371 mozilla::gfx::DrawTarget* aDrawTarget, gfxFont::BoundingBoxType aType) {
372 if (aLength == 0) {
373 return nsBoundingMetrics();
375 StubPropertyProvider provider;
376 AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
377 nsBoundingMetrics m;
378 if (textRun.get()) {
379 gfxTextRun::Metrics theMetrics = textRun->MeasureText(
380 gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
382 m.leftBearing = NSToCoordFloor(theMetrics.mBoundingBox.X());
383 m.rightBearing = NSToCoordCeil(theMetrics.mBoundingBox.XMost());
384 m.ascent = NSToCoordCeil(-theMetrics.mBoundingBox.Y());
385 m.descent = NSToCoordCeil(theMetrics.mBoundingBox.YMost());
386 m.width = NSToCoordRound(theMetrics.mAdvanceWidth);
388 return m;
391 nsBoundingMetrics nsFontMetrics::GetBoundingMetrics(
392 const char16_t* aString, uint32_t aLength, DrawTarget* aDrawTarget) const {
393 return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
394 gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
397 nsBoundingMetrics nsFontMetrics::GetInkBoundsForInkOverflow(
398 const char16_t* aString, uint32_t aLength, DrawTarget* aDrawTarget) const {
399 return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
400 gfxFont::LOOSE_INK_EXTENTS);
403 gfxUserFontSet* nsFontMetrics::GetUserFontSet() const {
404 return mFontGroup->GetUserFontSet();