1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/src/nsFontMetrics.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,396 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsFontMetrics.h" 1.10 +#include <math.h> // for floor, ceil 1.11 +#include <algorithm> // for max 1.12 +#include "gfxPlatform.h" // for gfxPlatform 1.13 +#include "gfxPoint.h" // for gfxPoint 1.14 +#include "gfxRect.h" // for gfxRect 1.15 +#include "gfxTypes.h" // for gfxFloat 1.16 +#include "nsBoundingMetrics.h" // for nsBoundingMetrics 1.17 +#include "nsDebug.h" // for NS_ERROR, NS_ABORT_IF_FALSE 1.18 +#include "nsDeviceContext.h" // for nsDeviceContext 1.19 +#include "nsIAtom.h" // for nsIAtom 1.20 +#include "nsMathUtils.h" // for NS_round 1.21 +#include "nsRenderingContext.h" // for nsRenderingContext 1.22 +#include "nsString.h" // for nsString 1.23 +#include "nsStyleConsts.h" // for NS_STYLE_HYPHENS_NONE 1.24 + 1.25 +class gfxUserFontSet; 1.26 + 1.27 +namespace { 1.28 + 1.29 +class AutoTextRun { 1.30 +public: 1.31 + AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, 1.32 + const char* aString, int32_t aLength) 1.33 + { 1.34 + mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( 1.35 + reinterpret_cast<const uint8_t*>(aString), aLength, 1.36 + aRC->ThebesContext(), 1.37 + aMetrics->AppUnitsPerDevPixel(), 1.38 + ComputeFlags(aMetrics)); 1.39 + } 1.40 + 1.41 + AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC, 1.42 + const char16_t* aString, int32_t aLength) 1.43 + { 1.44 + mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun( 1.45 + aString, aLength, 1.46 + aRC->ThebesContext(), 1.47 + aMetrics->AppUnitsPerDevPixel(), 1.48 + ComputeFlags(aMetrics)); 1.49 + } 1.50 + 1.51 + gfxTextRun *get() { return mTextRun; } 1.52 + gfxTextRun *operator->() { return mTextRun; } 1.53 + 1.54 +private: 1.55 + static uint32_t ComputeFlags(nsFontMetrics* aMetrics) { 1.56 + uint32_t flags = 0; 1.57 + if (aMetrics->GetTextRunRTL()) { 1.58 + flags |= gfxTextRunFactory::TEXT_IS_RTL; 1.59 + } 1.60 + return flags; 1.61 + } 1.62 + 1.63 + nsAutoPtr<gfxTextRun> mTextRun; 1.64 +}; 1.65 + 1.66 +class StubPropertyProvider : public gfxTextRun::PropertyProvider { 1.67 +public: 1.68 + virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength, 1.69 + bool* aBreakBefore) { 1.70 + NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); 1.71 + } 1.72 + virtual int8_t GetHyphensOption() { 1.73 + NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText"); 1.74 + return NS_STYLE_HYPHENS_NONE; 1.75 + } 1.76 + virtual gfxFloat GetHyphenWidth() { 1.77 + NS_ERROR("This shouldn't be called because we never enable hyphens"); 1.78 + return 0; 1.79 + } 1.80 + virtual already_AddRefed<gfxContext> GetContext() { 1.81 + NS_ERROR("This shouldn't be called because we never enable hyphens"); 1.82 + return nullptr; 1.83 + } 1.84 + virtual uint32_t GetAppUnitsPerDevUnit() { 1.85 + NS_ERROR("This shouldn't be called because we never enable hyphens"); 1.86 + return 60; 1.87 + } 1.88 + virtual void GetSpacing(uint32_t aStart, uint32_t aLength, 1.89 + Spacing* aSpacing) { 1.90 + NS_ERROR("This shouldn't be called because we never enable spacing"); 1.91 + } 1.92 +}; 1.93 + 1.94 +} // anon namespace 1.95 + 1.96 +nsFontMetrics::nsFontMetrics() 1.97 + : mDeviceContext(nullptr), mP2A(0), mTextRunRTL(false) 1.98 +{ 1.99 +} 1.100 + 1.101 +nsFontMetrics::~nsFontMetrics() 1.102 +{ 1.103 + if (mDeviceContext) 1.104 + mDeviceContext->FontMetricsDeleted(this); 1.105 +} 1.106 + 1.107 +nsresult 1.108 +nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage, 1.109 + nsDeviceContext *aContext, 1.110 + gfxUserFontSet *aUserFontSet, 1.111 + gfxTextPerfMetrics *aTextPerf) 1.112 +{ 1.113 + NS_ABORT_IF_FALSE(mP2A == 0, "already initialized"); 1.114 + 1.115 + mFont = aFont; 1.116 + mLanguage = aLanguage; 1.117 + mDeviceContext = aContext; 1.118 + mP2A = mDeviceContext->AppUnitsPerDevPixel(); 1.119 + 1.120 + gfxFontStyle style(aFont.style, 1.121 + aFont.weight, 1.122 + aFont.stretch, 1.123 + gfxFloat(aFont.size) / mP2A, 1.124 + aLanguage, 1.125 + aFont.sizeAdjust, 1.126 + aFont.systemFont, 1.127 + mDeviceContext->IsPrinterSurface(), 1.128 + aFont.languageOverride); 1.129 + 1.130 + aFont.AddFontFeaturesToStyle(&style); 1.131 + 1.132 + mFontGroup = gfxPlatform::GetPlatform()-> 1.133 + CreateFontGroup(aFont.name, &style, aUserFontSet); 1.134 + mFontGroup->SetTextPerfMetrics(aTextPerf); 1.135 + if (mFontGroup->FontListLength() < 1) 1.136 + return NS_ERROR_UNEXPECTED; 1.137 + 1.138 + return NS_OK; 1.139 +} 1.140 + 1.141 +void 1.142 +nsFontMetrics::Destroy() 1.143 +{ 1.144 + mDeviceContext = nullptr; 1.145 +} 1.146 + 1.147 +// XXXTODO get rid of this macro 1.148 +#define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5) 1.149 +#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A) 1.150 + 1.151 +const gfxFont::Metrics& nsFontMetrics::GetMetrics() const 1.152 +{ 1.153 + return mFontGroup->GetFontAt(0)->GetMetrics(); 1.154 +} 1.155 + 1.156 +nscoord 1.157 +nsFontMetrics::XHeight() 1.158 +{ 1.159 + return ROUND_TO_TWIPS(GetMetrics().xHeight); 1.160 +} 1.161 + 1.162 +nscoord 1.163 +nsFontMetrics::SuperscriptOffset() 1.164 +{ 1.165 + return ROUND_TO_TWIPS(GetMetrics().superscriptOffset); 1.166 +} 1.167 + 1.168 +nscoord 1.169 +nsFontMetrics::SubscriptOffset() 1.170 +{ 1.171 + return ROUND_TO_TWIPS(GetMetrics().subscriptOffset); 1.172 +} 1.173 + 1.174 +void 1.175 +nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize) 1.176 +{ 1.177 + aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset); 1.178 + aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize); 1.179 +} 1.180 + 1.181 +void 1.182 +nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) 1.183 +{ 1.184 + aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset()); 1.185 + aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize); 1.186 +} 1.187 + 1.188 +// GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the 1.189 +// text-decoration lines drawable area. See bug 421353. 1.190 +// BE CAREFUL for rounding each values. The logic MUST be same as 1.191 +// nsCSSRendering::GetTextDecorationRectInternal's. 1.192 + 1.193 +static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics, 1.194 + gfxFontGroup* aFontGroup) 1.195 +{ 1.196 + gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5); 1.197 + gfxFloat size = NS_round(aMetrics.underlineSize); 1.198 + gfxFloat minDescent = floor(offset + size + 0.5); 1.199 + return std::max(minDescent, aMetrics.maxDescent); 1.200 +} 1.201 + 1.202 +static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics) 1.203 +{ 1.204 + return floor(aMetrics.maxAscent + 0.5); 1.205 +} 1.206 + 1.207 +nscoord 1.208 +nsFontMetrics::InternalLeading() 1.209 +{ 1.210 + return ROUND_TO_TWIPS(GetMetrics().internalLeading); 1.211 +} 1.212 + 1.213 +nscoord 1.214 +nsFontMetrics::ExternalLeading() 1.215 +{ 1.216 + return ROUND_TO_TWIPS(GetMetrics().externalLeading); 1.217 +} 1.218 + 1.219 +nscoord 1.220 +nsFontMetrics::EmHeight() 1.221 +{ 1.222 + return ROUND_TO_TWIPS(GetMetrics().emHeight); 1.223 +} 1.224 + 1.225 +nscoord 1.226 +nsFontMetrics::EmAscent() 1.227 +{ 1.228 + return ROUND_TO_TWIPS(GetMetrics().emAscent); 1.229 +} 1.230 + 1.231 +nscoord 1.232 +nsFontMetrics::EmDescent() 1.233 +{ 1.234 + return ROUND_TO_TWIPS(GetMetrics().emDescent); 1.235 +} 1.236 + 1.237 +nscoord 1.238 +nsFontMetrics::MaxHeight() 1.239 +{ 1.240 + return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) + 1.241 + CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup)); 1.242 +} 1.243 + 1.244 +nscoord 1.245 +nsFontMetrics::MaxAscent() 1.246 +{ 1.247 + return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())); 1.248 +} 1.249 + 1.250 +nscoord 1.251 +nsFontMetrics::MaxDescent() 1.252 +{ 1.253 + return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup)); 1.254 +} 1.255 + 1.256 +nscoord 1.257 +nsFontMetrics::MaxAdvance() 1.258 +{ 1.259 + return CEIL_TO_TWIPS(GetMetrics().maxAdvance); 1.260 +} 1.261 + 1.262 +nscoord 1.263 +nsFontMetrics::AveCharWidth() 1.264 +{ 1.265 + // Use CEIL instead of ROUND for consistency with GetMaxAdvance 1.266 + return CEIL_TO_TWIPS(GetMetrics().aveCharWidth); 1.267 +} 1.268 + 1.269 +nscoord 1.270 +nsFontMetrics::SpaceWidth() 1.271 +{ 1.272 + return CEIL_TO_TWIPS(GetMetrics().spaceWidth); 1.273 +} 1.274 + 1.275 +int32_t 1.276 +nsFontMetrics::GetMaxStringLength() 1.277 +{ 1.278 + const gfxFont::Metrics& m = GetMetrics(); 1.279 + const double x = 32767.0 / m.maxAdvance; 1.280 + int32_t len = (int32_t)floor(x); 1.281 + return std::max(1, len); 1.282 +} 1.283 + 1.284 +nscoord 1.285 +nsFontMetrics::GetWidth(const char* aString, uint32_t aLength, 1.286 + nsRenderingContext *aContext) 1.287 +{ 1.288 + if (aLength == 0) 1.289 + return 0; 1.290 + 1.291 + if (aLength == 1 && aString[0] == ' ') 1.292 + return SpaceWidth(); 1.293 + 1.294 + StubPropertyProvider provider; 1.295 + AutoTextRun textRun(this, aContext, aString, aLength); 1.296 + return textRun.get() ? 1.297 + NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; 1.298 +} 1.299 + 1.300 +nscoord 1.301 +nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength, 1.302 + nsRenderingContext *aContext) 1.303 +{ 1.304 + if (aLength == 0) 1.305 + return 0; 1.306 + 1.307 + if (aLength == 1 && aString[0] == ' ') 1.308 + return SpaceWidth(); 1.309 + 1.310 + StubPropertyProvider provider; 1.311 + AutoTextRun textRun(this, aContext, aString, aLength); 1.312 + return textRun.get() ? 1.313 + NSToCoordRound(textRun->GetAdvanceWidth(0, aLength, &provider)) : 0; 1.314 +} 1.315 + 1.316 +// Draw a string using this font handle on the surface passed in. 1.317 +void 1.318 +nsFontMetrics::DrawString(const char *aString, uint32_t aLength, 1.319 + nscoord aX, nscoord aY, 1.320 + nsRenderingContext *aContext) 1.321 +{ 1.322 + if (aLength == 0) 1.323 + return; 1.324 + 1.325 + StubPropertyProvider provider; 1.326 + AutoTextRun textRun(this, aContext, aString, aLength); 1.327 + if (!textRun.get()) { 1.328 + return; 1.329 + } 1.330 + gfxPoint pt(aX, aY); 1.331 + if (mTextRunRTL) { 1.332 + pt.x += textRun->GetAdvanceWidth(0, aLength, &provider); 1.333 + } 1.334 + textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength, 1.335 + &provider, nullptr, nullptr); 1.336 +} 1.337 + 1.338 +void 1.339 +nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength, 1.340 + nscoord aX, nscoord aY, 1.341 + nsRenderingContext *aContext, 1.342 + nsRenderingContext *aTextRunConstructionContext) 1.343 +{ 1.344 + if (aLength == 0) 1.345 + return; 1.346 + 1.347 + StubPropertyProvider provider; 1.348 + AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength); 1.349 + if (!textRun.get()) { 1.350 + return; 1.351 + } 1.352 + gfxPoint pt(aX, aY); 1.353 + if (mTextRunRTL) { 1.354 + pt.x += textRun->GetAdvanceWidth(0, aLength, &provider); 1.355 + } 1.356 + textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength, 1.357 + &provider, nullptr, nullptr); 1.358 +} 1.359 + 1.360 +static nsBoundingMetrics 1.361 +GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_t aLength, 1.362 + nsRenderingContext *aContext, gfxFont::BoundingBoxType aType) 1.363 +{ 1.364 + if (aLength == 0) 1.365 + return nsBoundingMetrics(); 1.366 + 1.367 + StubPropertyProvider provider; 1.368 + AutoTextRun textRun(aMetrics, aContext, aString, aLength); 1.369 + nsBoundingMetrics m; 1.370 + if (textRun.get()) { 1.371 + gfxTextRun::Metrics theMetrics = 1.372 + textRun->MeasureText(0, aLength, 1.373 + aType, 1.374 + aContext->ThebesContext(), &provider); 1.375 + 1.376 + m.leftBearing = NSToCoordFloor( theMetrics.mBoundingBox.X()); 1.377 + m.rightBearing = NSToCoordCeil( theMetrics.mBoundingBox.XMost()); 1.378 + m.ascent = NSToCoordCeil( -theMetrics.mBoundingBox.Y()); 1.379 + m.descent = NSToCoordCeil( theMetrics.mBoundingBox.YMost()); 1.380 + m.width = NSToCoordRound( theMetrics.mAdvanceWidth); 1.381 + } 1.382 + return m; 1.383 +} 1.384 + 1.385 +nsBoundingMetrics 1.386 +nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength, 1.387 + nsRenderingContext *aContext) 1.388 +{ 1.389 + return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS); 1.390 + 1.391 +} 1.392 + 1.393 +nsBoundingMetrics 1.394 +nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength, 1.395 + nsRenderingContext *aContext) 1.396 +{ 1.397 + return GetTextBoundingMetrics(this, aString, aLength, aContext, gfxFont::LOOSE_INK_EXTENTS); 1.398 +} 1.399 +