1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "gfxFontConstants.h" // for NS_FONT_SYNTHESIS_*
10 #include "gfxPlatform.h" // for gfxPlatform
11 #include "gfxPoint.h" // for gfxPoint
12 #include "gfxRect.h" // for gfxRect
13 #include "gfxTypes.h" // for gfxFloat
14 #include "nsBoundingMetrics.h" // for nsBoundingMetrics
15 #include "nsDebug.h" // for NS_ERROR, NS_ABORT_IF_FALSE
16 #include "nsDeviceContext.h" // for nsDeviceContext
17 #include "nsIAtom.h" // for nsIAtom
18 #include "nsMathUtils.h" // for NS_round
19 #include "nsRenderingContext.h" // for nsRenderingContext
20 #include "nsString.h" // for nsString
21 #include "nsStyleConsts.h" // for NS_STYLE_HYPHENS_NONE
29 AutoTextRun(nsFontMetrics
* aMetrics
, nsRenderingContext
* aRC
,
30 const char* aString
, int32_t aLength
)
32 mTextRun
= aMetrics
->GetThebesFontGroup()->MakeTextRun(
33 reinterpret_cast<const uint8_t*>(aString
), aLength
,
35 aMetrics
->AppUnitsPerDevPixel(),
36 ComputeFlags(aMetrics
));
39 AutoTextRun(nsFontMetrics
* aMetrics
, nsRenderingContext
* aRC
,
40 const char16_t
* aString
, int32_t aLength
)
42 mTextRun
= aMetrics
->GetThebesFontGroup()->MakeTextRun(
45 aMetrics
->AppUnitsPerDevPixel(),
46 ComputeFlags(aMetrics
));
49 gfxTextRun
*get() { return mTextRun
; }
50 gfxTextRun
*operator->() { return mTextRun
; }
53 static uint32_t ComputeFlags(nsFontMetrics
* aMetrics
) {
55 if (aMetrics
->GetTextRunRTL()) {
56 flags
|= gfxTextRunFactory::TEXT_IS_RTL
;
61 nsAutoPtr
<gfxTextRun
> mTextRun
;
64 class StubPropertyProvider
: public gfxTextRun::PropertyProvider
{
66 virtual void GetHyphenationBreaks(uint32_t aStart
, uint32_t aLength
,
68 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
70 virtual int8_t GetHyphensOption() {
71 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
72 return NS_STYLE_HYPHENS_NONE
;
74 virtual gfxFloat
GetHyphenWidth() {
75 NS_ERROR("This shouldn't be called because we never enable hyphens");
78 virtual already_AddRefed
<gfxContext
> GetContext() {
79 NS_ERROR("This shouldn't be called because we never enable hyphens");
82 virtual uint32_t GetAppUnitsPerDevUnit() {
83 NS_ERROR("This shouldn't be called because we never enable hyphens");
86 virtual void GetSpacing(uint32_t aStart
, uint32_t aLength
,
88 NS_ERROR("This shouldn't be called because we never enable spacing");
94 nsFontMetrics::nsFontMetrics()
95 : mDeviceContext(nullptr), mP2A(0), mTextRunRTL(false)
99 nsFontMetrics::~nsFontMetrics()
102 mDeviceContext
->FontMetricsDeleted(this);
106 nsFontMetrics::Init(const nsFont
& aFont
, nsIAtom
* aLanguage
,
107 nsDeviceContext
*aContext
,
108 gfxUserFontSet
*aUserFontSet
,
109 gfxTextPerfMetrics
*aTextPerf
)
111 NS_ABORT_IF_FALSE(mP2A
== 0, "already initialized");
114 mLanguage
= aLanguage
;
115 mDeviceContext
= aContext
;
116 mP2A
= mDeviceContext
->AppUnitsPerDevPixel();
118 gfxFontStyle
style(aFont
.style
,
121 gfxFloat(aFont
.size
) / mP2A
,
125 mDeviceContext
->IsPrinterSurface(),
126 aFont
.synthesis
& NS_FONT_SYNTHESIS_WEIGHT
,
127 aFont
.synthesis
& NS_FONT_SYNTHESIS_STYLE
,
128 aFont
.languageOverride
);
130 aFont
.AddFontFeaturesToStyle(&style
);
132 mFontGroup
= gfxPlatform::GetPlatform()->
133 CreateFontGroup(aFont
.fontlist
, &style
, aUserFontSet
);
134 mFontGroup
->SetTextPerfMetrics(aTextPerf
);
135 if (mFontGroup
->FontListLength() < 1)
136 return NS_ERROR_UNEXPECTED
;
142 nsFontMetrics::Destroy()
144 mDeviceContext
= nullptr;
147 // XXXTODO get rid of this macro
148 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
149 #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
151 const gfxFont::Metrics
& nsFontMetrics::GetMetrics() const
153 return mFontGroup
->GetFontAt(0)->GetMetrics();
157 nsFontMetrics::XHeight()
159 return ROUND_TO_TWIPS(GetMetrics().xHeight
);
163 nsFontMetrics::SuperscriptOffset()
165 return ROUND_TO_TWIPS(GetMetrics().emHeight
*
166 NS_FONT_SUPERSCRIPT_OFFSET_RATIO
);
170 nsFontMetrics::SubscriptOffset()
172 return ROUND_TO_TWIPS(GetMetrics().emHeight
*
173 NS_FONT_SUBSCRIPT_OFFSET_RATIO
);
177 nsFontMetrics::GetStrikeout(nscoord
& aOffset
, nscoord
& aSize
)
179 aOffset
= ROUND_TO_TWIPS(GetMetrics().strikeoutOffset
);
180 aSize
= ROUND_TO_TWIPS(GetMetrics().strikeoutSize
);
184 nsFontMetrics::GetUnderline(nscoord
& aOffset
, nscoord
& aSize
)
186 aOffset
= ROUND_TO_TWIPS(mFontGroup
->GetUnderlineOffset());
187 aSize
= ROUND_TO_TWIPS(GetMetrics().underlineSize
);
190 // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
191 // text-decoration lines drawable area. See bug 421353.
192 // BE CAREFUL for rounding each values. The logic MUST be same as
193 // nsCSSRendering::GetTextDecorationRectInternal's.
195 static gfxFloat
ComputeMaxDescent(const gfxFont::Metrics
& aMetrics
,
196 gfxFontGroup
* aFontGroup
)
198 gfxFloat offset
= floor(-aFontGroup
->GetUnderlineOffset() + 0.5);
199 gfxFloat size
= NS_round(aMetrics
.underlineSize
);
200 gfxFloat minDescent
= floor(offset
+ size
+ 0.5);
201 return std::max(minDescent
, aMetrics
.maxDescent
);
204 static gfxFloat
ComputeMaxAscent(const gfxFont::Metrics
& aMetrics
)
206 return floor(aMetrics
.maxAscent
+ 0.5);
210 nsFontMetrics::InternalLeading()
212 return ROUND_TO_TWIPS(GetMetrics().internalLeading
);
216 nsFontMetrics::ExternalLeading()
218 return ROUND_TO_TWIPS(GetMetrics().externalLeading
);
222 nsFontMetrics::EmHeight()
224 return ROUND_TO_TWIPS(GetMetrics().emHeight
);
228 nsFontMetrics::EmAscent()
230 return ROUND_TO_TWIPS(GetMetrics().emAscent
);
234 nsFontMetrics::EmDescent()
236 return ROUND_TO_TWIPS(GetMetrics().emDescent
);
240 nsFontMetrics::MaxHeight()
242 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
243 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
247 nsFontMetrics::MaxAscent()
249 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
253 nsFontMetrics::MaxDescent()
255 return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
259 nsFontMetrics::MaxAdvance()
261 return CEIL_TO_TWIPS(GetMetrics().maxAdvance
);
265 nsFontMetrics::AveCharWidth()
267 // Use CEIL instead of ROUND for consistency with GetMaxAdvance
268 return CEIL_TO_TWIPS(GetMetrics().aveCharWidth
);
272 nsFontMetrics::SpaceWidth()
274 return CEIL_TO_TWIPS(GetMetrics().spaceWidth
);
278 nsFontMetrics::GetMaxStringLength()
280 const gfxFont::Metrics
& m
= GetMetrics();
281 const double x
= 32767.0 / m
.maxAdvance
;
282 int32_t len
= (int32_t)floor(x
);
283 return std::max(1, len
);
287 nsFontMetrics::GetWidth(const char* aString
, uint32_t aLength
,
288 nsRenderingContext
*aContext
)
293 if (aLength
== 1 && aString
[0] == ' ')
296 StubPropertyProvider provider
;
297 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
298 return textRun
.get() ?
299 NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
)) : 0;
303 nsFontMetrics::GetWidth(const char16_t
* aString
, uint32_t aLength
,
304 nsRenderingContext
*aContext
)
309 if (aLength
== 1 && aString
[0] == ' ')
312 StubPropertyProvider provider
;
313 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
314 return textRun
.get() ?
315 NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
)) : 0;
318 // Draw a string using this font handle on the surface passed in.
320 nsFontMetrics::DrawString(const char *aString
, uint32_t aLength
,
321 nscoord aX
, nscoord aY
,
322 nsRenderingContext
*aContext
)
327 StubPropertyProvider provider
;
328 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
329 if (!textRun
.get()) {
334 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
336 textRun
->Draw(aContext
->ThebesContext(), pt
, DrawMode::GLYPH_FILL
, 0, aLength
,
337 &provider
, nullptr, nullptr);
341 nsFontMetrics::DrawString(const char16_t
* aString
, uint32_t aLength
,
342 nscoord aX
, nscoord aY
,
343 nsRenderingContext
*aContext
,
344 nsRenderingContext
*aTextRunConstructionContext
)
349 StubPropertyProvider provider
;
350 AutoTextRun
textRun(this, aTextRunConstructionContext
, aString
, aLength
);
351 if (!textRun
.get()) {
356 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
358 textRun
->Draw(aContext
->ThebesContext(), pt
, DrawMode::GLYPH_FILL
, 0, aLength
,
359 &provider
, nullptr, nullptr);
362 static nsBoundingMetrics
363 GetTextBoundingMetrics(nsFontMetrics
* aMetrics
, const char16_t
*aString
, uint32_t aLength
,
364 nsRenderingContext
*aContext
, gfxFont::BoundingBoxType aType
)
367 return nsBoundingMetrics();
369 StubPropertyProvider provider
;
370 AutoTextRun
textRun(aMetrics
, aContext
, aString
, aLength
);
373 gfxTextRun::Metrics theMetrics
=
374 textRun
->MeasureText(0, aLength
,
376 aContext
->ThebesContext(), &provider
);
378 m
.leftBearing
= NSToCoordFloor( theMetrics
.mBoundingBox
.X());
379 m
.rightBearing
= NSToCoordCeil( theMetrics
.mBoundingBox
.XMost());
380 m
.ascent
= NSToCoordCeil( -theMetrics
.mBoundingBox
.Y());
381 m
.descent
= NSToCoordCeil( theMetrics
.mBoundingBox
.YMost());
382 m
.width
= NSToCoordRound( theMetrics
.mAdvanceWidth
);
388 nsFontMetrics::GetBoundingMetrics(const char16_t
*aString
, uint32_t aLength
,
389 nsRenderingContext
*aContext
)
391 return GetTextBoundingMetrics(this, aString
, aLength
, aContext
, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS
);
396 nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t
*aString
, uint32_t aLength
,
397 nsRenderingContext
*aContext
)
399 return GetTextBoundingMetrics(this, aString
, aLength
, aContext
, gfxFont::LOOSE_INK_EXTENTS
);