Line data Source code
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/. */
5 :
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 "gfxTypes.h" // for gfxFloat
15 : #include "nsBoundingMetrics.h" // for nsBoundingMetrics
16 : #include "nsDebug.h" // for NS_ERROR
17 : #include "nsDeviceContext.h" // for nsDeviceContext
18 : #include "nsIAtom.h" // for nsIAtom
19 : #include "nsMathUtils.h" // for NS_round
20 : #include "nsString.h" // for nsString
21 : #include "nsStyleConsts.h" // for StyleHyphens::None
22 : #include "mozilla/Assertions.h" // for MOZ_ASSERT
23 : #include "mozilla/UniquePtr.h" // for UniquePtr
24 :
25 : class gfxUserFontSet;
26 : using namespace mozilla;
27 :
28 : namespace {
29 :
30 62 : class AutoTextRun {
31 : public:
32 : typedef mozilla::gfx::DrawTarget DrawTarget;
33 :
34 0 : AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
35 : const char* aString, int32_t aLength)
36 0 : {
37 0 : mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
38 : reinterpret_cast<const uint8_t*>(aString), aLength,
39 : aDrawTarget,
40 : aMetrics->AppUnitsPerDevPixel(),
41 : ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
42 0 : nullptr);
43 0 : }
44 :
45 62 : AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
46 : const char16_t* aString, int32_t aLength)
47 62 : {
48 124 : mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
49 : aString, aLength,
50 : aDrawTarget,
51 : aMetrics->AppUnitsPerDevPixel(),
52 : ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
53 62 : nullptr);
54 62 : }
55 :
56 62 : gfxTextRun *get() { return mTextRun.get(); }
57 62 : gfxTextRun *operator->() { return mTextRun.get(); }
58 :
59 : private:
60 62 : static gfx::ShapedTextFlags ComputeFlags(nsFontMetrics* aMetrics) {
61 62 : gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
62 62 : if (aMetrics->GetTextRunRTL()) {
63 0 : flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
64 : }
65 62 : if (aMetrics->GetVertical()) {
66 0 : switch (aMetrics->GetTextOrientation()) {
67 : case NS_STYLE_TEXT_ORIENTATION_MIXED:
68 0 : flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
69 0 : break;
70 : case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
71 0 : flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
72 0 : break;
73 : case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
74 0 : flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
75 0 : break;
76 : }
77 : }
78 62 : return flags;
79 : }
80 :
81 : RefPtr<gfxTextRun> mTextRun;
82 : };
83 :
84 : class StubPropertyProvider final : public gfxTextRun::PropertyProvider {
85 : public:
86 0 : void GetHyphenationBreaks(gfxTextRun::Range aRange,
87 : gfxTextRun::HyphenType* aBreakBefore) const {
88 0 : NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
89 0 : }
90 0 : mozilla::StyleHyphens GetHyphensOption() const {
91 0 : NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
92 0 : return mozilla::StyleHyphens::None;
93 : }
94 0 : gfxFloat GetHyphenWidth() const {
95 0 : NS_ERROR("This shouldn't be called because we never enable hyphens");
96 0 : return 0;
97 : }
98 0 : already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() const {
99 0 : NS_ERROR("This shouldn't be called because we never enable hyphens");
100 0 : return nullptr;
101 : }
102 0 : uint32_t GetAppUnitsPerDevUnit() const {
103 0 : NS_ERROR("This shouldn't be called because we never enable hyphens");
104 0 : return 60;
105 : }
106 0 : void GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) const {
107 0 : NS_ERROR("This shouldn't be called because we never enable spacing");
108 0 : }
109 : };
110 :
111 : } // namespace
112 :
113 6 : nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
114 6 : nsDeviceContext *aContext)
115 : : mFont(aFont)
116 6 : , mLanguage(aParams.language)
117 : , mDeviceContext(aContext)
118 6 : , mP2A(aContext->AppUnitsPerDevPixel())
119 6 : , mOrientation(aParams.orientation)
120 : , mTextRunRTL(false)
121 : , mVertical(false)
122 24 : , mTextOrientation(0)
123 : {
124 6 : gfxFontStyle style(aFont.style,
125 6 : aFont.weight,
126 6 : aFont.stretch,
127 6 : gfxFloat(aFont.size) / mP2A,
128 6 : aParams.language,
129 6 : aParams.explicitLanguage,
130 6 : aFont.sizeAdjust,
131 6 : aFont.systemFont,
132 6 : mDeviceContext->IsPrinterContext(),
133 6 : aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
134 6 : aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
135 66 : aFont.languageOverride);
136 :
137 6 : aFont.AddFontFeaturesToStyle(&style);
138 6 : aFont.AddFontVariationsToStyle(&style);
139 :
140 6 : gfxFloat devToCssSize = gfxFloat(mP2A) /
141 6 : gfxFloat(mDeviceContext->AppUnitsPerCSSPixel());
142 6 : mFontGroup = gfxPlatform::GetPlatform()->
143 6 : CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
144 12 : aParams.userFontSet, devToCssSize);
145 6 : }
146 :
147 2 : nsFontMetrics::~nsFontMetrics()
148 : {
149 : // Should not be dropped by stylo
150 1 : MOZ_ASSERT(NS_IsMainThread());
151 1 : if (mDeviceContext) {
152 0 : mDeviceContext->FontMetricsDeleted(this);
153 : }
154 1 : }
155 :
156 : void
157 1 : nsFontMetrics::Destroy()
158 : {
159 1 : mDeviceContext = nullptr;
160 1 : }
161 :
162 : // XXXTODO get rid of this macro
163 : #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
164 : #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
165 :
166 : const gfxFont::Metrics&
167 938 : nsFontMetrics::GetMetrics(gfxFont::Orientation aOrientation) const
168 : {
169 938 : return mFontGroup->GetFirstValidFont()->GetMetrics(aOrientation);
170 : }
171 :
172 : nscoord
173 0 : nsFontMetrics::XHeight()
174 : {
175 0 : return ROUND_TO_TWIPS(GetMetrics().xHeight);
176 : }
177 :
178 : nscoord
179 0 : nsFontMetrics::CapHeight()
180 : {
181 0 : return ROUND_TO_TWIPS(GetMetrics().capHeight);
182 : }
183 :
184 : nscoord
185 0 : nsFontMetrics::SuperscriptOffset()
186 : {
187 0 : return ROUND_TO_TWIPS(GetMetrics().emHeight *
188 : NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
189 : }
190 :
191 : nscoord
192 0 : nsFontMetrics::SubscriptOffset()
193 : {
194 0 : return ROUND_TO_TWIPS(GetMetrics().emHeight *
195 : NS_FONT_SUBSCRIPT_OFFSET_RATIO);
196 : }
197 :
198 : void
199 0 : nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
200 : {
201 0 : aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset);
202 0 : aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize);
203 0 : }
204 :
205 : void
206 0 : nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
207 : {
208 0 : aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
209 0 : aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
210 0 : }
211 :
212 : // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
213 : // text-decoration lines drawable area. See bug 421353.
214 : // BE CAREFUL for rounding each values. The logic MUST be same as
215 : // nsCSSRendering::GetTextDecorationRectInternal's.
216 :
217 138 : static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
218 : gfxFontGroup* aFontGroup)
219 : {
220 138 : gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
221 138 : gfxFloat size = NS_round(aMetrics.underlineSize);
222 138 : gfxFloat minDescent = offset + size;
223 138 : return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
224 : }
225 :
226 270 : static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
227 : {
228 270 : return floor(aMetrics.maxAscent + 0.5);
229 : }
230 :
231 : nscoord
232 147 : nsFontMetrics::InternalLeading()
233 : {
234 147 : return ROUND_TO_TWIPS(GetMetrics().internalLeading);
235 : }
236 :
237 : nscoord
238 147 : nsFontMetrics::ExternalLeading()
239 : {
240 147 : return ROUND_TO_TWIPS(GetMetrics().externalLeading);
241 : }
242 :
243 : nscoord
244 147 : nsFontMetrics::EmHeight()
245 : {
246 147 : return ROUND_TO_TWIPS(GetMetrics().emHeight);
247 : }
248 :
249 : nscoord
250 0 : nsFontMetrics::EmAscent()
251 : {
252 0 : return ROUND_TO_TWIPS(GetMetrics().emAscent);
253 : }
254 :
255 : nscoord
256 0 : nsFontMetrics::EmDescent()
257 : {
258 0 : return ROUND_TO_TWIPS(GetMetrics().emDescent);
259 : }
260 :
261 : nscoord
262 114 : nsFontMetrics::MaxHeight()
263 : {
264 114 : return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
265 114 : CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
266 : }
267 :
268 : nscoord
269 156 : nsFontMetrics::MaxAscent()
270 : {
271 156 : return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
272 : }
273 :
274 : nscoord
275 24 : nsFontMetrics::MaxDescent()
276 : {
277 24 : return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
278 : }
279 :
280 : nscoord
281 20 : nsFontMetrics::MaxAdvance()
282 : {
283 20 : return CEIL_TO_TWIPS(GetMetrics().maxAdvance);
284 : }
285 :
286 : nscoord
287 29 : nsFontMetrics::AveCharWidth()
288 : {
289 : // Use CEIL instead of ROUND for consistency with GetMaxAdvance
290 29 : return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
291 : }
292 :
293 : nscoord
294 0 : nsFontMetrics::SpaceWidth()
295 : {
296 : // For vertical text with mixed or sideways orientation, we want the
297 : // width of a horizontal space (even if we're using vertical line-spacing
298 : // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
299 0 : return CEIL_TO_TWIPS(
300 : GetMetrics(mVertical &&
301 : mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT
302 : ? gfxFont::eVertical
303 : : gfxFont::eHorizontal).spaceWidth);
304 : }
305 :
306 : int32_t
307 40 : nsFontMetrics::GetMaxStringLength()
308 : {
309 40 : const gfxFont::Metrics& m = GetMetrics();
310 40 : const double x = 32767.0 / m.maxAdvance;
311 40 : int32_t len = (int32_t)floor(x);
312 40 : return std::max(1, len);
313 : }
314 :
315 : nscoord
316 0 : nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
317 : DrawTarget* aDrawTarget)
318 : {
319 0 : if (aLength == 0)
320 0 : return 0;
321 :
322 0 : if (aLength == 1 && aString[0] == ' ')
323 0 : return SpaceWidth();
324 :
325 0 : StubPropertyProvider provider;
326 0 : AutoTextRun textRun(this, aDrawTarget, aString, aLength);
327 0 : if (textRun.get()) {
328 0 : return NSToCoordRound(
329 0 : textRun->GetAdvanceWidth(Range(0, aLength), &provider));
330 : }
331 0 : return 0;
332 : }
333 :
334 : nscoord
335 40 : nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
336 : DrawTarget* aDrawTarget)
337 : {
338 40 : if (aLength == 0)
339 0 : return 0;
340 :
341 40 : if (aLength == 1 && aString[0] == ' ')
342 0 : return SpaceWidth();
343 :
344 40 : StubPropertyProvider provider;
345 80 : AutoTextRun textRun(this, aDrawTarget, aString, aLength);
346 40 : if (textRun.get()) {
347 80 : return NSToCoordRound(
348 40 : textRun->GetAdvanceWidth(Range(0, aLength), &provider));
349 : }
350 0 : return 0;
351 : }
352 :
353 : // Draw a string using this font handle on the surface passed in.
354 : void
355 0 : nsFontMetrics::DrawString(const char *aString, uint32_t aLength,
356 : nscoord aX, nscoord aY,
357 : gfxContext *aContext)
358 : {
359 0 : if (aLength == 0)
360 0 : return;
361 :
362 0 : StubPropertyProvider provider;
363 0 : AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
364 0 : if (!textRun.get()) {
365 0 : return;
366 : }
367 0 : gfxPoint pt(aX, aY);
368 0 : Range range(0, aLength);
369 0 : if (mTextRunRTL) {
370 0 : if (mVertical) {
371 0 : pt.y += textRun->GetAdvanceWidth(range, &provider);
372 : } else {
373 0 : pt.x += textRun->GetAdvanceWidth(range, &provider);
374 : }
375 : }
376 0 : gfxTextRun::DrawParams params(aContext);
377 0 : params.provider = &provider;
378 0 : textRun->Draw(range, pt, params);
379 : }
380 :
381 : void
382 3 : nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
383 : nscoord aX, nscoord aY,
384 : gfxContext *aContext,
385 : DrawTarget* aTextRunConstructionDrawTarget)
386 : {
387 3 : if (aLength == 0)
388 0 : return;
389 :
390 3 : StubPropertyProvider provider;
391 6 : AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
392 3 : if (!textRun.get()) {
393 0 : return;
394 : }
395 3 : gfxPoint pt(aX, aY);
396 3 : Range range(0, aLength);
397 3 : if (mTextRunRTL) {
398 0 : if (mVertical) {
399 0 : pt.y += textRun->GetAdvanceWidth(range, &provider);
400 : } else {
401 0 : pt.x += textRun->GetAdvanceWidth(range, &provider);
402 : }
403 : }
404 3 : gfxTextRun::DrawParams params(aContext);
405 3 : params.provider = &provider;
406 3 : textRun->Draw(range, pt, params);
407 : }
408 :
409 : static nsBoundingMetrics
410 24 : GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString,
411 : uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget,
412 : gfxFont::BoundingBoxType aType)
413 : {
414 24 : if (aLength == 0)
415 5 : return nsBoundingMetrics();
416 :
417 19 : StubPropertyProvider provider;
418 38 : AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
419 19 : nsBoundingMetrics m;
420 19 : if (textRun.get()) {
421 : gfxTextRun::Metrics theMetrics = textRun->MeasureText(
422 19 : gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
423 :
424 19 : m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X());
425 19 : m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost());
426 19 : m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y());
427 19 : m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost());
428 19 : m.width = NSToCoordRound( theMetrics.mAdvanceWidth);
429 : }
430 19 : return m;
431 : }
432 :
433 : nsBoundingMetrics
434 0 : nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength,
435 : DrawTarget* aDrawTarget)
436 : {
437 : return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
438 0 : gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
439 : }
440 :
441 : nsBoundingMetrics
442 24 : nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength,
443 : DrawTarget* aDrawTarget)
444 : {
445 : return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
446 24 : gfxFont::LOOSE_INK_EXTENTS);
447 : }
448 :
|