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"
48 NS_IMPL_ISUPPORTS1(nsThebesFontMetrics
, nsIFontMetrics
)
52 nsThebesFontMetrics::nsThebesFontMetrics()
58 nsThebesFontMetrics::~nsThebesFontMetrics()
65 nsThebesFontMetrics::Init(const nsFont
& aFont
, nsIAtom
* aLangGroup
,
66 nsIDeviceContext
*aContext
)
69 mLangGroup
= aLangGroup
;
70 mDeviceContext
= (nsThebesDeviceContext
*)aContext
;
71 mP2A
= mDeviceContext
->AppUnitsPerDevPixel();
72 mIsRightToLeft
= PR_FALSE
;
73 mTextRunRTL
= PR_FALSE
;
75 gfxFloat size
= gfxFloat(aFont
.size
) / mP2A
;
80 mLangGroup
->GetUTF8String(&lg
);
84 mFontStyle
= new gfxFontStyle(aFont
.style
, aFont
.weight
, size
, langGroup
,
85 aFont
.sizeAdjust
, aFont
.systemFont
,
86 aFont
.familyNameQuirks
);
89 gfxPlatform::GetPlatform()->CreateFontGroup(aFont
.name
, mFontStyle
);
95 nsThebesFontMetrics::Destroy()
100 // XXXTODO get rid of this macro
101 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
102 #define CEIL_TO_TWIPS(x) (nscoord)NS_ceil((x) * mP2A)
104 const gfxFont::Metrics
& nsThebesFontMetrics::GetMetrics() const
106 return mFontGroup
->GetFontAt(0)->GetMetrics();
110 nsThebesFontMetrics::GetXHeight(nscoord
& aResult
)
112 aResult
= ROUND_TO_TWIPS(GetMetrics().xHeight
);
117 nsThebesFontMetrics::GetSuperscriptOffset(nscoord
& aResult
)
119 aResult
= ROUND_TO_TWIPS(GetMetrics().superscriptOffset
);
124 nsThebesFontMetrics::GetSubscriptOffset(nscoord
& aResult
)
126 aResult
= ROUND_TO_TWIPS(GetMetrics().subscriptOffset
);
131 nsThebesFontMetrics::GetStrikeout(nscoord
& aOffset
, nscoord
& aSize
)
133 aOffset
= ROUND_TO_TWIPS(GetMetrics().strikeoutOffset
);
134 aSize
= ROUND_TO_TWIPS(GetMetrics().strikeoutSize
);
139 nsThebesFontMetrics::GetUnderline(nscoord
& aOffset
, nscoord
& aSize
)
141 aOffset
= ROUND_TO_TWIPS(mFontGroup
->GetUnderlineOffset());
142 aSize
= ROUND_TO_TWIPS(GetMetrics().underlineSize
);
147 // GetHeight/GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
148 // text-decoration lines drawable area. See bug 421353.
149 // BE CAREFUL for rounding each values. The logic MUST be same as
150 // nsCSSRendering::GetTextDecorationRectInternal's.
152 static gfxFloat
ComputeMaxDescent(const gfxFont::Metrics
& aMetrics
,
153 gfxFontGroup
* aFontGroup
)
155 gfxFloat offset
= NS_floor(-aFontGroup
->GetUnderlineOffset() + 0.5);
156 gfxFloat size
= NS_round(aMetrics
.underlineSize
);
157 gfxFloat minDescent
= NS_floor(offset
+ size
+ 0.5);
158 return PR_MAX(minDescent
, aMetrics
.maxDescent
);
161 static gfxFloat
ComputeMaxAscent(const gfxFont::Metrics
& aMetrics
)
163 return NS_floor(aMetrics
.maxAscent
+ 0.5);
167 nsThebesFontMetrics::GetHeight(nscoord
&aHeight
)
169 aHeight
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
170 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
175 nsThebesFontMetrics::GetInternalLeading(nscoord
&aLeading
)
177 aLeading
= ROUND_TO_TWIPS(GetMetrics().internalLeading
);
182 nsThebesFontMetrics::GetExternalLeading(nscoord
&aLeading
)
184 aLeading
= ROUND_TO_TWIPS(GetMetrics().externalLeading
);
189 nsThebesFontMetrics::GetEmHeight(nscoord
&aHeight
)
191 aHeight
= ROUND_TO_TWIPS(GetMetrics().emHeight
);
196 nsThebesFontMetrics::GetEmAscent(nscoord
&aAscent
)
198 aAscent
= ROUND_TO_TWIPS(GetMetrics().emAscent
);
203 nsThebesFontMetrics::GetEmDescent(nscoord
&aDescent
)
205 aDescent
= ROUND_TO_TWIPS(GetMetrics().emDescent
);
210 nsThebesFontMetrics::GetMaxHeight(nscoord
&aHeight
)
212 aHeight
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
213 CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
218 nsThebesFontMetrics::GetMaxAscent(nscoord
&aAscent
)
220 aAscent
= CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
225 nsThebesFontMetrics::GetMaxDescent(nscoord
&aDescent
)
227 aDescent
= CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup
));
232 nsThebesFontMetrics::GetMaxAdvance(nscoord
&aAdvance
)
234 aAdvance
= CEIL_TO_TWIPS(GetMetrics().maxAdvance
);
239 nsThebesFontMetrics::GetLangGroup(nsIAtom
** aLangGroup
)
241 *aLangGroup
= mLangGroup
;
242 NS_IF_ADDREF(*aLangGroup
);
247 nsThebesFontMetrics::GetFontHandle(nsFontHandle
&aHandle
)
249 return NS_ERROR_NOT_IMPLEMENTED
;
253 nsThebesFontMetrics::GetAveCharWidth(nscoord
& aAveCharWidth
)
255 // Use CEIL instead of ROUND for consistency with GetMaxAdvance
256 aAveCharWidth
= CEIL_TO_TWIPS(GetMetrics().aveCharWidth
);
261 nsThebesFontMetrics::GetSpaceWidth(nscoord
& aSpaceCharWidth
)
263 aSpaceCharWidth
= CEIL_TO_TWIPS(GetMetrics().spaceWidth
);
268 nsThebesFontMetrics::GetMaxStringLength()
270 const gfxFont::Metrics
& m
= GetMetrics();
271 const double x
= 32767.0 / m
.maxAdvance
;
272 PRInt32 len
= (PRInt32
)floor(x
);
273 return PR_MAX(1, len
);
276 class StubPropertyProvider
: public gfxTextRun::PropertyProvider
{
278 virtual void GetHyphenationBreaks(PRUint32 aStart
, PRUint32 aLength
,
279 PRPackedBool
* aBreakBefore
) {
280 NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
282 virtual gfxFloat
GetHyphenWidth() {
283 NS_ERROR("This shouldn't be called because we never enable hyphens");
286 virtual void GetSpacing(PRUint32 aStart
, PRUint32 aLength
,
288 NS_ERROR("This shouldn't be called because we never enable spacing");
293 nsThebesFontMetrics::GetWidth(const char* aString
, PRUint32 aLength
, nscoord
& aWidth
,
294 nsThebesRenderingContext
*aContext
)
301 // callers that hit this should not be so stupid
302 if ((aLength
== 1) && (aString
[0] == ' '))
303 return GetSpaceWidth(aWidth
);
305 StubPropertyProvider provider
;
306 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
308 return NS_ERROR_FAILURE
;
310 aWidth
= NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
));
316 nsThebesFontMetrics::GetWidth(const PRUnichar
* aString
, PRUint32 aLength
,
317 nscoord
& aWidth
, PRInt32
*aFontID
,
318 nsThebesRenderingContext
*aContext
)
325 // callers that hit this should not be so stupid
326 if ((aLength
== 1) && (aString
[0] == ' '))
327 return GetSpaceWidth(aWidth
);
329 StubPropertyProvider provider
;
330 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
332 return NS_ERROR_FAILURE
;
334 aWidth
= NSToCoordRound(textRun
->GetAdvanceWidth(0, aLength
, &provider
));
339 // Get the text dimensions for this string
341 nsThebesFontMetrics::GetTextDimensions(const PRUnichar
* aString
,
343 nsTextDimensions
& aDimensions
,
350 nsThebesFontMetrics::GetTextDimensions(const char* aString
,
355 nsTextDimensions
& aDimensions
,
356 PRInt32
& aNumCharsFit
,
357 nsTextDimensions
& aLastWordDimensions
,
363 nsThebesFontMetrics::GetTextDimensions(const PRUnichar
* aString
,
368 nsTextDimensions
& aDimensions
,
369 PRInt32
& aNumCharsFit
,
370 nsTextDimensions
& aLastWordDimensions
,
376 // Draw a string using this font handle on the surface passed in.
378 nsThebesFontMetrics::DrawString(const char *aString
, PRUint32 aLength
,
379 nscoord aX
, nscoord aY
,
380 const nscoord
* aSpacing
,
381 nsThebesRenderingContext
*aContext
)
386 NS_ASSERTION(!aSpacing
, "Spacing not supported here");
387 StubPropertyProvider provider
;
388 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
390 return NS_ERROR_FAILURE
;
393 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
395 textRun
->Draw(aContext
->ThebesContext(), pt
, 0, aLength
,
396 nsnull
, &provider
, nsnull
);
401 nsThebesFontMetrics::DrawString(const PRUnichar
* aString
, PRUint32 aLength
,
402 nscoord aX
, nscoord aY
,
404 const nscoord
* aSpacing
,
405 nsThebesRenderingContext
*aContext
)
410 NS_ASSERTION(!aSpacing
, "Spacing not supported here");
411 StubPropertyProvider provider
;
412 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
414 return NS_ERROR_FAILURE
;
417 pt
.x
+= textRun
->GetAdvanceWidth(0, aLength
, &provider
);
419 textRun
->Draw(aContext
->ThebesContext(), pt
, 0, aLength
,
420 nsnull
, &provider
, nsnull
);
427 GetTextRunBoundingMetrics(gfxTextRun
*aTextRun
, PRUint32 aStart
, PRUint32 aLength
,
428 nsThebesRenderingContext
*aContext
,
429 nsBoundingMetrics
&aBoundingMetrics
)
431 StubPropertyProvider provider
;
432 gfxTextRun::Metrics theMetrics
=
433 aTextRun
->MeasureText(aStart
, aLength
, PR_TRUE
, aContext
->ThebesContext(), &provider
);
435 aBoundingMetrics
.leftBearing
= NSToCoordFloor(theMetrics
.mBoundingBox
.X());
436 aBoundingMetrics
.rightBearing
= NSToCoordCeil(theMetrics
.mBoundingBox
.XMost());
437 aBoundingMetrics
.width
= NSToCoordRound(theMetrics
.mAdvanceWidth
);
438 aBoundingMetrics
.ascent
= NSToCoordCeil(- theMetrics
.mBoundingBox
.Y());
439 aBoundingMetrics
.descent
= NSToCoordCeil(theMetrics
.mBoundingBox
.YMost());
443 nsThebesFontMetrics::GetBoundingMetrics(const char *aString
, PRUint32 aLength
,
444 nsThebesRenderingContext
*aContext
,
445 nsBoundingMetrics
&aBoundingMetrics
)
448 aBoundingMetrics
.Clear();
452 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
454 return NS_ERROR_FAILURE
;
456 GetTextRunBoundingMetrics(textRun
.get(), 0, aLength
, aContext
, aBoundingMetrics
);
461 nsThebesFontMetrics::GetBoundingMetrics(const PRUnichar
*aString
, PRUint32 aLength
,
462 nsThebesRenderingContext
*aContext
,
463 nsBoundingMetrics
&aBoundingMetrics
)
466 aBoundingMetrics
.Clear();
470 AutoTextRun
textRun(this, aContext
, aString
, aLength
);
472 return NS_ERROR_FAILURE
;
474 GetTextRunBoundingMetrics(textRun
.get(), 0, aLength
, aContext
, aBoundingMetrics
);
478 #endif /* MOZ_MATHML */
480 // Set the direction of the text rendering
482 nsThebesFontMetrics::SetRightToLeftText(PRBool aIsRTL
)
484 mIsRightToLeft
= aIsRTL
;
488 // Set the direction of the text rendering
490 nsThebesFontMetrics::GetRightToLeftText()
492 return mIsRightToLeft
;