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
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/
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
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Stuart Parmenter <pavlov@pavlov.net>
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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsThebesFontMetrics.h"
45 #include "gfxTextRunCache.h"
46 #include "gfxPlatform.h"
47 #include "gfxUserFontSet.h"
49 NS_IMPL_ISUPPORTS1(nsThebesFontMetrics
, nsIFontMetrics
)
53 nsThebesFontMetrics::nsThebesFontMetrics()
59 nsThebesFontMetrics::~nsThebesFontMetrics()
62 mDeviceContext
->FontMetricsDeleted(this);
68 nsThebesFontMetrics::Init(const nsFont
& aFont
, nsIAtom
* aLanguage
,
69 nsIDeviceContext
*aContext
,
70 gfxUserFontSet
*aUserFontSet
)
73 mLanguage
= aLanguage
;
74 mDeviceContext
= (nsThebesDeviceContext
*)aContext
;
75 mP2A
= mDeviceContext
->AppUnitsPerDevPixel();
76 mIsRightToLeft
= PR_FALSE
;
77 mTextRunRTL
= PR_FALSE
;
79 gfxFloat size
= gfxFloat(aFont
.size
) / mP2A
;
81 PRBool printerFont
= mDeviceContext
->IsPrinterSurface();
82 mFontStyle
= new gfxFontStyle(aFont
.style
, aFont
.weight
, aFont
.stretch
,
84 aFont
.sizeAdjust
, aFont
.systemFont
,
85 aFont
.familyNameQuirks
,
87 aFont
.featureSettings
,
88 aFont
.languageOverride
);
91 gfxPlatform::GetPlatform()->CreateFontGroup(aFont
.name
, mFontStyle
,
93 if (mFontGroup
->FontListLength() < 1)
94 return NS_ERROR_UNEXPECTED
;
100 nsThebesFontMetrics::Destroy()
102 mDeviceContext
= nsnull
;
106 // XXXTODO get rid of this macro
107 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
108 #define CEIL_TO_TWIPS(x) (nscoord)NS_ceil((x) * mP2A)
110 const gfxFont::Metrics
& nsThebesFontMetrics::GetMetrics() const
112 return mFontGroup
->GetFontAt(0)->GetMetrics();
116 nsThebesFontMetrics::GetXHeight(nscoord
& aResult
)
118 aResult
= ROUND_TO_TWIPS(GetMetrics().xHeight
);
123 nsThebesFontMetrics::GetSuperscriptOffset(nscoord
& aResult
)
125 aResult
= ROUND_TO_TWIPS(GetMetrics().superscriptOffset
);
130 nsThebesFontMetrics::GetSubscriptOffset(nscoord
& aResult
)
132 aResult
= ROUND_TO_TWIPS(GetMetrics().subscriptOffset
);
137 nsThebesFontMetrics::GetStrikeout(nscoord
& aOffset
, nscoord
& aSize
)
139 aOffset
= ROUND_TO_TWIPS(GetMetrics().strikeoutOffset
);
140 aSize
= ROUND_TO_TWIPS(GetMetrics().strikeoutSize
);
145 nsThebesFontMetrics::GetUnderline(nscoord
& aOffset
, nscoord
& aSize
)
147 aOffset
= ROUND_TO_TWIPS(mFontGroup
->GetUnderlineOffset());
148 aSize
= ROUND_TO_TWIPS(GetMetrics().underlineSize
);
153 // GetHeight/GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
154 // text-decoration lines drawable area. See bug 421353.
155 // BE CAREFUL for rounding each values. The logic MUST be same as
156 // nsCSSRendering::GetTextDecorationRectInternal's.
158 static gfxFloat
ComputeMaxDescent(const gfxFont::Metrics
& aMetrics
,
159 gfxFontGroup
* aFontGroup
)
161 gfxFloat offset
= NS_floor(-aFontGroup
->GetUnderlineOffset() + 0.5);
162 gfxFloat size
= NS_round(aMetrics
.underlineSize
);
163 gfxFloat minDescent
= NS_floor(offset
+ size
+ 0.5);
164 return PR_MAX(minDescent
, aMetrics
.maxDescent
);
167 static gfxFloat
ComputeMaxAscent(const gfxFont::Metrics
& aMetrics
)
169 return NS_floor(aMetrics
.maxAscent
+ 0.5);
173 nsThebesFontMetrics::GetHeight(nscoord
&aHeight
)
175 aHeight
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
176 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
181 nsThebesFontMetrics::GetInternalLeading(nscoord
&aLeading
)
183 aLeading
= ROUND_TO_TWIPS(GetMetrics().internalLeading
);
188 nsThebesFontMetrics::GetExternalLeading(nscoord
&aLeading
)
190 aLeading
= ROUND_TO_TWIPS(GetMetrics().externalLeading
);
195 nsThebesFontMetrics::GetEmHeight(nscoord
&aHeight
)
197 aHeight
= ROUND_TO_TWIPS(GetMetrics().emHeight
);
202 nsThebesFontMetrics::GetEmAscent(nscoord
&aAscent
)
204 aAscent
= ROUND_TO_TWIPS(GetMetrics().emAscent
);
209 nsThebesFontMetrics::GetEmDescent(nscoord
&aDescent
)
211 aDescent
= ROUND_TO_TWIPS(GetMetrics().emDescent
);
216 nsThebesFontMetrics::GetMaxHeight(nscoord
&aHeight
)
218 aHeight
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
219 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
224 nsThebesFontMetrics::GetMaxAscent(nscoord
&aAscent
)
226 aAscent
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
231 nsThebesFontMetrics::GetMaxDescent(nscoord
&aDescent
)
233 aDescent
= CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
238 nsThebesFontMetrics::GetMaxAdvance(nscoord
&aAdvance
)
240 aAdvance
= CEIL_TO_TWIPS(GetMetrics().maxAdvance
);
245 nsThebesFontMetrics::GetLanguage(nsIAtom
** aLanguage
)
247 *aLanguage
= mLanguage
;
248 NS_IF_ADDREF(*aLanguage
);
253 nsThebesFontMetrics::GetFontHandle(nsFontHandle
&aHandle
)
255 return NS_ERROR_NOT_IMPLEMENTED
;
259 nsThebesFontMetrics::GetAveCharWidth(nscoord
& aAveCharWidth
)
261 // Use CEIL instead of ROUND for consistency with GetMaxAdvance
262 aAveCharWidth
= CEIL_TO_TWIPS(GetMetrics().aveCharWidth
);
267 nsThebesFontMetrics::GetSpaceWidth(nscoord
& aSpaceCharWidth
)
269 aSpaceCharWidth
= CEIL_TO_TWIPS(GetMetrics().spaceWidth
);
274 nsThebesFontMetrics::GetMaxStringLength()
276 const gfxFont::Metrics
& m
= GetMetrics();
277 const double x
= 32767.0 / m
.maxAdvance
;
278 PRInt32 len
= (PRInt32
)floor(x
);
279 return PR_MAX(1, len
);
282 class StubPropertyProvider
: public gfxTextRun::PropertyProvider
{
284 virtual void GetHyphenationBreaks(PRUint32 aStart
, PRUint32 aLength
,
285 PRPackedBool
* aBreakBefore
) {
286 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
288 virtual gfxFloat
GetHyphenWidth() {
289 NS_ERROR("This shouldn't be called because we never enable hyphens");
292 virtual void GetSpacing(PRUint32 aStart
, PRUint32 aLength
,
294 NS_ERROR("This shouldn't be called because we never enable spacing");
299 nsThebesFontMetrics::GetWidth(const char* aString
, PRUint32 aLength
, nscoord
& aWidth
,
300 nsThebesRenderingContext
*aContext
)
307 // callers that hit this should not be so stupid
308 if ((aLength
== 1) && (aString
[0] == ' '))
309 return GetSpaceWidth(aWidth
);
311 StubPropertyProvider provider
;
312 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
314 return NS_ERROR_FAILURE
;
316 aWidth
= NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
));
322 nsThebesFontMetrics::GetWidth(const PRUnichar
* aString
, PRUint32 aLength
,
323 nscoord
& aWidth
, PRInt32
*aFontID
,
324 nsThebesRenderingContext
*aContext
)
331 // callers that hit this should not be so stupid
332 if ((aLength
== 1) && (aString
[0] == ' '))
333 return GetSpaceWidth(aWidth
);
335 StubPropertyProvider provider
;
336 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
338 return NS_ERROR_FAILURE
;
340 aWidth
= NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
));
345 // Get the text dimensions for this string
347 nsThebesFontMetrics::GetTextDimensions(const PRUnichar
* aString
,
349 nsTextDimensions
& aDimensions
,
356 nsThebesFontMetrics::GetTextDimensions(const char* aString
,
361 nsTextDimensions
& aDimensions
,
362 PRInt32
& aNumCharsFit
,
363 nsTextDimensions
& aLastWordDimensions
,
369 nsThebesFontMetrics::GetTextDimensions(const PRUnichar
* aString
,
374 nsTextDimensions
& aDimensions
,
375 PRInt32
& aNumCharsFit
,
376 nsTextDimensions
& aLastWordDimensions
,
382 // Draw a string using this font handle on the surface passed in.
384 nsThebesFontMetrics::DrawString(const char *aString
, PRUint32 aLength
,
385 nscoord aX
, nscoord aY
,
386 const nscoord
* aSpacing
,
387 nsThebesRenderingContext
*aContext
)
392 NS_ASSERTION(!aSpacing
, "Spacing not supported here");
393 StubPropertyProvider provider
;
394 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
396 return NS_ERROR_FAILURE
;
399 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
401 textRun
->Draw(aContext
->ThebesContext(), pt
, 0, aLength
,
402 nsnull
, &provider
, nsnull
);
407 nsThebesFontMetrics::DrawString(const PRUnichar
* aString
, PRUint32 aLength
,
408 nscoord aX
, nscoord aY
,
410 const nscoord
* aSpacing
,
411 nsThebesRenderingContext
*aContext
)
416 NS_ASSERTION(!aSpacing
, "Spacing not supported here");
417 StubPropertyProvider provider
;
418 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
420 return NS_ERROR_FAILURE
;
423 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
425 textRun
->Draw(aContext
->ThebesContext(), pt
, 0, aLength
,
426 nsnull
, &provider
, nsnull
);
433 GetTextRunBoundingMetrics(gfxTextRun
*aTextRun
, PRUint32 aStart
, PRUint32 aLength
,
434 nsThebesRenderingContext
*aContext
,
435 nsBoundingMetrics
&aBoundingMetrics
)
437 StubPropertyProvider provider
;
438 gfxTextRun::Metrics theMetrics
=
439 aTextRun
->MeasureText(aStart
, aLength
, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS
,
440 aContext
->ThebesContext(), &provider
);
441 // note that TIGHT_HINTED_OUTLINE_EXTENTS can be expensive (on Windows)
442 // but this is only used for MathML positioning so it's not critical
444 aBoundingMetrics
.leftBearing
= NSToCoordFloor(theMetrics
.mBoundingBox
.X());
445 aBoundingMetrics
.rightBearing
= NSToCoordCeil(theMetrics
.mBoundingBox
.XMost());
446 aBoundingMetrics
.width
= NSToCoordRound(theMetrics
.mAdvanceWidth
);
447 aBoundingMetrics
.ascent
= NSToCoordCeil(- theMetrics
.mBoundingBox
.Y());
448 aBoundingMetrics
.descent
= NSToCoordCeil(theMetrics
.mBoundingBox
.YMost());
452 nsThebesFontMetrics::GetBoundingMetrics(const char *aString
, PRUint32 aLength
,
453 nsThebesRenderingContext
*aContext
,
454 nsBoundingMetrics
&aBoundingMetrics
)
457 aBoundingMetrics
.Clear();
461 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
463 return NS_ERROR_FAILURE
;
465 GetTextRunBoundingMetrics(textRun
.get(), 0, aLength
, aContext
, aBoundingMetrics
);
470 nsThebesFontMetrics::GetBoundingMetrics(const PRUnichar
*aString
, PRUint32 aLength
,
471 nsThebesRenderingContext
*aContext
,
472 nsBoundingMetrics
&aBoundingMetrics
)
475 aBoundingMetrics
.Clear();
479 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
481 return NS_ERROR_FAILURE
;
483 GetTextRunBoundingMetrics(textRun
.get(), 0, aLength
, aContext
, aBoundingMetrics
);
487 #endif /* MOZ_MATHML */
489 // Set the direction of the text rendering
491 nsThebesFontMetrics::SetRightToLeftText(PRBool aIsRTL
)
493 mIsRightToLeft
= aIsRTL
;
497 // Set the direction of the text rendering
499 nsThebesFontMetrics::GetRightToLeftText()
501 return mIsRightToLeft
;
504 /* virtual */ gfxUserFontSet
*
505 nsThebesFontMetrics::GetUserFontSet()
507 return mFontGroup
->GetUserFontSet();