1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * mozilla.org.
19 : * Portions created by the Initial Developer are Copyright (C) 2005
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Stuart Parmenter <pavlov@pavlov.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsFontMetrics.h"
40 : #include "nsBoundingMetrics.h"
41 : #include "nsRenderingContext.h"
42 : #include "nsDeviceContext.h"
43 : #include "nsStyleConsts.h"
44 :
45 : namespace {
46 :
47 0 : class AutoTextRun {
48 : public:
49 0 : AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
50 : const char* aString, PRInt32 aLength)
51 0 : {
52 : mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
53 : reinterpret_cast<const PRUint8*>(aString), aLength,
54 : aRC->ThebesContext(),
55 : aMetrics->AppUnitsPerDevPixel(),
56 0 : ComputeFlags(aMetrics));
57 0 : }
58 :
59 0 : AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
60 : const PRUnichar* aString, PRInt32 aLength)
61 0 : {
62 : mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
63 : aString, aLength,
64 : aRC->ThebesContext(),
65 : aMetrics->AppUnitsPerDevPixel(),
66 0 : ComputeFlags(aMetrics));
67 0 : }
68 :
69 0 : gfxTextRun *get() { return mTextRun; }
70 0 : gfxTextRun *operator->() { return mTextRun; }
71 :
72 : private:
73 0 : static PRUint32 ComputeFlags(nsFontMetrics* aMetrics) {
74 0 : PRUint32 flags = 0;
75 0 : if (aMetrics->GetTextRunRTL()) {
76 0 : flags |= gfxTextRunFactory::TEXT_IS_RTL;
77 : }
78 0 : return flags;
79 : }
80 :
81 : nsAutoPtr<gfxTextRun> mTextRun;
82 : };
83 :
84 0 : class StubPropertyProvider : public gfxTextRun::PropertyProvider {
85 : public:
86 0 : virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
87 : bool* aBreakBefore) {
88 0 : NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
89 0 : }
90 0 : virtual PRInt8 GetHyphensOption() {
91 0 : NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
92 0 : return NS_STYLE_HYPHENS_NONE;
93 : }
94 0 : virtual gfxFloat GetHyphenWidth() {
95 0 : NS_ERROR("This shouldn't be called because we never enable hyphens");
96 0 : return 0;
97 : }
98 0 : virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength,
99 : Spacing* aSpacing) {
100 0 : NS_ERROR("This shouldn't be called because we never enable spacing");
101 0 : }
102 : };
103 :
104 : } // anon namespace
105 :
106 0 : nsFontMetrics::nsFontMetrics()
107 0 : : mDeviceContext(nsnull), mP2A(0), mTextRunRTL(false)
108 : {
109 0 : }
110 :
111 0 : nsFontMetrics::~nsFontMetrics()
112 : {
113 0 : if (mDeviceContext)
114 0 : mDeviceContext->FontMetricsDeleted(this);
115 0 : }
116 :
117 : nsresult
118 0 : nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
119 : nsDeviceContext *aContext,
120 : gfxUserFontSet *aUserFontSet)
121 : {
122 0 : NS_ABORT_IF_FALSE(mP2A == 0, "already initialized");
123 :
124 0 : mFont = aFont;
125 0 : mLanguage = aLanguage;
126 0 : mDeviceContext = aContext;
127 0 : mP2A = mDeviceContext->AppUnitsPerDevPixel();
128 :
129 : gfxFontStyle style(aFont.style,
130 : aFont.weight,
131 : aFont.stretch,
132 : gfxFloat(aFont.size) / mP2A,
133 : aLanguage,
134 : aFont.sizeAdjust,
135 : aFont.systemFont,
136 0 : mDeviceContext->IsPrinterSurface(),
137 : aFont.featureSettings,
138 0 : aFont.languageOverride);
139 :
140 0 : mFontGroup = gfxPlatform::GetPlatform()->
141 0 : CreateFontGroup(aFont.name, &style, aUserFontSet);
142 0 : if (mFontGroup->FontListLength() < 1)
143 0 : return NS_ERROR_UNEXPECTED;
144 :
145 0 : return NS_OK;
146 : }
147 :
148 : void
149 0 : nsFontMetrics::Destroy()
150 : {
151 0 : mDeviceContext = nsnull;
152 0 : }
153 :
154 : // XXXTODO get rid of this macro
155 : #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
156 : #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
157 :
158 0 : const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
159 : {
160 0 : return mFontGroup->GetFontAt(0)->GetMetrics();
161 : }
162 :
163 : nscoord
164 0 : nsFontMetrics::XHeight()
165 : {
166 0 : return ROUND_TO_TWIPS(GetMetrics().xHeight);
167 : }
168 :
169 : nscoord
170 0 : nsFontMetrics::SuperscriptOffset()
171 : {
172 0 : return ROUND_TO_TWIPS(GetMetrics().superscriptOffset);
173 : }
174 :
175 : nscoord
176 0 : nsFontMetrics::SubscriptOffset()
177 : {
178 0 : return ROUND_TO_TWIPS(GetMetrics().subscriptOffset);
179 : }
180 :
181 : void
182 0 : nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
183 : {
184 0 : aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset);
185 0 : aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize);
186 0 : }
187 :
188 : void
189 0 : nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
190 : {
191 0 : aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
192 0 : aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
193 0 : }
194 :
195 : // GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
196 : // text-decoration lines drawable area. See bug 421353.
197 : // BE CAREFUL for rounding each values. The logic MUST be same as
198 : // nsCSSRendering::GetTextDecorationRectInternal's.
199 :
200 0 : static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
201 : gfxFontGroup* aFontGroup)
202 : {
203 0 : gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
204 0 : gfxFloat size = NS_round(aMetrics.underlineSize);
205 0 : gfxFloat minDescent = floor(offset + size + 0.5);
206 0 : return NS_MAX(minDescent, aMetrics.maxDescent);
207 : }
208 :
209 0 : static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
210 : {
211 0 : return floor(aMetrics.maxAscent + 0.5);
212 : }
213 :
214 : nscoord
215 0 : nsFontMetrics::InternalLeading()
216 : {
217 0 : return ROUND_TO_TWIPS(GetMetrics().internalLeading);
218 : }
219 :
220 : nscoord
221 0 : nsFontMetrics::ExternalLeading()
222 : {
223 0 : return ROUND_TO_TWIPS(GetMetrics().externalLeading);
224 : }
225 :
226 : nscoord
227 0 : nsFontMetrics::EmHeight()
228 : {
229 0 : return ROUND_TO_TWIPS(GetMetrics().emHeight);
230 : }
231 :
232 : nscoord
233 0 : nsFontMetrics::EmAscent()
234 : {
235 0 : return ROUND_TO_TWIPS(GetMetrics().emAscent);
236 : }
237 :
238 : nscoord
239 0 : nsFontMetrics::EmDescent()
240 : {
241 0 : return ROUND_TO_TWIPS(GetMetrics().emDescent);
242 : }
243 :
244 : nscoord
245 0 : nsFontMetrics::MaxHeight()
246 : {
247 0 : return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
248 0 : CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
249 : }
250 :
251 : nscoord
252 0 : nsFontMetrics::MaxAscent()
253 : {
254 0 : return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
255 : }
256 :
257 : nscoord
258 0 : nsFontMetrics::MaxDescent()
259 : {
260 0 : return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
261 : }
262 :
263 : nscoord
264 0 : nsFontMetrics::MaxAdvance()
265 : {
266 0 : return CEIL_TO_TWIPS(GetMetrics().maxAdvance);
267 : }
268 :
269 : nscoord
270 0 : nsFontMetrics::AveCharWidth()
271 : {
272 : // Use CEIL instead of ROUND for consistency with GetMaxAdvance
273 0 : return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
274 : }
275 :
276 : nscoord
277 0 : nsFontMetrics::SpaceWidth()
278 : {
279 0 : return CEIL_TO_TWIPS(GetMetrics().spaceWidth);
280 : }
281 :
282 : PRInt32
283 0 : nsFontMetrics::GetMaxStringLength()
284 : {
285 0 : const gfxFont::Metrics& m = GetMetrics();
286 0 : const double x = 32767.0 / m.maxAdvance;
287 0 : PRInt32 len = (PRInt32)floor(x);
288 0 : return NS_MAX(1, len);
289 : }
290 :
291 : nscoord
292 0 : nsFontMetrics::GetWidth(const char* aString, PRUint32 aLength,
293 : nsRenderingContext *aContext)
294 : {
295 0 : if (aLength == 0)
296 0 : return 0;
297 :
298 0 : if (aLength == 1 && aString[0] == ' ')
299 0 : return SpaceWidth();
300 :
301 0 : StubPropertyProvider provider;
302 0 : AutoTextRun textRun(this, aContext, aString, aLength);
303 0 : return textRun.get() ?
304 0 : NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0;
305 : }
306 :
307 : nscoord
308 0 : nsFontMetrics::GetWidth(const PRUnichar* aString, PRUint32 aLength,
309 : nsRenderingContext *aContext)
310 : {
311 0 : if (aLength == 0)
312 0 : return 0;
313 :
314 0 : if (aLength == 1 && aString[0] == ' ')
315 0 : return SpaceWidth();
316 :
317 0 : StubPropertyProvider provider;
318 0 : AutoTextRun textRun(this, aContext, aString, aLength);
319 0 : return textRun.get() ?
320 0 : NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0;
321 : }
322 :
323 : // Draw a string using this font handle on the surface passed in.
324 : void
325 0 : nsFontMetrics::DrawString(const char *aString, PRUint32 aLength,
326 : nscoord aX, nscoord aY,
327 : nsRenderingContext *aContext)
328 : {
329 0 : if (aLength == 0)
330 0 : return;
331 :
332 0 : StubPropertyProvider provider;
333 0 : AutoTextRun textRun(this, aContext, aString, aLength);
334 0 : if (!textRun.get()) {
335 : return;
336 : }
337 0 : gfxPoint pt(aX, aY);
338 0 : if (mTextRunRTL) {
339 0 : pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
340 : }
341 : textRun->Draw(aContext->ThebesContext(), pt, gfxFont::GLYPH_FILL, 0, aLength,
342 0 : &provider, nsnull, nsnull);
343 : }
344 :
345 : void
346 0 : nsFontMetrics::DrawString(const PRUnichar* aString, PRUint32 aLength,
347 : nscoord aX, nscoord aY,
348 : nsRenderingContext *aContext,
349 : nsRenderingContext *aTextRunConstructionContext)
350 : {
351 0 : if (aLength == 0)
352 0 : return;
353 :
354 0 : StubPropertyProvider provider;
355 0 : AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength);
356 0 : if (!textRun.get()) {
357 : return;
358 : }
359 0 : gfxPoint pt(aX, aY);
360 0 : if (mTextRunRTL) {
361 0 : pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
362 : }
363 : textRun->Draw(aContext->ThebesContext(), pt, gfxFont::GLYPH_FILL, 0, aLength,
364 0 : &provider, nsnull, nsnull);
365 : }
366 :
367 : nsBoundingMetrics
368 0 : nsFontMetrics::GetBoundingMetrics(const PRUnichar *aString, PRUint32 aLength,
369 : nsRenderingContext *aContext)
370 : {
371 0 : if (aLength == 0)
372 0 : return nsBoundingMetrics();
373 :
374 0 : StubPropertyProvider provider;
375 0 : AutoTextRun textRun(this, aContext, aString, aLength);
376 0 : nsBoundingMetrics m;
377 0 : if (textRun.get()) {
378 : gfxTextRun::Metrics theMetrics =
379 : textRun->MeasureText(0, aLength,
380 : gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
381 0 : aContext->ThebesContext(), &provider);
382 :
383 0 : m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X());
384 0 : m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost());
385 0 : m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y());
386 0 : m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost());
387 0 : m.width = NSToCoordRound( theMetrics.mAdvanceWidth);
388 : }
389 0 : return m;
390 : }
|