|
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 "gfxPlatform.h" // for gfxPlatform |
|
10 #include "gfxPoint.h" // for gfxPoint |
|
11 #include "gfxRect.h" // for gfxRect |
|
12 #include "gfxTypes.h" // for gfxFloat |
|
13 #include "nsBoundingMetrics.h" // for nsBoundingMetrics |
|
14 #include "nsDebug.h" // for NS_ERROR, NS_ABORT_IF_FALSE |
|
15 #include "nsDeviceContext.h" // for nsDeviceContext |
|
16 #include "nsIAtom.h" // for nsIAtom |
|
17 #include "nsMathUtils.h" // for NS_round |
|
18 #include "nsRenderingContext.h" // for nsRenderingContext |
|
19 #include "nsString.h" // for nsString |
|
20 #include "nsStyleConsts.h" // for NS_STYLE_HYPHENS_NONE |
|
21 |
|
22 class gfxUserFontSet; |
|
23 |
|
24 namespace { |
|
25 |
|
26 class AutoTextRun { |
|
27 public: |
|
28 AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, |
|
29 const char* aString, int32_t aLength) |
|
30 { |
|
31 mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( |
|
32 reinterpret_cast<const uint8_t*>(aString), aLength, |
|
33 aRC->ThebesContext(), |
|
34 aMetrics->AppUnitsPerDevPixel(), |
|
35 ComputeFlags(aMetrics)); |
|
36 } |
|
37 |
|
38 AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, |
|
39 const char16_t* aString, int32_t aLength) |
|
40 { |
|
41 mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( |
|
42 aString, aLength, |
|
43 aRC->ThebesContext(), |
|
44 aMetrics->AppUnitsPerDevPixel(), |
|
45 ComputeFlags(aMetrics)); |
|
46 } |
|
47 |
|
48 gfxTextRun *get() { return mTextRun; } |
|
49 gfxTextRun *operator->() { return mTextRun; } |
|
50 |
|
51 private: |
|
52 static uint32_t ComputeFlags(nsFontMetrics* aMetrics) { |
|
53 uint32_t flags = 0; |
|
54 if (aMetrics->GetTextRunRTL()) { |
|
55 flags |= gfxTextRunFactory::TEXT_IS_RTL; |
|
56 } |
|
57 return flags; |
|
58 } |
|
59 |
|
60 nsAutoPtr<gfxTextRun> mTextRun; |
|
61 }; |
|
62 |
|
63 class StubPropertyProvider : public gfxTextRun::PropertyProvider { |
|
64 public: |
|
65 virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength, |
|
66 bool* aBreakBefore) { |
|
67 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); |
|
68 } |
|
69 virtual int8_t GetHyphensOption() { |
|
70 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); |
|
71 return NS_STYLE_HYPHENS_NONE; |
|
72 } |
|
73 virtual gfxFloat GetHyphenWidth() { |
|
74 NS_ERROR("This shouldn't be called because we never enable hyphens"); |
|
75 return 0; |
|
76 } |
|
77 virtual already_AddRefed<gfxContext> GetContext() { |
|
78 NS_ERROR("This shouldn't be called because we never enable hyphens"); |
|
79 return nullptr; |
|
80 } |
|
81 virtual uint32_t GetAppUnitsPerDevUnit() { |
|
82 NS_ERROR("This shouldn't be called because we never enable hyphens"); |
|
83 return 60; |
|
84 } |
|
85 virtual void GetSpacing(uint32_t aStart, uint32_t aLength, |
|
86 Spacing* aSpacing) { |
|
87 NS_ERROR("This shouldn't be called because we never enable spacing"); |
|
88 } |
|
89 }; |
|
90 |
|
91 } // anon namespace |
|
92 |
|
93 nsFontMetrics::nsFontMetrics() |
|
94 : mDeviceContext(nullptr), mP2A(0), mTextRunRTL(false) |
|
95 { |
|
96 } |
|
97 |
|
98 nsFontMetrics::~nsFontMetrics() |
|
99 { |
|
100 if (mDeviceContext) |
|
101 mDeviceContext->FontMetricsDeleted(this); |
|
102 } |
|
103 |
|
104 nsresult |
|
105 nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, |
|
106 nsDeviceContext *aContext, |
|
107 gfxUserFontSet *aUserFontSet, |
|
108 gfxTextPerfMetrics *aTextPerf) |
|
109 { |
|
110 NS_ABORT_IF_FALSE(mP2A == 0, "already initialized"); |
|
111 |
|
112 mFont = aFont; |
|
113 mLanguage = aLanguage; |
|
114 mDeviceContext = aContext; |
|
115 mP2A = mDeviceContext->AppUnitsPerDevPixel(); |
|
116 |
|
117 gfxFontStyle style(aFont.style, |
|
118 aFont.weight, |
|
119 aFont.stretch, |
|
120 gfxFloat(aFont.size) / mP2A, |
|
121 aLanguage, |
|
122 aFont.sizeAdjust, |
|
123 aFont.systemFont, |
|
124 mDeviceContext->IsPrinterSurface(), |
|
125 aFont.languageOverride); |
|
126 |
|
127 aFont.AddFontFeaturesToStyle(&style); |
|
128 |
|
129 mFontGroup = gfxPlatform::GetPlatform()-> |
|
130 CreateFontGroup(aFont.name, &style, aUserFontSet); |
|
131 mFontGroup->SetTextPerfMetrics(aTextPerf); |
|
132 if (mFontGroup->FontListLength() < 1) |
|
133 return NS_ERROR_UNEXPECTED; |
|
134 |
|
135 return NS_OK; |
|
136 } |
|
137 |
|
138 void |
|
139 nsFontMetrics::Destroy() |
|
140 { |
|
141 mDeviceContext = nullptr; |
|
142 } |
|
143 |
|
144 // XXXTODO get rid of this macro |
|
145 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5) |
|
146 #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A) |
|
147 |
|
148 const gfxFont::Metrics& nsFontMetrics::GetMetrics() const |
|
149 { |
|
150 return mFontGroup->GetFontAt(0)->GetMetrics(); |
|
151 } |
|
152 |
|
153 nscoord |
|
154 nsFontMetrics::XHeight() |
|
155 { |
|
156 return ROUND_TO_TWIPS(GetMetrics().xHeight); |
|
157 } |
|
158 |
|
159 nscoord |
|
160 nsFontMetrics::SuperscriptOffset() |
|
161 { |
|
162 return ROUND_TO_TWIPS(GetMetrics().superscriptOffset); |
|
163 } |
|
164 |
|
165 nscoord |
|
166 nsFontMetrics::SubscriptOffset() |
|
167 { |
|
168 return ROUND_TO_TWIPS(GetMetrics().subscriptOffset); |
|
169 } |
|
170 |
|
171 void |
|
172 nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize) |
|
173 { |
|
174 aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset); |
|
175 aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize); |
|
176 } |
|
177 |
|
178 void |
|
179 nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) |
|
180 { |
|
181 aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset()); |
|
182 aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize); |
|
183 } |
|
184 |
|
185 // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the |
|
186 // text-decoration lines drawable area. See bug 421353. |
|
187 // BE CAREFUL for rounding each values. The logic MUST be same as |
|
188 // nsCSSRendering::GetTextDecorationRectInternal's. |
|
189 |
|
190 static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics, |
|
191 gfxFontGroup* aFontGroup) |
|
192 { |
|
193 gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5); |
|
194 gfxFloat size = NS_round(aMetrics.underlineSize); |
|
195 gfxFloat minDescent = floor(offset + size + 0.5); |
|
196 return std::max(minDescent, aMetrics.maxDescent); |
|
197 } |
|
198 |
|
199 static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics) |
|
200 { |
|
201 return floor(aMetrics.maxAscent + 0.5); |
|
202 } |
|
203 |
|
204 nscoord |
|
205 nsFontMetrics::InternalLeading() |
|
206 { |
|
207 return ROUND_TO_TWIPS(GetMetrics().internalLeading); |
|
208 } |
|
209 |
|
210 nscoord |
|
211 nsFontMetrics::ExternalLeading() |
|
212 { |
|
213 return ROUND_TO_TWIPS(GetMetrics().externalLeading); |
|
214 } |
|
215 |
|
216 nscoord |
|
217 nsFontMetrics::EmHeight() |
|
218 { |
|
219 return ROUND_TO_TWIPS(GetMetrics().emHeight); |
|
220 } |
|
221 |
|
222 nscoord |
|
223 nsFontMetrics::EmAscent() |
|
224 { |
|
225 return ROUND_TO_TWIPS(GetMetrics().emAscent); |
|
226 } |
|
227 |
|
228 nscoord |
|
229 nsFontMetrics::EmDescent() |
|
230 { |
|
231 return ROUND_TO_TWIPS(GetMetrics().emDescent); |
|
232 } |
|
233 |
|
234 nscoord |
|
235 nsFontMetrics::MaxHeight() |
|
236 { |
|
237 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) + |
|
238 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup)); |
|
239 } |
|
240 |
|
241 nscoord |
|
242 nsFontMetrics::MaxAscent() |
|
243 { |
|
244 return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())); |
|
245 } |
|
246 |
|
247 nscoord |
|
248 nsFontMetrics::MaxDescent() |
|
249 { |
|
250 return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup)); |
|
251 } |
|
252 |
|
253 nscoord |
|
254 nsFontMetrics::MaxAdvance() |
|
255 { |
|
256 return CEIL_TO_TWIPS(GetMetrics().maxAdvance); |
|
257 } |
|
258 |
|
259 nscoord |
|
260 nsFontMetrics::AveCharWidth() |
|
261 { |
|
262 // Use CEIL instead of ROUND for consistency with GetMaxAdvance |
|
263 return CEIL_TO_TWIPS(GetMetrics().aveCharWidth); |
|
264 } |
|
265 |
|
266 nscoord |
|
267 nsFontMetrics::SpaceWidth() |
|
268 { |
|
269 return CEIL_TO_TWIPS(GetMetrics().spaceWidth); |
|
270 } |
|
271 |
|
272 int32_t |
|
273 nsFontMetrics::GetMaxStringLength() |
|
274 { |
|
275 const gfxFont::Metrics& m = GetMetrics(); |
|
276 const double x = 32767.0 / m.maxAdvance; |
|
277 int32_t len = (int32_t)floor(x); |
|
278 return std::max(1, len); |
|
279 } |
|
280 |
|
281 nscoord |
|
282 nsFontMetrics::GetWidth(const char* aString, uint32_t aLength, |
|
283 nsRenderingContext *aContext) |
|
284 { |
|
285 if (aLength == 0) |
|
286 return 0; |
|
287 |
|
288 if (aLength == 1 && aString[0] == ' ') |
|
289 return SpaceWidth(); |
|
290 |
|
291 StubPropertyProvider provider; |
|
292 AutoTextRun textRun(this, aContext, aString, aLength); |
|
293 return textRun.get() ? |
|
294 NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; |
|
295 } |
|
296 |
|
297 nscoord |
|
298 nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength, |
|
299 nsRenderingContext *aContext) |
|
300 { |
|
301 if (aLength == 0) |
|
302 return 0; |
|
303 |
|
304 if (aLength == 1 && aString[0] == ' ') |
|
305 return SpaceWidth(); |
|
306 |
|
307 StubPropertyProvider provider; |
|
308 AutoTextRun textRun(this, aContext, aString, aLength); |
|
309 return textRun.get() ? |
|
310 NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; |
|
311 } |
|
312 |
|
313 // Draw a string using this font handle on the surface passed in. |
|
314 void |
|
315 nsFontMetrics::DrawString(const char *aString, uint32_t aLength, |
|
316 nscoord aX, nscoord aY, |
|
317 nsRenderingContext *aContext) |
|
318 { |
|
319 if (aLength == 0) |
|
320 return; |
|
321 |
|
322 StubPropertyProvider provider; |
|
323 AutoTextRun textRun(this, aContext, aString, aLength); |
|
324 if (!textRun.get()) { |
|
325 return; |
|
326 } |
|
327 gfxPoint pt(aX, aY); |
|
328 if (mTextRunRTL) { |
|
329 pt.x += textRun->GetAdvanceWidth(0, aLength, &provider); |
|
330 } |
|
331 textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength, |
|
332 &provider, nullptr, nullptr); |
|
333 } |
|
334 |
|
335 void |
|
336 nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength, |
|
337 nscoord aX, nscoord aY, |
|
338 nsRenderingContext *aContext, |
|
339 nsRenderingContext *aTextRunConstructionContext) |
|
340 { |
|
341 if (aLength == 0) |
|
342 return; |
|
343 |
|
344 StubPropertyProvider provider; |
|
345 AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength); |
|
346 if (!textRun.get()) { |
|
347 return; |
|
348 } |
|
349 gfxPoint pt(aX, aY); |
|
350 if (mTextRunRTL) { |
|
351 pt.x += textRun->GetAdvanceWidth(0, aLength, &provider); |
|
352 } |
|
353 textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength, |
|
354 &provider, nullptr, nullptr); |
|
355 } |
|
356 |
|
357 static nsBoundingMetrics |
|
358 GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_t aLength, |
|
359 nsRenderingContext *aContext, gfxFont::BoundingBoxType aType) |
|
360 { |
|
361 if (aLength == 0) |
|
362 return nsBoundingMetrics(); |
|
363 |
|
364 StubPropertyProvider provider; |
|
365 AutoTextRun textRun(aMetrics, aContext, aString, aLength); |
|
366 nsBoundingMetrics m; |
|
367 if (textRun.get()) { |
|
368 gfxTextRun::Metrics theMetrics = |
|
369 textRun->MeasureText(0, aLength, |
|
370 aType, |
|
371 aContext->ThebesContext(), &provider); |
|
372 |
|
373 m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X()); |
|
374 m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost()); |
|
375 m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y()); |
|
376 m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost()); |
|
377 m.width = NSToCoordRound( theMetrics.mAdvanceWidth); |
|
378 } |
|
379 return m; |
|
380 } |
|
381 |
|
382 nsBoundingMetrics |
|
383 nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength, |
|
384 nsRenderingContext *aContext) |
|
385 { |
|
386 return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS); |
|
387 |
|
388 } |
|
389 |
|
390 nsBoundingMetrics |
|
391 nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength, |
|
392 nsRenderingContext *aContext) |
|
393 { |
|
394 return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::LOOSE_INK_EXTENTS); |
|
395 } |
|
396 |