Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef GFX_FONT_H |
michael@0 | 7 | #define GFX_FONT_H |
michael@0 | 8 | |
michael@0 | 9 | #include "gfxTypes.h" |
michael@0 | 10 | #include "nsString.h" |
michael@0 | 11 | #include "gfxPoint.h" |
michael@0 | 12 | #include "gfxFontUtils.h" |
michael@0 | 13 | #include "nsTArray.h" |
michael@0 | 14 | #include "nsTHashtable.h" |
michael@0 | 15 | #include "nsHashKeys.h" |
michael@0 | 16 | #include "gfxSkipChars.h" |
michael@0 | 17 | #include "gfxRect.h" |
michael@0 | 18 | #include "nsExpirationTracker.h" |
michael@0 | 19 | #include "gfxPlatform.h" |
michael@0 | 20 | #include "nsIAtom.h" |
michael@0 | 21 | #include "mozilla/HashFunctions.h" |
michael@0 | 22 | #include "nsIMemoryReporter.h" |
michael@0 | 23 | #include "nsIObserver.h" |
michael@0 | 24 | #include "gfxFontFeatures.h" |
michael@0 | 25 | #include "mozilla/MemoryReporting.h" |
michael@0 | 26 | #include "mozilla/Attributes.h" |
michael@0 | 27 | #include <algorithm> |
michael@0 | 28 | #include "DrawMode.h" |
michael@0 | 29 | #include "nsUnicodeScriptCodes.h" |
michael@0 | 30 | #include "nsDataHashtable.h" |
michael@0 | 31 | #include "harfbuzz/hb.h" |
michael@0 | 32 | #include "mozilla/gfx/2D.h" |
michael@0 | 33 | |
michael@0 | 34 | typedef struct _cairo_scaled_font cairo_scaled_font_t; |
michael@0 | 35 | typedef struct gr_face gr_face; |
michael@0 | 36 | |
michael@0 | 37 | #ifdef DEBUG |
michael@0 | 38 | #include <stdio.h> |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | class gfxContext; |
michael@0 | 42 | class gfxTextRun; |
michael@0 | 43 | class gfxFont; |
michael@0 | 44 | class gfxFontFamily; |
michael@0 | 45 | class gfxFontGroup; |
michael@0 | 46 | class gfxGraphiteShaper; |
michael@0 | 47 | class gfxHarfBuzzShaper; |
michael@0 | 48 | class gfxUserFontSet; |
michael@0 | 49 | class gfxUserFontData; |
michael@0 | 50 | class gfxShapedText; |
michael@0 | 51 | class gfxShapedWord; |
michael@0 | 52 | class gfxSVGGlyphs; |
michael@0 | 53 | class gfxMathTable; |
michael@0 | 54 | class gfxTextContextPaint; |
michael@0 | 55 | class FontInfoData; |
michael@0 | 56 | |
michael@0 | 57 | class nsILanguageAtomService; |
michael@0 | 58 | |
michael@0 | 59 | #define FONT_MAX_SIZE 2000.0 |
michael@0 | 60 | |
michael@0 | 61 | #define NO_FONT_LANGUAGE_OVERRIDE 0 |
michael@0 | 62 | |
michael@0 | 63 | struct FontListSizes; |
michael@0 | 64 | struct gfxTextRunDrawCallbacks; |
michael@0 | 65 | |
michael@0 | 66 | namespace mozilla { |
michael@0 | 67 | namespace gfx { |
michael@0 | 68 | class GlyphRenderingOptions; |
michael@0 | 69 | } |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | struct gfxFontStyle { |
michael@0 | 73 | gfxFontStyle(); |
michael@0 | 74 | gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch, |
michael@0 | 75 | gfxFloat aSize, nsIAtom *aLanguage, |
michael@0 | 76 | float aSizeAdjust, bool aSystemFont, |
michael@0 | 77 | bool aPrinterFont, |
michael@0 | 78 | const nsString& aLanguageOverride); |
michael@0 | 79 | gfxFontStyle(const gfxFontStyle& aStyle); |
michael@0 | 80 | |
michael@0 | 81 | // the language (may be an internal langGroup code rather than an actual |
michael@0 | 82 | // language code) specified in the document or element's lang property, |
michael@0 | 83 | // or inferred from the charset |
michael@0 | 84 | nsRefPtr<nsIAtom> language; |
michael@0 | 85 | |
michael@0 | 86 | // Features are composed of (1) features from style rules (2) features |
michael@0 | 87 | // from feature setttings rules and (3) family-specific features. (1) and |
michael@0 | 88 | // (3) are guaranteed to be mutually exclusive |
michael@0 | 89 | |
michael@0 | 90 | // custom opentype feature settings |
michael@0 | 91 | nsTArray<gfxFontFeature> featureSettings; |
michael@0 | 92 | |
michael@0 | 93 | // Some font-variant property values require font-specific settings |
michael@0 | 94 | // defined via @font-feature-values rules. These are resolved after |
michael@0 | 95 | // font matching occurs. |
michael@0 | 96 | |
michael@0 | 97 | // -- list of value tags for specific alternate features |
michael@0 | 98 | nsTArray<gfxAlternateValue> alternateValues; |
michael@0 | 99 | |
michael@0 | 100 | // -- object used to look these up once the font is matched |
michael@0 | 101 | nsRefPtr<gfxFontFeatureValueSet> featureValueLookup; |
michael@0 | 102 | |
michael@0 | 103 | // The logical size of the font, in pixels |
michael@0 | 104 | gfxFloat size; |
michael@0 | 105 | |
michael@0 | 106 | // The aspect-value (ie., the ratio actualsize:actualxheight) that any |
michael@0 | 107 | // actual physical font created from this font structure must have when |
michael@0 | 108 | // rendering or measuring a string. A value of 0 means no adjustment |
michael@0 | 109 | // needs to be done. |
michael@0 | 110 | float sizeAdjust; |
michael@0 | 111 | |
michael@0 | 112 | // Language system tag, to override document language; |
michael@0 | 113 | // an OpenType "language system" tag represented as a 32-bit integer |
michael@0 | 114 | // (see http://www.microsoft.com/typography/otspec/languagetags.htm). |
michael@0 | 115 | // Normally 0, so font rendering will use the document or element language |
michael@0 | 116 | // (see above) to control any language-specific rendering, but the author |
michael@0 | 117 | // can override this for cases where the options implemented in the font |
michael@0 | 118 | // do not directly match the actual language. (E.g. lang may be Macedonian, |
michael@0 | 119 | // but the font in use does not explicitly support this; the author can |
michael@0 | 120 | // use font-language-override to request the Serbian option in the font |
michael@0 | 121 | // in order to get correct glyph shapes.) |
michael@0 | 122 | uint32_t languageOverride; |
michael@0 | 123 | |
michael@0 | 124 | // The weight of the font: 100, 200, ... 900. |
michael@0 | 125 | uint16_t weight; |
michael@0 | 126 | |
michael@0 | 127 | // The stretch of the font (the sum of various NS_FONT_STRETCH_* |
michael@0 | 128 | // constants; see gfxFontConstants.h). |
michael@0 | 129 | int8_t stretch; |
michael@0 | 130 | |
michael@0 | 131 | // Say that this font is a system font and therefore does not |
michael@0 | 132 | // require certain fixup that we do for fonts from untrusted |
michael@0 | 133 | // sources. |
michael@0 | 134 | bool systemFont : 1; |
michael@0 | 135 | |
michael@0 | 136 | // Say that this font is used for print or print preview. |
michael@0 | 137 | bool printerFont : 1; |
michael@0 | 138 | |
michael@0 | 139 | // Used to imitate -webkit-font-smoothing: antialiased |
michael@0 | 140 | bool useGrayscaleAntialiasing : 1; |
michael@0 | 141 | |
michael@0 | 142 | // The style of font (normal, italic, oblique) |
michael@0 | 143 | uint8_t style : 2; |
michael@0 | 144 | |
michael@0 | 145 | // Return the final adjusted font size for the given aspect ratio. |
michael@0 | 146 | // Not meant to be called when sizeAdjust = 0. |
michael@0 | 147 | gfxFloat GetAdjustedSize(gfxFloat aspect) const { |
michael@0 | 148 | NS_ASSERTION(sizeAdjust != 0.0, "Not meant to be called when sizeAdjust = 0"); |
michael@0 | 149 | gfxFloat adjustedSize = std::max(NS_round(size*(sizeAdjust/aspect)), 1.0); |
michael@0 | 150 | return std::min(adjustedSize, FONT_MAX_SIZE); |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | PLDHashNumber Hash() const { |
michael@0 | 154 | return ((style + (systemFont << 7) + |
michael@0 | 155 | (weight << 8)) + uint32_t(size*1000) + uint32_t(sizeAdjust*1000)) ^ |
michael@0 | 156 | nsISupportsHashKey::HashKey(language); |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | int8_t ComputeWeight() const; |
michael@0 | 160 | |
michael@0 | 161 | bool Equals(const gfxFontStyle& other) const { |
michael@0 | 162 | return |
michael@0 | 163 | (*reinterpret_cast<const uint64_t*>(&size) == |
michael@0 | 164 | *reinterpret_cast<const uint64_t*>(&other.size)) && |
michael@0 | 165 | (style == other.style) && |
michael@0 | 166 | (systemFont == other.systemFont) && |
michael@0 | 167 | (printerFont == other.printerFont) && |
michael@0 | 168 | (useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) && |
michael@0 | 169 | (weight == other.weight) && |
michael@0 | 170 | (stretch == other.stretch) && |
michael@0 | 171 | (language == other.language) && |
michael@0 | 172 | (*reinterpret_cast<const uint32_t*>(&sizeAdjust) == |
michael@0 | 173 | *reinterpret_cast<const uint32_t*>(&other.sizeAdjust)) && |
michael@0 | 174 | (featureSettings == other.featureSettings) && |
michael@0 | 175 | (languageOverride == other.languageOverride) && |
michael@0 | 176 | (alternateValues == other.alternateValues) && |
michael@0 | 177 | (featureValueLookup == other.featureValueLookup); |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | static void ParseFontFeatureSettings(const nsString& aFeatureString, |
michael@0 | 181 | nsTArray<gfxFontFeature>& aFeatures); |
michael@0 | 182 | |
michael@0 | 183 | static uint32_t ParseFontLanguageOverride(const nsString& aLangTag); |
michael@0 | 184 | }; |
michael@0 | 185 | |
michael@0 | 186 | class gfxCharacterMap : public gfxSparseBitSet { |
michael@0 | 187 | public: |
michael@0 | 188 | nsrefcnt AddRef() { |
michael@0 | 189 | NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); |
michael@0 | 190 | ++mRefCnt; |
michael@0 | 191 | NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this)); |
michael@0 | 192 | return mRefCnt; |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | nsrefcnt Release() { |
michael@0 | 196 | NS_PRECONDITION(0 != mRefCnt, "dup release"); |
michael@0 | 197 | --mRefCnt; |
michael@0 | 198 | NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap"); |
michael@0 | 199 | if (mRefCnt == 0) { |
michael@0 | 200 | NotifyReleased(); |
michael@0 | 201 | // |this| has been deleted. |
michael@0 | 202 | return 0; |
michael@0 | 203 | } |
michael@0 | 204 | return mRefCnt; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | gfxCharacterMap() : |
michael@0 | 208 | mHash(0), mBuildOnTheFly(false), mShared(false) |
michael@0 | 209 | { } |
michael@0 | 210 | |
michael@0 | 211 | void CalcHash() { mHash = GetChecksum(); } |
michael@0 | 212 | |
michael@0 | 213 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { |
michael@0 | 214 | return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | // hash of the cmap bitvector |
michael@0 | 218 | uint32_t mHash; |
michael@0 | 219 | |
michael@0 | 220 | // if cmap is built on the fly it's never shared |
michael@0 | 221 | bool mBuildOnTheFly; |
michael@0 | 222 | |
michael@0 | 223 | // cmap is shared globally |
michael@0 | 224 | bool mShared; |
michael@0 | 225 | |
michael@0 | 226 | protected: |
michael@0 | 227 | void NotifyReleased(); |
michael@0 | 228 | |
michael@0 | 229 | nsAutoRefCnt mRefCnt; |
michael@0 | 230 | |
michael@0 | 231 | private: |
michael@0 | 232 | gfxCharacterMap(const gfxCharacterMap&); |
michael@0 | 233 | gfxCharacterMap& operator=(const gfxCharacterMap&); |
michael@0 | 234 | }; |
michael@0 | 235 | |
michael@0 | 236 | class gfxFontEntry { |
michael@0 | 237 | public: |
michael@0 | 238 | NS_INLINE_DECL_REFCOUNTING(gfxFontEntry) |
michael@0 | 239 | |
michael@0 | 240 | gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false); |
michael@0 | 241 | |
michael@0 | 242 | // unique name for the face, *not* the family; not necessarily the |
michael@0 | 243 | // "real" or user-friendly name, may be an internal identifier |
michael@0 | 244 | const nsString& Name() const { return mName; } |
michael@0 | 245 | |
michael@0 | 246 | // family name |
michael@0 | 247 | const nsString& FamilyName() const { return mFamilyName; } |
michael@0 | 248 | |
michael@0 | 249 | // The following two methods may be relatively expensive, as they |
michael@0 | 250 | // will (usually, except on Linux) load and parse the 'name' table; |
michael@0 | 251 | // they are intended only for the font-inspection API, not for |
michael@0 | 252 | // perf-critical layout/drawing work. |
michael@0 | 253 | |
michael@0 | 254 | // The "real" name of the face, if available from the font resource; |
michael@0 | 255 | // returns Name() if nothing better is available. |
michael@0 | 256 | virtual nsString RealFaceName(); |
michael@0 | 257 | |
michael@0 | 258 | uint16_t Weight() const { return mWeight; } |
michael@0 | 259 | int16_t Stretch() const { return mStretch; } |
michael@0 | 260 | |
michael@0 | 261 | bool IsUserFont() const { return mIsUserFont; } |
michael@0 | 262 | bool IsLocalUserFont() const { return mIsLocalUserFont; } |
michael@0 | 263 | bool IsFixedPitch() const { return mFixedPitch; } |
michael@0 | 264 | bool IsItalic() const { return mItalic; } |
michael@0 | 265 | bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above |
michael@0 | 266 | bool IgnoreGDEF() const { return mIgnoreGDEF; } |
michael@0 | 267 | bool IgnoreGSUB() const { return mIgnoreGSUB; } |
michael@0 | 268 | |
michael@0 | 269 | virtual bool IsSymbolFont(); |
michael@0 | 270 | |
michael@0 | 271 | virtual bool HasFontTable(uint32_t aTableTag); |
michael@0 | 272 | |
michael@0 | 273 | inline bool HasGraphiteTables() { |
michael@0 | 274 | if (!mCheckedForGraphiteTables) { |
michael@0 | 275 | CheckForGraphiteTables(); |
michael@0 | 276 | mCheckedForGraphiteTables = true; |
michael@0 | 277 | } |
michael@0 | 278 | return mHasGraphiteTables; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | inline bool HasCmapTable() { |
michael@0 | 282 | if (!mCharacterMap) { |
michael@0 | 283 | ReadCMAP(); |
michael@0 | 284 | NS_ASSERTION(mCharacterMap, "failed to initialize character map"); |
michael@0 | 285 | } |
michael@0 | 286 | return mHasCmapTable; |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | inline bool HasCharacter(uint32_t ch) { |
michael@0 | 290 | if (mCharacterMap && mCharacterMap->test(ch)) { |
michael@0 | 291 | return true; |
michael@0 | 292 | } |
michael@0 | 293 | return TestCharacterMap(ch); |
michael@0 | 294 | } |
michael@0 | 295 | |
michael@0 | 296 | virtual bool SkipDuringSystemFallback() { return false; } |
michael@0 | 297 | virtual bool TestCharacterMap(uint32_t aCh); |
michael@0 | 298 | nsresult InitializeUVSMap(); |
michael@0 | 299 | uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS); |
michael@0 | 300 | |
michael@0 | 301 | // All concrete gfxFontEntry subclasses (except gfxProxyFontEntry) need |
michael@0 | 302 | // to override this, otherwise the font will never be used as it will |
michael@0 | 303 | // be considered to support no characters. |
michael@0 | 304 | // ReadCMAP() must *always* set the mCharacterMap pointer to a valid |
michael@0 | 305 | // gfxCharacterMap, even if empty, as other code assumes this pointer |
michael@0 | 306 | // can be safely dereferenced. |
michael@0 | 307 | virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr); |
michael@0 | 308 | |
michael@0 | 309 | bool TryGetSVGData(gfxFont* aFont); |
michael@0 | 310 | bool HasSVGGlyph(uint32_t aGlyphId); |
michael@0 | 311 | bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId, |
michael@0 | 312 | gfxRect *aResult); |
michael@0 | 313 | bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode, |
michael@0 | 314 | gfxTextContextPaint *aContextPaint); |
michael@0 | 315 | // Call this when glyph geometry or rendering has changed |
michael@0 | 316 | // (e.g. animated SVG glyphs) |
michael@0 | 317 | void NotifyGlyphsChanged(); |
michael@0 | 318 | |
michael@0 | 319 | enum MathConstant { |
michael@0 | 320 | // The order of the constants must match the order of the fields |
michael@0 | 321 | // defined in the MATH table. |
michael@0 | 322 | ScriptPercentScaleDown, |
michael@0 | 323 | ScriptScriptPercentScaleDown, |
michael@0 | 324 | DelimitedSubFormulaMinHeight, |
michael@0 | 325 | DisplayOperatorMinHeight, |
michael@0 | 326 | MathLeading, |
michael@0 | 327 | AxisHeight, |
michael@0 | 328 | AccentBaseHeight, |
michael@0 | 329 | FlattenedAccentBaseHeight, |
michael@0 | 330 | SubscriptShiftDown, |
michael@0 | 331 | SubscriptTopMax, |
michael@0 | 332 | SubscriptBaselineDropMin, |
michael@0 | 333 | SuperscriptShiftUp, |
michael@0 | 334 | SuperscriptShiftUpCramped, |
michael@0 | 335 | SuperscriptBottomMin, |
michael@0 | 336 | SuperscriptBaselineDropMax, |
michael@0 | 337 | SubSuperscriptGapMin, |
michael@0 | 338 | SuperscriptBottomMaxWithSubscript, |
michael@0 | 339 | SpaceAfterScript, |
michael@0 | 340 | UpperLimitGapMin, |
michael@0 | 341 | UpperLimitBaselineRiseMin, |
michael@0 | 342 | LowerLimitGapMin, |
michael@0 | 343 | LowerLimitBaselineDropMin, |
michael@0 | 344 | StackTopShiftUp, |
michael@0 | 345 | StackTopDisplayStyleShiftUp, |
michael@0 | 346 | StackBottomShiftDown, |
michael@0 | 347 | StackBottomDisplayStyleShiftDown, |
michael@0 | 348 | StackGapMin, |
michael@0 | 349 | StackDisplayStyleGapMin, |
michael@0 | 350 | StretchStackTopShiftUp, |
michael@0 | 351 | StretchStackBottomShiftDown, |
michael@0 | 352 | StretchStackGapAboveMin, |
michael@0 | 353 | StretchStackGapBelowMin, |
michael@0 | 354 | FractionNumeratorShiftUp, |
michael@0 | 355 | FractionNumeratorDisplayStyleShiftUp, |
michael@0 | 356 | FractionDenominatorShiftDown, |
michael@0 | 357 | FractionDenominatorDisplayStyleShiftDown, |
michael@0 | 358 | FractionNumeratorGapMin, |
michael@0 | 359 | FractionNumDisplayStyleGapMin, |
michael@0 | 360 | FractionRuleThickness, |
michael@0 | 361 | FractionDenominatorGapMin, |
michael@0 | 362 | FractionDenomDisplayStyleGapMin, |
michael@0 | 363 | SkewedFractionHorizontalGap, |
michael@0 | 364 | SkewedFractionVerticalGap, |
michael@0 | 365 | OverbarVerticalGap, |
michael@0 | 366 | OverbarRuleThickness, |
michael@0 | 367 | OverbarExtraAscender, |
michael@0 | 368 | UnderbarVerticalGap, |
michael@0 | 369 | UnderbarRuleThickness, |
michael@0 | 370 | UnderbarExtraDescender, |
michael@0 | 371 | RadicalVerticalGap, |
michael@0 | 372 | RadicalDisplayStyleVerticalGap, |
michael@0 | 373 | RadicalRuleThickness, |
michael@0 | 374 | RadicalExtraAscender, |
michael@0 | 375 | RadicalKernBeforeDegree, |
michael@0 | 376 | RadicalKernAfterDegree, |
michael@0 | 377 | RadicalDegreeBottomRaisePercent |
michael@0 | 378 | }; |
michael@0 | 379 | |
michael@0 | 380 | // Call TryGetMathTable to try to load the Open Type MATH table. The other |
michael@0 | 381 | // functions forward the call to the gfxMathTable class. The GetMath...() |
michael@0 | 382 | // functions MUST NOT be called unless TryGetMathTable() has returned true. |
michael@0 | 383 | bool TryGetMathTable(gfxFont* aFont); |
michael@0 | 384 | gfxFloat GetMathConstant(MathConstant aConstant); |
michael@0 | 385 | bool GetMathItalicsCorrection(uint32_t aGlyphID, |
michael@0 | 386 | gfxFloat* aItalicCorrection); |
michael@0 | 387 | uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, |
michael@0 | 388 | uint16_t aSize); |
michael@0 | 389 | bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, |
michael@0 | 390 | uint32_t aGlyphs[4]); |
michael@0 | 391 | |
michael@0 | 392 | virtual bool MatchesGenericFamily(const nsACString& aGeneric) const { |
michael@0 | 393 | return true; |
michael@0 | 394 | } |
michael@0 | 395 | virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const { |
michael@0 | 396 | return true; |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | // Access to raw font table data (needed for Harfbuzz): |
michael@0 | 400 | // returns a pointer to data owned by the fontEntry or the OS, |
michael@0 | 401 | // which will remain valid until the blob is destroyed. |
michael@0 | 402 | // The data MUST be treated as read-only; we may be getting a |
michael@0 | 403 | // reference to a shared system font cache. |
michael@0 | 404 | // |
michael@0 | 405 | // The default implementation uses CopyFontTable to get the data |
michael@0 | 406 | // into a byte array, and maintains a cache of loaded tables. |
michael@0 | 407 | // |
michael@0 | 408 | // Subclasses should override this if they can provide more efficient |
michael@0 | 409 | // access than copying table data into our own buffers. |
michael@0 | 410 | // |
michael@0 | 411 | // Get blob that encapsulates a specific font table, or nullptr if |
michael@0 | 412 | // the table doesn't exist in the font. |
michael@0 | 413 | // |
michael@0 | 414 | // Caller is responsible to call hb_blob_destroy() on the returned blob |
michael@0 | 415 | // (if non-nullptr) when no longer required. For transient access to a |
michael@0 | 416 | // table, use of AutoTable (below) is generally preferred. |
michael@0 | 417 | virtual hb_blob_t *GetFontTable(uint32_t aTag); |
michael@0 | 418 | |
michael@0 | 419 | // Stack-based utility to return a specified table, automatically releasing |
michael@0 | 420 | // the blob when the AutoTable goes out of scope. |
michael@0 | 421 | class AutoTable { |
michael@0 | 422 | public: |
michael@0 | 423 | AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag) |
michael@0 | 424 | { |
michael@0 | 425 | mBlob = aFontEntry->GetFontTable(aTag); |
michael@0 | 426 | } |
michael@0 | 427 | ~AutoTable() { |
michael@0 | 428 | if (mBlob) { |
michael@0 | 429 | hb_blob_destroy(mBlob); |
michael@0 | 430 | } |
michael@0 | 431 | } |
michael@0 | 432 | operator hb_blob_t*() const { return mBlob; } |
michael@0 | 433 | private: |
michael@0 | 434 | hb_blob_t* mBlob; |
michael@0 | 435 | // not implemented: |
michael@0 | 436 | AutoTable(const AutoTable&) MOZ_DELETE; |
michael@0 | 437 | AutoTable& operator=(const AutoTable&) MOZ_DELETE; |
michael@0 | 438 | }; |
michael@0 | 439 | |
michael@0 | 440 | already_AddRefed<gfxFont> FindOrMakeFont(const gfxFontStyle *aStyle, |
michael@0 | 441 | bool aNeedsBold); |
michael@0 | 442 | |
michael@0 | 443 | // Get an existing font table cache entry in aBlob if it has been |
michael@0 | 444 | // registered, or return false if not. Callers must call |
michael@0 | 445 | // hb_blob_destroy on aBlob if true is returned. |
michael@0 | 446 | // |
michael@0 | 447 | // Note that some gfxFont implementations may not call this at all, |
michael@0 | 448 | // if it is more efficient to get the table from the OS at that level. |
michael@0 | 449 | bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob); |
michael@0 | 450 | |
michael@0 | 451 | // Elements of aTable are transferred (not copied) to and returned in a |
michael@0 | 452 | // new hb_blob_t which is registered on the gfxFontEntry, but the initial |
michael@0 | 453 | // reference is owned by the caller. Removing the last reference |
michael@0 | 454 | // unregisters the table from the font entry. |
michael@0 | 455 | // |
michael@0 | 456 | // Pass nullptr for aBuffer to indicate that the table is not present and |
michael@0 | 457 | // nullptr will be returned. Also returns nullptr on OOM. |
michael@0 | 458 | hb_blob_t *ShareFontTableAndGetBlob(uint32_t aTag, |
michael@0 | 459 | FallibleTArray<uint8_t>* aTable); |
michael@0 | 460 | |
michael@0 | 461 | // Get the font's unitsPerEm from the 'head' table, in the case of an |
michael@0 | 462 | // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts, |
michael@0 | 463 | // if present on the platform. |
michael@0 | 464 | uint16_t UnitsPerEm(); |
michael@0 | 465 | enum { |
michael@0 | 466 | kMinUPEM = 16, // Limits on valid unitsPerEm range, from the |
michael@0 | 467 | kMaxUPEM = 16384, // OpenType spec |
michael@0 | 468 | kInvalidUPEM = uint16_t(-1) |
michael@0 | 469 | }; |
michael@0 | 470 | |
michael@0 | 471 | // Shaper face accessors: |
michael@0 | 472 | // NOTE that harfbuzz and graphite handle ownership/lifetime of the face |
michael@0 | 473 | // object in completely different ways. |
michael@0 | 474 | |
michael@0 | 475 | // Get HarfBuzz face corresponding to this font file. |
michael@0 | 476 | // Caller must release with hb_face_destroy() when finished with it, |
michael@0 | 477 | // and the font entry will be notified via ForgetHBFace. |
michael@0 | 478 | hb_face_t* GetHBFace(); |
michael@0 | 479 | virtual void ForgetHBFace(); |
michael@0 | 480 | |
michael@0 | 481 | // Get Graphite face corresponding to this font file. |
michael@0 | 482 | // Caller must call gfxFontEntry::ReleaseGrFace when finished with it. |
michael@0 | 483 | gr_face* GetGrFace(); |
michael@0 | 484 | virtual void ReleaseGrFace(gr_face* aFace); |
michael@0 | 485 | |
michael@0 | 486 | // Release any SVG-glyphs document this font may have loaded. |
michael@0 | 487 | void DisconnectSVG(); |
michael@0 | 488 | |
michael@0 | 489 | // Called to notify that aFont is being destroyed. Needed when we're tracking |
michael@0 | 490 | // the fonts belonging to this font entry. |
michael@0 | 491 | void NotifyFontDestroyed(gfxFont* aFont); |
michael@0 | 492 | |
michael@0 | 493 | // For memory reporting |
michael@0 | 494 | virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 495 | FontListSizes* aSizes) const; |
michael@0 | 496 | virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 497 | FontListSizes* aSizes) const; |
michael@0 | 498 | |
michael@0 | 499 | // Used when checking for complex script support, to mask off cmap ranges |
michael@0 | 500 | struct ScriptRange { |
michael@0 | 501 | uint32_t rangeStart; |
michael@0 | 502 | uint32_t rangeEnd; |
michael@0 | 503 | hb_tag_t tags[3]; // one or two OpenType script tags to check, |
michael@0 | 504 | // plus a NULL terminator |
michael@0 | 505 | }; |
michael@0 | 506 | |
michael@0 | 507 | bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags); |
michael@0 | 508 | |
michael@0 | 509 | nsString mName; |
michael@0 | 510 | nsString mFamilyName; |
michael@0 | 511 | |
michael@0 | 512 | bool mItalic : 1; |
michael@0 | 513 | bool mFixedPitch : 1; |
michael@0 | 514 | bool mIsProxy : 1; |
michael@0 | 515 | bool mIsValid : 1; |
michael@0 | 516 | bool mIsBadUnderlineFont : 1; |
michael@0 | 517 | bool mIsUserFont : 1; |
michael@0 | 518 | bool mIsLocalUserFont : 1; |
michael@0 | 519 | bool mStandardFace : 1; |
michael@0 | 520 | bool mSymbolFont : 1; |
michael@0 | 521 | bool mIgnoreGDEF : 1; |
michael@0 | 522 | bool mIgnoreGSUB : 1; |
michael@0 | 523 | bool mSVGInitialized : 1; |
michael@0 | 524 | bool mMathInitialized : 1; |
michael@0 | 525 | bool mHasSpaceFeaturesInitialized : 1; |
michael@0 | 526 | bool mHasSpaceFeatures : 1; |
michael@0 | 527 | bool mHasSpaceFeaturesKerning : 1; |
michael@0 | 528 | bool mHasSpaceFeaturesNonKerning : 1; |
michael@0 | 529 | bool mSkipDefaultFeatureSpaceCheck : 1; |
michael@0 | 530 | bool mHasGraphiteTables : 1; |
michael@0 | 531 | bool mCheckedForGraphiteTables : 1; |
michael@0 | 532 | bool mHasCmapTable : 1; |
michael@0 | 533 | bool mGrFaceInitialized : 1; |
michael@0 | 534 | |
michael@0 | 535 | // bitvector of substitution space features per script, one each |
michael@0 | 536 | // for default and non-default features |
michael@0 | 537 | uint32_t mDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32]; |
michael@0 | 538 | uint32_t mNonDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32]; |
michael@0 | 539 | |
michael@0 | 540 | uint16_t mWeight; |
michael@0 | 541 | int16_t mStretch; |
michael@0 | 542 | |
michael@0 | 543 | nsRefPtr<gfxCharacterMap> mCharacterMap; |
michael@0 | 544 | uint32_t mUVSOffset; |
michael@0 | 545 | nsAutoArrayPtr<uint8_t> mUVSData; |
michael@0 | 546 | nsAutoPtr<gfxUserFontData> mUserFontData; |
michael@0 | 547 | nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs; |
michael@0 | 548 | // list of gfxFonts that are using SVG glyphs |
michael@0 | 549 | nsTArray<gfxFont*> mFontsUsingSVGGlyphs; |
michael@0 | 550 | nsAutoPtr<gfxMathTable> mMathTable; |
michael@0 | 551 | nsTArray<gfxFontFeature> mFeatureSettings; |
michael@0 | 552 | uint32_t mLanguageOverride; |
michael@0 | 553 | |
michael@0 | 554 | protected: |
michael@0 | 555 | friend class gfxPlatformFontList; |
michael@0 | 556 | friend class gfxMacPlatformFontList; |
michael@0 | 557 | friend class gfxUserFcFontEntry; |
michael@0 | 558 | friend class gfxFontFamily; |
michael@0 | 559 | friend class gfxSingleFaceMacFontFamily; |
michael@0 | 560 | |
michael@0 | 561 | gfxFontEntry(); |
michael@0 | 562 | |
michael@0 | 563 | // Protected destructor, to discourage deletion outside of Release(): |
michael@0 | 564 | virtual ~gfxFontEntry(); |
michael@0 | 565 | |
michael@0 | 566 | virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) { |
michael@0 | 567 | NS_NOTREACHED("oops, somebody didn't override CreateFontInstance"); |
michael@0 | 568 | return nullptr; |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | virtual void CheckForGraphiteTables(); |
michael@0 | 572 | |
michael@0 | 573 | // Copy a font table into aBuffer. |
michael@0 | 574 | // The caller will be responsible for ownership of the data. |
michael@0 | 575 | virtual nsresult CopyFontTable(uint32_t aTableTag, |
michael@0 | 576 | FallibleTArray<uint8_t>& aBuffer) { |
michael@0 | 577 | NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?"); |
michael@0 | 578 | return NS_ERROR_FAILURE; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | // Return a blob that wraps a table found within a buffer of font data. |
michael@0 | 582 | // The blob does NOT own its data; caller guarantees that the buffer |
michael@0 | 583 | // will remain valid at least as long as the blob. |
michael@0 | 584 | // Returns null if the specified table is not found. |
michael@0 | 585 | // This method assumes aFontData is valid 'sfnt' data; before using this, |
michael@0 | 586 | // caller is responsible to do any sanitization/validation necessary. |
michael@0 | 587 | hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag); |
michael@0 | 588 | |
michael@0 | 589 | // lookup the cmap in cached font data |
michael@0 | 590 | virtual already_AddRefed<gfxCharacterMap> |
michael@0 | 591 | GetCMAPFromFontInfo(FontInfoData *aFontInfoData, |
michael@0 | 592 | uint32_t& aUVSOffset, |
michael@0 | 593 | bool& aSymbolFont); |
michael@0 | 594 | |
michael@0 | 595 | // Font's unitsPerEm from the 'head' table, if available (will be set to |
michael@0 | 596 | // kInvalidUPEM for non-sfnt font formats) |
michael@0 | 597 | uint16_t mUnitsPerEm; |
michael@0 | 598 | |
michael@0 | 599 | // Shaper-specific face objects, shared by all instantiations of the same |
michael@0 | 600 | // physical font, regardless of size. |
michael@0 | 601 | // Usually, only one of these will actually be created for any given font |
michael@0 | 602 | // entry, depending on the font tables that are present. |
michael@0 | 603 | |
michael@0 | 604 | // hb_face_t is refcounted internally, so each shaper that's using it will |
michael@0 | 605 | // bump the ref count when it acquires the face, and "destroy" (release) it |
michael@0 | 606 | // in its destructor. The font entry has only this non-owning reference to |
michael@0 | 607 | // the face; when the face is deleted, it will tell the font entry to forget |
michael@0 | 608 | // it, so that a new face will be created next time it is needed. |
michael@0 | 609 | hb_face_t* mHBFace; |
michael@0 | 610 | |
michael@0 | 611 | static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData); |
michael@0 | 612 | |
michael@0 | 613 | // Callback that the hb_face will use to tell us when it is being deleted. |
michael@0 | 614 | static void HBFaceDeletedCallback(void *aUserData); |
michael@0 | 615 | |
michael@0 | 616 | // gr_face is -not- refcounted, so it will be owned directly by the font |
michael@0 | 617 | // entry, and we'll keep a count of how many references we've handed out; |
michael@0 | 618 | // each shaper is responsible to call ReleaseGrFace on its entry when |
michael@0 | 619 | // finished with it, so that we know when it can be deleted. |
michael@0 | 620 | gr_face* mGrFace; |
michael@0 | 621 | |
michael@0 | 622 | // hashtable to map raw table data ptr back to its owning blob, for use by |
michael@0 | 623 | // graphite table-release callback |
michael@0 | 624 | nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap; |
michael@0 | 625 | |
michael@0 | 626 | // number of current users of this entry's mGrFace |
michael@0 | 627 | nsrefcnt mGrFaceRefCnt; |
michael@0 | 628 | |
michael@0 | 629 | static const void* GrGetTable(const void *aAppFaceHandle, |
michael@0 | 630 | unsigned int aName, |
michael@0 | 631 | size_t *aLen); |
michael@0 | 632 | static void GrReleaseTable(const void *aAppFaceHandle, |
michael@0 | 633 | const void *aTableBuffer); |
michael@0 | 634 | |
michael@0 | 635 | private: |
michael@0 | 636 | /** |
michael@0 | 637 | * Font table hashtable, to support GetFontTable for harfbuzz. |
michael@0 | 638 | * |
michael@0 | 639 | * The harfbuzz shaper (and potentially other clients) needs access to raw |
michael@0 | 640 | * font table data. This needs to be cached so that it can be used |
michael@0 | 641 | * repeatedly (each time we construct a text run; in some cases, for |
michael@0 | 642 | * each character/glyph within the run) without re-fetching large tables |
michael@0 | 643 | * every time. |
michael@0 | 644 | * |
michael@0 | 645 | * Because we may instantiate many gfxFonts for the same physical font |
michael@0 | 646 | * file (at different sizes), we should ensure that they can share a |
michael@0 | 647 | * single cached copy of the font tables. To do this, we implement table |
michael@0 | 648 | * access and sharing on the fontEntry rather than the font itself. |
michael@0 | 649 | * |
michael@0 | 650 | * The default implementation uses GetFontTable() to read font table |
michael@0 | 651 | * data into byte arrays, and wraps them in blobs which are registered in |
michael@0 | 652 | * a hashtable. The hashtable can then return pre-existing blobs to |
michael@0 | 653 | * harfbuzz. |
michael@0 | 654 | * |
michael@0 | 655 | * Harfbuzz will "destroy" the blobs when it is finished with them. When |
michael@0 | 656 | * the last blob reference is removed, the FontTableBlobData user data |
michael@0 | 657 | * will remove the blob from the hashtable if still registered. |
michael@0 | 658 | */ |
michael@0 | 659 | |
michael@0 | 660 | class FontTableBlobData; |
michael@0 | 661 | |
michael@0 | 662 | /** |
michael@0 | 663 | * FontTableHashEntry manages the entries of hb_blob_t's containing font |
michael@0 | 664 | * table data. |
michael@0 | 665 | * |
michael@0 | 666 | * This is used to share font tables across fonts with the same |
michael@0 | 667 | * font entry (but different sizes) for use by HarfBuzz. The hashtable |
michael@0 | 668 | * does not own a strong reference to the blob, but keeps a weak pointer, |
michael@0 | 669 | * managed by FontTableBlobData. Similarly FontTableBlobData keeps only a |
michael@0 | 670 | * weak pointer to the hashtable, managed by FontTableHashEntry. |
michael@0 | 671 | */ |
michael@0 | 672 | |
michael@0 | 673 | class FontTableHashEntry : public nsUint32HashKey |
michael@0 | 674 | { |
michael@0 | 675 | public: |
michael@0 | 676 | // Declarations for nsTHashtable |
michael@0 | 677 | |
michael@0 | 678 | typedef nsUint32HashKey KeyClass; |
michael@0 | 679 | typedef KeyClass::KeyType KeyType; |
michael@0 | 680 | typedef KeyClass::KeyTypePointer KeyTypePointer; |
michael@0 | 681 | |
michael@0 | 682 | FontTableHashEntry(KeyTypePointer aTag) |
michael@0 | 683 | : KeyClass(aTag) |
michael@0 | 684 | , mSharedBlobData(nullptr) |
michael@0 | 685 | , mBlob(nullptr) |
michael@0 | 686 | { } |
michael@0 | 687 | |
michael@0 | 688 | // NOTE: This assumes the new entry belongs to the same hashtable as |
michael@0 | 689 | // the old, because the mHashtable pointer in mSharedBlobData (if |
michael@0 | 690 | // present) will not be updated. |
michael@0 | 691 | FontTableHashEntry(FontTableHashEntry&& toMove) |
michael@0 | 692 | : KeyClass(mozilla::Move(toMove)) |
michael@0 | 693 | , mSharedBlobData(mozilla::Move(toMove.mSharedBlobData)) |
michael@0 | 694 | , mBlob(mozilla::Move(toMove.mBlob)) |
michael@0 | 695 | { |
michael@0 | 696 | toMove.mSharedBlobData = nullptr; |
michael@0 | 697 | toMove.mBlob = nullptr; |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | ~FontTableHashEntry() { Clear(); } |
michael@0 | 701 | |
michael@0 | 702 | // FontTable/Blob API |
michael@0 | 703 | |
michael@0 | 704 | // Transfer (not copy) elements of aTable to a new hb_blob_t and |
michael@0 | 705 | // return ownership to the caller. A weak reference to the blob is |
michael@0 | 706 | // recorded in the hashtable entry so that others may use the same |
michael@0 | 707 | // table. |
michael@0 | 708 | hb_blob_t * |
michael@0 | 709 | ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable, |
michael@0 | 710 | nsTHashtable<FontTableHashEntry> *aHashtable); |
michael@0 | 711 | |
michael@0 | 712 | // Return a strong reference to the blob. |
michael@0 | 713 | // Callers must hb_blob_destroy the returned blob. |
michael@0 | 714 | hb_blob_t *GetBlob() const; |
michael@0 | 715 | |
michael@0 | 716 | void Clear(); |
michael@0 | 717 | |
michael@0 | 718 | static size_t |
michael@0 | 719 | SizeOfEntryExcludingThis(FontTableHashEntry *aEntry, |
michael@0 | 720 | mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 721 | void* aUserArg); |
michael@0 | 722 | |
michael@0 | 723 | private: |
michael@0 | 724 | static void DeleteFontTableBlobData(void *aBlobData); |
michael@0 | 725 | // not implemented |
michael@0 | 726 | FontTableHashEntry& operator=(FontTableHashEntry& toCopy); |
michael@0 | 727 | |
michael@0 | 728 | FontTableBlobData *mSharedBlobData; |
michael@0 | 729 | hb_blob_t *mBlob; |
michael@0 | 730 | }; |
michael@0 | 731 | |
michael@0 | 732 | nsAutoPtr<nsTHashtable<FontTableHashEntry> > mFontTableCache; |
michael@0 | 733 | |
michael@0 | 734 | gfxFontEntry(const gfxFontEntry&); |
michael@0 | 735 | gfxFontEntry& operator=(const gfxFontEntry&); |
michael@0 | 736 | }; |
michael@0 | 737 | |
michael@0 | 738 | |
michael@0 | 739 | // used when iterating over all fonts looking for a match for a given character |
michael@0 | 740 | struct GlobalFontMatch { |
michael@0 | 741 | GlobalFontMatch(const uint32_t aCharacter, |
michael@0 | 742 | int32_t aRunScript, |
michael@0 | 743 | const gfxFontStyle *aStyle) : |
michael@0 | 744 | mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle), |
michael@0 | 745 | mMatchRank(0), mCount(0), mCmapsTested(0) |
michael@0 | 746 | { |
michael@0 | 747 | |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | const uint32_t mCh; // codepoint to be matched |
michael@0 | 751 | int32_t mRunScript; // Unicode script for the codepoint |
michael@0 | 752 | const gfxFontStyle* mStyle; // style to match |
michael@0 | 753 | int32_t mMatchRank; // metric indicating closest match |
michael@0 | 754 | nsRefPtr<gfxFontEntry> mBestMatch; // current best match |
michael@0 | 755 | nsRefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to |
michael@0 | 756 | uint32_t mCount; // number of fonts matched |
michael@0 | 757 | uint32_t mCmapsTested; // number of cmaps tested |
michael@0 | 758 | }; |
michael@0 | 759 | |
michael@0 | 760 | class gfxFontFamily { |
michael@0 | 761 | public: |
michael@0 | 762 | NS_INLINE_DECL_REFCOUNTING(gfxFontFamily) |
michael@0 | 763 | |
michael@0 | 764 | gfxFontFamily(const nsAString& aName) : |
michael@0 | 765 | mName(aName), |
michael@0 | 766 | mOtherFamilyNamesInitialized(false), |
michael@0 | 767 | mHasOtherFamilyNames(false), |
michael@0 | 768 | mFaceNamesInitialized(false), |
michael@0 | 769 | mHasStyles(false), |
michael@0 | 770 | mIsSimpleFamily(false), |
michael@0 | 771 | mIsBadUnderlineFamily(false), |
michael@0 | 772 | mFamilyCharacterMapInitialized(false), |
michael@0 | 773 | mSkipDefaultFeatureSpaceCheck(false) |
michael@0 | 774 | { } |
michael@0 | 775 | |
michael@0 | 776 | const nsString& Name() { return mName; } |
michael@0 | 777 | |
michael@0 | 778 | virtual void LocalizedName(nsAString& aLocalizedName); |
michael@0 | 779 | virtual bool HasOtherFamilyNames(); |
michael@0 | 780 | |
michael@0 | 781 | nsTArray<nsRefPtr<gfxFontEntry> >& GetFontList() { return mAvailableFonts; } |
michael@0 | 782 | |
michael@0 | 783 | void AddFontEntry(nsRefPtr<gfxFontEntry> aFontEntry) { |
michael@0 | 784 | // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces |
michael@0 | 785 | // of Times New Roman, because of buggy table in those fonts |
michael@0 | 786 | if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() && |
michael@0 | 787 | Name().EqualsLiteral("Times New Roman")) |
michael@0 | 788 | { |
michael@0 | 789 | aFontEntry->mIgnoreGDEF = true; |
michael@0 | 790 | } |
michael@0 | 791 | aFontEntry->mFamilyName = Name(); |
michael@0 | 792 | aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck; |
michael@0 | 793 | mAvailableFonts.AppendElement(aFontEntry); |
michael@0 | 794 | } |
michael@0 | 795 | |
michael@0 | 796 | // note that the styles for this family have been added |
michael@0 | 797 | bool HasStyles() { return mHasStyles; } |
michael@0 | 798 | void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; } |
michael@0 | 799 | |
michael@0 | 800 | // choose a specific face to match a style using CSS font matching |
michael@0 | 801 | // rules (weight matching occurs here). may return a face that doesn't |
michael@0 | 802 | // precisely match (e.g. normal face when no italic face exists). |
michael@0 | 803 | // aNeedsSyntheticBold is set to true when synthetic bolding is |
michael@0 | 804 | // needed, false otherwise |
michael@0 | 805 | gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, |
michael@0 | 806 | bool& aNeedsSyntheticBold); |
michael@0 | 807 | |
michael@0 | 808 | // checks for a matching font within the family |
michael@0 | 809 | // used as part of the font fallback process |
michael@0 | 810 | void FindFontForChar(GlobalFontMatch *aMatchData); |
michael@0 | 811 | |
michael@0 | 812 | // checks all fonts for a matching font within the family |
michael@0 | 813 | void SearchAllFontsForChar(GlobalFontMatch *aMatchData); |
michael@0 | 814 | |
michael@0 | 815 | // read in other family names, if any, and use functor to add each into cache |
michael@0 | 816 | virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList); |
michael@0 | 817 | |
michael@0 | 818 | // helper method for reading localized family names from the name table |
michael@0 | 819 | // of a single face |
michael@0 | 820 | static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName, |
michael@0 | 821 | const char *aNameData, |
michael@0 | 822 | uint32_t aDataLength, |
michael@0 | 823 | nsTArray<nsString>& aOtherFamilyNames, |
michael@0 | 824 | bool useFullName); |
michael@0 | 825 | |
michael@0 | 826 | // set when other family names have been read in |
michael@0 | 827 | void SetOtherFamilyNamesInitialized() { |
michael@0 | 828 | mOtherFamilyNamesInitialized = true; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | // read in other localized family names, fullnames and Postscript names |
michael@0 | 832 | // for all faces and append to lookup tables |
michael@0 | 833 | virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList, |
michael@0 | 834 | bool aNeedFullnamePostscriptNames, |
michael@0 | 835 | FontInfoData *aFontInfoData = nullptr); |
michael@0 | 836 | |
michael@0 | 837 | // find faces belonging to this family (platform implementations override this; |
michael@0 | 838 | // should be made pure virtual once all subclasses have been updated) |
michael@0 | 839 | virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { } |
michael@0 | 840 | |
michael@0 | 841 | // search for a specific face using the Postscript name |
michael@0 | 842 | gfxFontEntry* FindFont(const nsAString& aPostscriptName); |
michael@0 | 843 | |
michael@0 | 844 | // read in cmaps for all the faces |
michael@0 | 845 | void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr); |
michael@0 | 846 | |
michael@0 | 847 | bool TestCharacterMap(uint32_t aCh) { |
michael@0 | 848 | if (!mFamilyCharacterMapInitialized) { |
michael@0 | 849 | ReadAllCMAPs(); |
michael@0 | 850 | } |
michael@0 | 851 | return mFamilyCharacterMap.test(aCh); |
michael@0 | 852 | } |
michael@0 | 853 | |
michael@0 | 854 | void ResetCharacterMap() { |
michael@0 | 855 | mFamilyCharacterMap.reset(); |
michael@0 | 856 | mFamilyCharacterMapInitialized = false; |
michael@0 | 857 | } |
michael@0 | 858 | |
michael@0 | 859 | // mark this family as being in the "bad" underline offset blacklist |
michael@0 | 860 | void SetBadUnderlineFamily() { |
michael@0 | 861 | mIsBadUnderlineFamily = true; |
michael@0 | 862 | if (mHasStyles) { |
michael@0 | 863 | SetBadUnderlineFonts(); |
michael@0 | 864 | } |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; } |
michael@0 | 868 | |
michael@0 | 869 | // sort available fonts to put preferred (standard) faces towards the end |
michael@0 | 870 | void SortAvailableFonts(); |
michael@0 | 871 | |
michael@0 | 872 | // check whether the family fits into the simple 4-face model, |
michael@0 | 873 | // so we can use simplified style-matching; |
michael@0 | 874 | // if so set the mIsSimpleFamily flag (defaults to False before we've checked) |
michael@0 | 875 | void CheckForSimpleFamily(); |
michael@0 | 876 | |
michael@0 | 877 | // For memory reporter |
michael@0 | 878 | virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 879 | FontListSizes* aSizes) const; |
michael@0 | 880 | virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 881 | FontListSizes* aSizes) const; |
michael@0 | 882 | |
michael@0 | 883 | // Only used for debugging checks - does a linear search |
michael@0 | 884 | bool ContainsFace(gfxFontEntry* aFontEntry) { |
michael@0 | 885 | uint32_t i, numFonts = mAvailableFonts.Length(); |
michael@0 | 886 | for (i = 0; i < numFonts; i++) { |
michael@0 | 887 | if (mAvailableFonts[i] == aFontEntry) { |
michael@0 | 888 | return true; |
michael@0 | 889 | } |
michael@0 | 890 | } |
michael@0 | 891 | return false; |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | void SetSkipSpaceFeatureCheck(bool aSkipCheck) { |
michael@0 | 895 | mSkipDefaultFeatureSpaceCheck = aSkipCheck; |
michael@0 | 896 | } |
michael@0 | 897 | |
michael@0 | 898 | protected: |
michael@0 | 899 | // Protected destructor, to discourage deletion outside of Release(): |
michael@0 | 900 | virtual ~gfxFontFamily() |
michael@0 | 901 | { |
michael@0 | 902 | } |
michael@0 | 903 | |
michael@0 | 904 | // fills in an array with weights of faces that match style, |
michael@0 | 905 | // returns whether any matching entries found |
michael@0 | 906 | virtual bool FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], |
michael@0 | 907 | bool anItalic, int16_t aStretch); |
michael@0 | 908 | |
michael@0 | 909 | bool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList, |
michael@0 | 910 | hb_blob_t *aNameTable, |
michael@0 | 911 | bool useFullName = false); |
michael@0 | 912 | |
michael@0 | 913 | // set whether this font family is in "bad" underline offset blacklist. |
michael@0 | 914 | void SetBadUnderlineFonts() { |
michael@0 | 915 | uint32_t i, numFonts = mAvailableFonts.Length(); |
michael@0 | 916 | for (i = 0; i < numFonts; i++) { |
michael@0 | 917 | if (mAvailableFonts[i]) { |
michael@0 | 918 | mAvailableFonts[i]->mIsBadUnderlineFont = true; |
michael@0 | 919 | } |
michael@0 | 920 | } |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | nsString mName; |
michael@0 | 924 | nsTArray<nsRefPtr<gfxFontEntry> > mAvailableFonts; |
michael@0 | 925 | gfxSparseBitSet mFamilyCharacterMap; |
michael@0 | 926 | bool mOtherFamilyNamesInitialized : 1; |
michael@0 | 927 | bool mHasOtherFamilyNames : 1; |
michael@0 | 928 | bool mFaceNamesInitialized : 1; |
michael@0 | 929 | bool mHasStyles : 1; |
michael@0 | 930 | bool mIsSimpleFamily : 1; |
michael@0 | 931 | bool mIsBadUnderlineFamily : 1; |
michael@0 | 932 | bool mFamilyCharacterMapInitialized : 1; |
michael@0 | 933 | bool mSkipDefaultFeatureSpaceCheck : 1; |
michael@0 | 934 | |
michael@0 | 935 | enum { |
michael@0 | 936 | // for "simple" families, the faces are stored in mAvailableFonts |
michael@0 | 937 | // with fixed positions: |
michael@0 | 938 | kRegularFaceIndex = 0, |
michael@0 | 939 | kBoldFaceIndex = 1, |
michael@0 | 940 | kItalicFaceIndex = 2, |
michael@0 | 941 | kBoldItalicFaceIndex = 3, |
michael@0 | 942 | // mask values for selecting face with bold and/or italic attributes |
michael@0 | 943 | kBoldMask = 0x01, |
michael@0 | 944 | kItalicMask = 0x02 |
michael@0 | 945 | }; |
michael@0 | 946 | }; |
michael@0 | 947 | |
michael@0 | 948 | struct gfxTextRange { |
michael@0 | 949 | enum { |
michael@0 | 950 | // flags for recording the kind of font-matching that was used |
michael@0 | 951 | kFontGroup = 0x0001, |
michael@0 | 952 | kPrefsFallback = 0x0002, |
michael@0 | 953 | kSystemFallback = 0x0004 |
michael@0 | 954 | }; |
michael@0 | 955 | gfxTextRange(uint32_t aStart, uint32_t aEnd, |
michael@0 | 956 | gfxFont* aFont, uint8_t aMatchType) |
michael@0 | 957 | : start(aStart), |
michael@0 | 958 | end(aEnd), |
michael@0 | 959 | font(aFont), |
michael@0 | 960 | matchType(aMatchType) |
michael@0 | 961 | { } |
michael@0 | 962 | uint32_t Length() const { return end - start; } |
michael@0 | 963 | uint32_t start, end; |
michael@0 | 964 | nsRefPtr<gfxFont> font; |
michael@0 | 965 | uint8_t matchType; |
michael@0 | 966 | }; |
michael@0 | 967 | |
michael@0 | 968 | |
michael@0 | 969 | /** |
michael@0 | 970 | * Font cache design: |
michael@0 | 971 | * |
michael@0 | 972 | * The mFonts hashtable contains most fonts, indexed by (gfxFontEntry*, style). |
michael@0 | 973 | * It does not add a reference to the fonts it contains. |
michael@0 | 974 | * When a font's refcount decreases to zero, instead of deleting it we |
michael@0 | 975 | * add it to our expiration tracker. |
michael@0 | 976 | * The expiration tracker tracks fonts with zero refcount. After a certain |
michael@0 | 977 | * period of time, such fonts expire and are deleted. |
michael@0 | 978 | * |
michael@0 | 979 | * We're using 3 generations with a ten-second generation interval, so |
michael@0 | 980 | * zero-refcount fonts will be deleted 20-30 seconds after their refcount |
michael@0 | 981 | * goes to zero, if timer events fire in a timely manner. |
michael@0 | 982 | * |
michael@0 | 983 | * The font cache also handles timed expiration of cached ShapedWords |
michael@0 | 984 | * for "persistent" fonts: it has a repeating timer, and notifies |
michael@0 | 985 | * each cached font to "age" its shaped words. The words will be released |
michael@0 | 986 | * by the fonts if they get aged three times without being re-used in the |
michael@0 | 987 | * meantime. |
michael@0 | 988 | * |
michael@0 | 989 | * Note that the ShapedWord timeout is much larger than the font timeout, |
michael@0 | 990 | * so that in the case of a short-lived font, we'll discard the gfxFont |
michael@0 | 991 | * completely, with all its words, and avoid the cost of aging the words |
michael@0 | 992 | * individually. That only happens with longer-lived fonts. |
michael@0 | 993 | */ |
michael@0 | 994 | struct FontCacheSizes { |
michael@0 | 995 | FontCacheSizes() |
michael@0 | 996 | : mFontInstances(0), mShapedWords(0) |
michael@0 | 997 | { } |
michael@0 | 998 | |
michael@0 | 999 | size_t mFontInstances; // memory used by instances of gfxFont subclasses |
michael@0 | 1000 | size_t mShapedWords; // memory used by the per-font shapedWord caches |
michael@0 | 1001 | }; |
michael@0 | 1002 | |
michael@0 | 1003 | class gfxFontCache MOZ_FINAL : public nsExpirationTracker<gfxFont,3> { |
michael@0 | 1004 | public: |
michael@0 | 1005 | enum { |
michael@0 | 1006 | FONT_TIMEOUT_SECONDS = 10, |
michael@0 | 1007 | SHAPED_WORD_TIMEOUT_SECONDS = 60 |
michael@0 | 1008 | }; |
michael@0 | 1009 | |
michael@0 | 1010 | gfxFontCache(); |
michael@0 | 1011 | ~gfxFontCache(); |
michael@0 | 1012 | |
michael@0 | 1013 | /* |
michael@0 | 1014 | * Get the global gfxFontCache. You must call Init() before |
michael@0 | 1015 | * calling this method --- the result will not be null. |
michael@0 | 1016 | */ |
michael@0 | 1017 | static gfxFontCache* GetCache() { |
michael@0 | 1018 | return gGlobalCache; |
michael@0 | 1019 | } |
michael@0 | 1020 | |
michael@0 | 1021 | static nsresult Init(); |
michael@0 | 1022 | // It's OK to call this even if Init() has not been called. |
michael@0 | 1023 | static void Shutdown(); |
michael@0 | 1024 | |
michael@0 | 1025 | // Look up a font in the cache. Returns an addrefed pointer, or null |
michael@0 | 1026 | // if there's nothing matching in the cache |
michael@0 | 1027 | already_AddRefed<gfxFont> Lookup(const gfxFontEntry *aFontEntry, |
michael@0 | 1028 | const gfxFontStyle *aStyle); |
michael@0 | 1029 | // We created a new font (presumably because Lookup returned null); |
michael@0 | 1030 | // put it in the cache. The font's refcount should be nonzero. It is |
michael@0 | 1031 | // allowable to add a new font even if there is one already in the |
michael@0 | 1032 | // cache with the same key; we'll forget about the old one. |
michael@0 | 1033 | void AddNew(gfxFont *aFont); |
michael@0 | 1034 | |
michael@0 | 1035 | // The font's refcount has gone to zero; give ownership of it to |
michael@0 | 1036 | // the cache. We delete it if it's not acquired again after a certain |
michael@0 | 1037 | // amount of time. |
michael@0 | 1038 | void NotifyReleased(gfxFont *aFont); |
michael@0 | 1039 | |
michael@0 | 1040 | // This gets called when the timeout has expired on a zero-refcount |
michael@0 | 1041 | // font; we just delete it. |
michael@0 | 1042 | virtual void NotifyExpired(gfxFont *aFont); |
michael@0 | 1043 | |
michael@0 | 1044 | // Cleans out the hashtable and removes expired fonts waiting for cleanup. |
michael@0 | 1045 | // Other gfxFont objects may be still in use but they will be pushed |
michael@0 | 1046 | // into the expiration queues and removed. |
michael@0 | 1047 | void Flush() { |
michael@0 | 1048 | mFonts.Clear(); |
michael@0 | 1049 | AgeAllGenerations(); |
michael@0 | 1050 | } |
michael@0 | 1051 | |
michael@0 | 1052 | void FlushShapedWordCaches() { |
michael@0 | 1053 | mFonts.EnumerateEntries(ClearCachedWordsForFont, nullptr); |
michael@0 | 1054 | } |
michael@0 | 1055 | |
michael@0 | 1056 | void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 1057 | FontCacheSizes* aSizes) const; |
michael@0 | 1058 | void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 1059 | FontCacheSizes* aSizes) const; |
michael@0 | 1060 | |
michael@0 | 1061 | protected: |
michael@0 | 1062 | class MemoryReporter MOZ_FINAL : public nsIMemoryReporter |
michael@0 | 1063 | { |
michael@0 | 1064 | public: |
michael@0 | 1065 | NS_DECL_ISUPPORTS |
michael@0 | 1066 | NS_DECL_NSIMEMORYREPORTER |
michael@0 | 1067 | }; |
michael@0 | 1068 | |
michael@0 | 1069 | // Observer for notifications that the font cache cares about |
michael@0 | 1070 | class Observer MOZ_FINAL |
michael@0 | 1071 | : public nsIObserver |
michael@0 | 1072 | { |
michael@0 | 1073 | public: |
michael@0 | 1074 | NS_DECL_ISUPPORTS |
michael@0 | 1075 | NS_DECL_NSIOBSERVER |
michael@0 | 1076 | }; |
michael@0 | 1077 | |
michael@0 | 1078 | void DestroyFont(gfxFont *aFont); |
michael@0 | 1079 | |
michael@0 | 1080 | static gfxFontCache *gGlobalCache; |
michael@0 | 1081 | |
michael@0 | 1082 | struct Key { |
michael@0 | 1083 | const gfxFontEntry* mFontEntry; |
michael@0 | 1084 | const gfxFontStyle* mStyle; |
michael@0 | 1085 | Key(const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle) |
michael@0 | 1086 | : mFontEntry(aFontEntry), mStyle(aStyle) {} |
michael@0 | 1087 | }; |
michael@0 | 1088 | |
michael@0 | 1089 | class HashEntry : public PLDHashEntryHdr { |
michael@0 | 1090 | public: |
michael@0 | 1091 | typedef const Key& KeyType; |
michael@0 | 1092 | typedef const Key* KeyTypePointer; |
michael@0 | 1093 | |
michael@0 | 1094 | // When constructing a new entry in the hashtable, we'll leave this |
michael@0 | 1095 | // blank. The caller of Put() will fill this in. |
michael@0 | 1096 | HashEntry(KeyTypePointer aStr) : mFont(nullptr) { } |
michael@0 | 1097 | HashEntry(const HashEntry& toCopy) : mFont(toCopy.mFont) { } |
michael@0 | 1098 | ~HashEntry() { } |
michael@0 | 1099 | |
michael@0 | 1100 | bool KeyEquals(const KeyTypePointer aKey) const; |
michael@0 | 1101 | static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } |
michael@0 | 1102 | static PLDHashNumber HashKey(const KeyTypePointer aKey) { |
michael@0 | 1103 | return mozilla::HashGeneric(aKey->mStyle->Hash(), aKey->mFontEntry); |
michael@0 | 1104 | } |
michael@0 | 1105 | enum { ALLOW_MEMMOVE = true }; |
michael@0 | 1106 | |
michael@0 | 1107 | gfxFont* mFont; |
michael@0 | 1108 | }; |
michael@0 | 1109 | |
michael@0 | 1110 | static size_t AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry, |
michael@0 | 1111 | mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 1112 | void* aUserArg); |
michael@0 | 1113 | |
michael@0 | 1114 | nsTHashtable<HashEntry> mFonts; |
michael@0 | 1115 | |
michael@0 | 1116 | static PLDHashOperator ClearCachedWordsForFont(HashEntry* aHashEntry, void*); |
michael@0 | 1117 | static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*); |
michael@0 | 1118 | static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache); |
michael@0 | 1119 | nsCOMPtr<nsITimer> mWordCacheExpirationTimer; |
michael@0 | 1120 | }; |
michael@0 | 1121 | |
michael@0 | 1122 | class gfxTextPerfMetrics { |
michael@0 | 1123 | public: |
michael@0 | 1124 | |
michael@0 | 1125 | struct TextCounts { |
michael@0 | 1126 | uint32_t numContentTextRuns; |
michael@0 | 1127 | uint32_t numChromeTextRuns; |
michael@0 | 1128 | uint32_t numChars; |
michael@0 | 1129 | uint32_t maxTextRunLen; |
michael@0 | 1130 | uint32_t wordCacheSpaceRules; |
michael@0 | 1131 | uint32_t wordCacheLong; |
michael@0 | 1132 | uint32_t wordCacheHit; |
michael@0 | 1133 | uint32_t wordCacheMiss; |
michael@0 | 1134 | uint32_t fallbackPrefs; |
michael@0 | 1135 | uint32_t fallbackSystem; |
michael@0 | 1136 | uint32_t textrunConst; |
michael@0 | 1137 | uint32_t textrunDestr; |
michael@0 | 1138 | }; |
michael@0 | 1139 | |
michael@0 | 1140 | uint32_t reflowCount; |
michael@0 | 1141 | |
michael@0 | 1142 | // counts per reflow operation |
michael@0 | 1143 | TextCounts current; |
michael@0 | 1144 | |
michael@0 | 1145 | // totals for the lifetime of a document |
michael@0 | 1146 | TextCounts cumulative; |
michael@0 | 1147 | |
michael@0 | 1148 | gfxTextPerfMetrics() { |
michael@0 | 1149 | memset(this, 0, sizeof(gfxTextPerfMetrics)); |
michael@0 | 1150 | } |
michael@0 | 1151 | |
michael@0 | 1152 | // add current totals to cumulative ones |
michael@0 | 1153 | void Accumulate() { |
michael@0 | 1154 | if (current.numChars == 0) { |
michael@0 | 1155 | return; |
michael@0 | 1156 | } |
michael@0 | 1157 | cumulative.numContentTextRuns += current.numContentTextRuns; |
michael@0 | 1158 | cumulative.numChromeTextRuns += current.numChromeTextRuns; |
michael@0 | 1159 | cumulative.numChars += current.numChars; |
michael@0 | 1160 | if (current.maxTextRunLen > cumulative.maxTextRunLen) { |
michael@0 | 1161 | cumulative.maxTextRunLen = current.maxTextRunLen; |
michael@0 | 1162 | } |
michael@0 | 1163 | cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules; |
michael@0 | 1164 | cumulative.wordCacheLong += current.wordCacheLong; |
michael@0 | 1165 | cumulative.wordCacheHit += current.wordCacheHit; |
michael@0 | 1166 | cumulative.wordCacheMiss += current.wordCacheMiss; |
michael@0 | 1167 | cumulative.fallbackPrefs += current.fallbackPrefs; |
michael@0 | 1168 | cumulative.fallbackSystem += current.fallbackSystem; |
michael@0 | 1169 | cumulative.textrunConst += current.textrunConst; |
michael@0 | 1170 | cumulative.textrunDestr += current.textrunDestr; |
michael@0 | 1171 | memset(¤t, 0, sizeof(current)); |
michael@0 | 1172 | } |
michael@0 | 1173 | }; |
michael@0 | 1174 | |
michael@0 | 1175 | class gfxTextRunFactory { |
michael@0 | 1176 | NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory) |
michael@0 | 1177 | |
michael@0 | 1178 | public: |
michael@0 | 1179 | // Flags in the mask 0xFFFF0000 are reserved for textrun clients |
michael@0 | 1180 | // Flags in the mask 0x0000F000 are reserved for per-platform fonts |
michael@0 | 1181 | // Flags in the mask 0x00000FFF are set by the textrun creator. |
michael@0 | 1182 | enum { |
michael@0 | 1183 | CACHE_TEXT_FLAGS = 0xF0000000, |
michael@0 | 1184 | USER_TEXT_FLAGS = 0x0FFF0000, |
michael@0 | 1185 | PLATFORM_TEXT_FLAGS = 0x0000F000, |
michael@0 | 1186 | TEXTRUN_TEXT_FLAGS = 0x00000FFF, |
michael@0 | 1187 | SETTABLE_FLAGS = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS, |
michael@0 | 1188 | |
michael@0 | 1189 | /** |
michael@0 | 1190 | * When set, the text string pointer used to create the text run |
michael@0 | 1191 | * is guaranteed to be available during the lifetime of the text run. |
michael@0 | 1192 | */ |
michael@0 | 1193 | TEXT_IS_PERSISTENT = 0x0001, |
michael@0 | 1194 | /** |
michael@0 | 1195 | * When set, the text is known to be all-ASCII (< 128). |
michael@0 | 1196 | */ |
michael@0 | 1197 | TEXT_IS_ASCII = 0x0002, |
michael@0 | 1198 | /** |
michael@0 | 1199 | * When set, the text is RTL. |
michael@0 | 1200 | */ |
michael@0 | 1201 | TEXT_IS_RTL = 0x0004, |
michael@0 | 1202 | /** |
michael@0 | 1203 | * When set, spacing is enabled and the textrun needs to call GetSpacing |
michael@0 | 1204 | * on the spacing provider. |
michael@0 | 1205 | */ |
michael@0 | 1206 | TEXT_ENABLE_SPACING = 0x0008, |
michael@0 | 1207 | /** |
michael@0 | 1208 | * When set, GetHyphenationBreaks may return true for some character |
michael@0 | 1209 | * positions, otherwise it will always return false for all characters. |
michael@0 | 1210 | */ |
michael@0 | 1211 | TEXT_ENABLE_HYPHEN_BREAKS = 0x0010, |
michael@0 | 1212 | /** |
michael@0 | 1213 | * When set, the text has no characters above 255 and it is stored |
michael@0 | 1214 | * in the textrun in 8-bit format. |
michael@0 | 1215 | */ |
michael@0 | 1216 | TEXT_IS_8BIT = 0x0020, |
michael@0 | 1217 | /** |
michael@0 | 1218 | * When set, the RunMetrics::mBoundingBox field will be initialized |
michael@0 | 1219 | * properly based on glyph extents, in particular, glyph extents that |
michael@0 | 1220 | * overflow the standard font-box (the box defined by the ascent, descent |
michael@0 | 1221 | * and advance width of the glyph). When not set, it may just be the |
michael@0 | 1222 | * standard font-box even if glyphs overflow. |
michael@0 | 1223 | */ |
michael@0 | 1224 | TEXT_NEED_BOUNDING_BOX = 0x0040, |
michael@0 | 1225 | /** |
michael@0 | 1226 | * When set, optional ligatures are disabled. Ligatures that are |
michael@0 | 1227 | * required for legible text should still be enabled. |
michael@0 | 1228 | */ |
michael@0 | 1229 | TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080, |
michael@0 | 1230 | /** |
michael@0 | 1231 | * When set, the textrun should favour speed of construction over |
michael@0 | 1232 | * quality. This may involve disabling ligatures and/or kerning or |
michael@0 | 1233 | * other effects. |
michael@0 | 1234 | */ |
michael@0 | 1235 | TEXT_OPTIMIZE_SPEED = 0x0100, |
michael@0 | 1236 | /** |
michael@0 | 1237 | * For internal use by the memory reporter when accounting for |
michael@0 | 1238 | * storage used by textruns. |
michael@0 | 1239 | * Because the reporter may visit each textrun multiple times while |
michael@0 | 1240 | * walking the frame trees and textrun cache, it needs to mark |
michael@0 | 1241 | * textruns that have been seen so as to avoid multiple-accounting. |
michael@0 | 1242 | */ |
michael@0 | 1243 | TEXT_RUN_SIZE_ACCOUNTED = 0x0200, |
michael@0 | 1244 | /** |
michael@0 | 1245 | * When set, the textrun should discard control characters instead of |
michael@0 | 1246 | * turning them into hexboxes. |
michael@0 | 1247 | */ |
michael@0 | 1248 | TEXT_HIDE_CONTROL_CHARACTERS = 0x0400, |
michael@0 | 1249 | |
michael@0 | 1250 | /** |
michael@0 | 1251 | * nsTextFrameThebes sets these, but they're defined here rather than |
michael@0 | 1252 | * in nsTextFrameUtils.h because ShapedWord creation/caching also needs |
michael@0 | 1253 | * to check the _INCOMING flag |
michael@0 | 1254 | */ |
michael@0 | 1255 | TEXT_TRAILING_ARABICCHAR = 0x20000000, |
michael@0 | 1256 | /** |
michael@0 | 1257 | * When set, the previous character for this textrun was an Arabic |
michael@0 | 1258 | * character. This is used for the context detection necessary for |
michael@0 | 1259 | * bidi.numeral implementation. |
michael@0 | 1260 | */ |
michael@0 | 1261 | TEXT_INCOMING_ARABICCHAR = 0x40000000, |
michael@0 | 1262 | |
michael@0 | 1263 | // Set if the textrun should use the OpenType 'math' script. |
michael@0 | 1264 | TEXT_USE_MATH_SCRIPT = 0x80000000, |
michael@0 | 1265 | |
michael@0 | 1266 | TEXT_UNUSED_FLAGS = 0x10000000 |
michael@0 | 1267 | }; |
michael@0 | 1268 | |
michael@0 | 1269 | /** |
michael@0 | 1270 | * This record contains all the parameters needed to initialize a textrun. |
michael@0 | 1271 | */ |
michael@0 | 1272 | struct Parameters { |
michael@0 | 1273 | // A reference context suggesting where the textrun will be rendered |
michael@0 | 1274 | gfxContext *mContext; |
michael@0 | 1275 | // Pointer to arbitrary user data (which should outlive the textrun) |
michael@0 | 1276 | void *mUserData; |
michael@0 | 1277 | // A description of which characters have been stripped from the original |
michael@0 | 1278 | // DOM string to produce the characters in the textrun. May be null |
michael@0 | 1279 | // if that information is not relevant. |
michael@0 | 1280 | gfxSkipChars *mSkipChars; |
michael@0 | 1281 | // A list of where linebreaks are currently placed in the textrun. May |
michael@0 | 1282 | // be null if mInitialBreakCount is zero. |
michael@0 | 1283 | uint32_t *mInitialBreaks; |
michael@0 | 1284 | uint32_t mInitialBreakCount; |
michael@0 | 1285 | // The ratio to use to convert device pixels to application layout units |
michael@0 | 1286 | int32_t mAppUnitsPerDevUnit; |
michael@0 | 1287 | }; |
michael@0 | 1288 | |
michael@0 | 1289 | protected: |
michael@0 | 1290 | // Protected destructor, to discourage deletion outside of Release(): |
michael@0 | 1291 | virtual ~gfxTextRunFactory() {} |
michael@0 | 1292 | }; |
michael@0 | 1293 | |
michael@0 | 1294 | /** |
michael@0 | 1295 | * This stores glyph bounds information for a particular gfxFont, at |
michael@0 | 1296 | * a particular appunits-per-dev-pixel ratio (because the compressed glyph |
michael@0 | 1297 | * width array is stored in appunits). |
michael@0 | 1298 | * |
michael@0 | 1299 | * We store a hashtable from glyph IDs to float bounding rects. For the |
michael@0 | 1300 | * common case where the glyph has no horizontal left bearing, and no |
michael@0 | 1301 | * y overflow above the font ascent or below the font descent, and tight |
michael@0 | 1302 | * bounding boxes are not required, we avoid storing the glyph ID in the hashtable |
michael@0 | 1303 | * and instead consult an array of 16-bit glyph XMost values (in appunits). |
michael@0 | 1304 | * This array always has an entry for the font's space glyph --- the width is |
michael@0 | 1305 | * assumed to be zero. |
michael@0 | 1306 | */ |
michael@0 | 1307 | class gfxGlyphExtents { |
michael@0 | 1308 | public: |
michael@0 | 1309 | gfxGlyphExtents(int32_t aAppUnitsPerDevUnit) : |
michael@0 | 1310 | mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) { |
michael@0 | 1311 | MOZ_COUNT_CTOR(gfxGlyphExtents); |
michael@0 | 1312 | } |
michael@0 | 1313 | ~gfxGlyphExtents(); |
michael@0 | 1314 | |
michael@0 | 1315 | enum { INVALID_WIDTH = 0xFFFF }; |
michael@0 | 1316 | |
michael@0 | 1317 | void NotifyGlyphsChanged() { |
michael@0 | 1318 | mTightGlyphExtents.Clear(); |
michael@0 | 1319 | } |
michael@0 | 1320 | |
michael@0 | 1321 | // returns INVALID_WIDTH => not a contained glyph |
michael@0 | 1322 | // Otherwise the glyph has no before-bearing or vertical bearings, |
michael@0 | 1323 | // and the result is its width measured from the baseline origin, in |
michael@0 | 1324 | // appunits. |
michael@0 | 1325 | uint16_t GetContainedGlyphWidthAppUnits(uint32_t aGlyphID) const { |
michael@0 | 1326 | return mContainedGlyphWidths.Get(aGlyphID); |
michael@0 | 1327 | } |
michael@0 | 1328 | |
michael@0 | 1329 | bool IsGlyphKnown(uint32_t aGlyphID) const { |
michael@0 | 1330 | return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH || |
michael@0 | 1331 | mTightGlyphExtents.GetEntry(aGlyphID) != nullptr; |
michael@0 | 1332 | } |
michael@0 | 1333 | |
michael@0 | 1334 | bool IsGlyphKnownWithTightExtents(uint32_t aGlyphID) const { |
michael@0 | 1335 | return mTightGlyphExtents.GetEntry(aGlyphID) != nullptr; |
michael@0 | 1336 | } |
michael@0 | 1337 | |
michael@0 | 1338 | // Get glyph extents; a rectangle relative to the left baseline origin |
michael@0 | 1339 | // Returns true on success. Can fail on OOM or when aContext is null |
michael@0 | 1340 | // and extents were not (successfully) prefetched. |
michael@0 | 1341 | bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext, |
michael@0 | 1342 | uint32_t aGlyphID, gfxRect *aExtents); |
michael@0 | 1343 | |
michael@0 | 1344 | void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) { |
michael@0 | 1345 | mContainedGlyphWidths.Set(aGlyphID, aWidth); |
michael@0 | 1346 | } |
michael@0 | 1347 | void SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits); |
michael@0 | 1348 | |
michael@0 | 1349 | int32_t GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; } |
michael@0 | 1350 | |
michael@0 | 1351 | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 1352 | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 1353 | |
michael@0 | 1354 | private: |
michael@0 | 1355 | class HashEntry : public nsUint32HashKey { |
michael@0 | 1356 | public: |
michael@0 | 1357 | // When constructing a new entry in the hashtable, we'll leave this |
michael@0 | 1358 | // blank. The caller of Put() will fill this in. |
michael@0 | 1359 | HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {} |
michael@0 | 1360 | HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) { |
michael@0 | 1361 | x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height; |
michael@0 | 1362 | } |
michael@0 | 1363 | |
michael@0 | 1364 | float x, y, width, height; |
michael@0 | 1365 | }; |
michael@0 | 1366 | |
michael@0 | 1367 | enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks |
michael@0 | 1368 | |
michael@0 | 1369 | class GlyphWidths { |
michael@0 | 1370 | public: |
michael@0 | 1371 | void Set(uint32_t aIndex, uint16_t aValue); |
michael@0 | 1372 | uint16_t Get(uint32_t aIndex) const { |
michael@0 | 1373 | uint32_t block = aIndex >> BLOCK_SIZE_BITS; |
michael@0 | 1374 | if (block >= mBlocks.Length()) |
michael@0 | 1375 | return INVALID_WIDTH; |
michael@0 | 1376 | uintptr_t bits = mBlocks[block]; |
michael@0 | 1377 | if (!bits) |
michael@0 | 1378 | return INVALID_WIDTH; |
michael@0 | 1379 | uint32_t indexInBlock = aIndex & (BLOCK_SIZE - 1); |
michael@0 | 1380 | if (bits & 0x1) { |
michael@0 | 1381 | if (GetGlyphOffset(bits) != indexInBlock) |
michael@0 | 1382 | return INVALID_WIDTH; |
michael@0 | 1383 | return GetWidth(bits); |
michael@0 | 1384 | } |
michael@0 | 1385 | uint16_t *widths = reinterpret_cast<uint16_t *>(bits); |
michael@0 | 1386 | return widths[indexInBlock]; |
michael@0 | 1387 | } |
michael@0 | 1388 | |
michael@0 | 1389 | uint32_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
michael@0 | 1390 | |
michael@0 | 1391 | ~GlyphWidths(); |
michael@0 | 1392 | |
michael@0 | 1393 | private: |
michael@0 | 1394 | static uint32_t GetGlyphOffset(uintptr_t aBits) { |
michael@0 | 1395 | NS_ASSERTION(aBits & 0x1, "This is really a pointer..."); |
michael@0 | 1396 | return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1); |
michael@0 | 1397 | } |
michael@0 | 1398 | static uint32_t GetWidth(uintptr_t aBits) { |
michael@0 | 1399 | NS_ASSERTION(aBits & 0x1, "This is really a pointer..."); |
michael@0 | 1400 | return aBits >> (1 + BLOCK_SIZE_BITS); |
michael@0 | 1401 | } |
michael@0 | 1402 | static uintptr_t MakeSingle(uint32_t aGlyphOffset, uint16_t aWidth) { |
michael@0 | 1403 | return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1; |
michael@0 | 1404 | } |
michael@0 | 1405 | |
michael@0 | 1406 | nsTArray<uintptr_t> mBlocks; |
michael@0 | 1407 | }; |
michael@0 | 1408 | |
michael@0 | 1409 | GlyphWidths mContainedGlyphWidths; |
michael@0 | 1410 | nsTHashtable<HashEntry> mTightGlyphExtents; |
michael@0 | 1411 | int32_t mAppUnitsPerDevUnit; |
michael@0 | 1412 | |
michael@0 | 1413 | private: |
michael@0 | 1414 | // not implemented: |
michael@0 | 1415 | gfxGlyphExtents(const gfxGlyphExtents& aOther) MOZ_DELETE; |
michael@0 | 1416 | gfxGlyphExtents& operator=(const gfxGlyphExtents& aOther) MOZ_DELETE; |
michael@0 | 1417 | }; |
michael@0 | 1418 | |
michael@0 | 1419 | /** |
michael@0 | 1420 | * gfxFontShaper |
michael@0 | 1421 | * |
michael@0 | 1422 | * This class implements text shaping (character to glyph mapping and |
michael@0 | 1423 | * glyph layout). There is a gfxFontShaper subclass for each text layout |
michael@0 | 1424 | * technology (uniscribe, core text, harfbuzz,....) we support. |
michael@0 | 1425 | * |
michael@0 | 1426 | * The shaper is responsible for setting up glyph data in gfxTextRuns. |
michael@0 | 1427 | * |
michael@0 | 1428 | * A generic, platform-independent shaper relies only on the standard |
michael@0 | 1429 | * gfxFont interface and can work with any concrete subclass of gfxFont. |
michael@0 | 1430 | * |
michael@0 | 1431 | * Platform-specific implementations designed to interface to platform |
michael@0 | 1432 | * shaping APIs such as Uniscribe or CoreText may rely on features of a |
michael@0 | 1433 | * specific font subclass to access native font references |
michael@0 | 1434 | * (such as CTFont, HFONT, DWriteFont, etc). |
michael@0 | 1435 | */ |
michael@0 | 1436 | |
michael@0 | 1437 | class gfxFontShaper { |
michael@0 | 1438 | public: |
michael@0 | 1439 | gfxFontShaper(gfxFont *aFont) |
michael@0 | 1440 | : mFont(aFont) |
michael@0 | 1441 | { |
michael@0 | 1442 | NS_ASSERTION(aFont, "shaper requires a valid font!"); |
michael@0 | 1443 | } |
michael@0 | 1444 | |
michael@0 | 1445 | virtual ~gfxFontShaper() { } |
michael@0 | 1446 | |
michael@0 | 1447 | // Shape a piece of text and store the resulting glyph data into |
michael@0 | 1448 | // aShapedText. Parameters aOffset/aLength indicate the range of |
michael@0 | 1449 | // aShapedText to be updated; aLength is also the length of aText. |
michael@0 | 1450 | virtual bool ShapeText(gfxContext *aContext, |
michael@0 | 1451 | const char16_t *aText, |
michael@0 | 1452 | uint32_t aOffset, |
michael@0 | 1453 | uint32_t aLength, |
michael@0 | 1454 | int32_t aScript, |
michael@0 | 1455 | gfxShapedText *aShapedText) = 0; |
michael@0 | 1456 | |
michael@0 | 1457 | gfxFont *GetFont() const { return mFont; } |
michael@0 | 1458 | |
michael@0 | 1459 | // returns true if features exist in output, false otherwise |
michael@0 | 1460 | static bool |
michael@0 | 1461 | MergeFontFeatures(const gfxFontStyle *aStyle, |
michael@0 | 1462 | const nsTArray<gfxFontFeature>& aFontFeatures, |
michael@0 | 1463 | bool aDisableLigatures, |
michael@0 | 1464 | const nsAString& aFamilyName, |
michael@0 | 1465 | nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures); |
michael@0 | 1466 | |
michael@0 | 1467 | protected: |
michael@0 | 1468 | // the font this shaper is working with |
michael@0 | 1469 | gfxFont * mFont; |
michael@0 | 1470 | }; |
michael@0 | 1471 | |
michael@0 | 1472 | /* a SPECIFIC single font family */ |
michael@0 | 1473 | class gfxFont { |
michael@0 | 1474 | |
michael@0 | 1475 | friend class gfxHarfBuzzShaper; |
michael@0 | 1476 | friend class gfxGraphiteShaper; |
michael@0 | 1477 | |
michael@0 | 1478 | public: |
michael@0 | 1479 | nsrefcnt AddRef(void) { |
michael@0 | 1480 | NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); |
michael@0 | 1481 | if (mExpirationState.IsTracked()) { |
michael@0 | 1482 | gfxFontCache::GetCache()->RemoveObject(this); |
michael@0 | 1483 | } |
michael@0 | 1484 | ++mRefCnt; |
michael@0 | 1485 | NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this)); |
michael@0 | 1486 | return mRefCnt; |
michael@0 | 1487 | } |
michael@0 | 1488 | nsrefcnt Release(void) { |
michael@0 | 1489 | NS_PRECONDITION(0 != mRefCnt, "dup release"); |
michael@0 | 1490 | --mRefCnt; |
michael@0 | 1491 | NS_LOG_RELEASE(this, mRefCnt, "gfxFont"); |
michael@0 | 1492 | if (mRefCnt == 0) { |
michael@0 | 1493 | NotifyReleased(); |
michael@0 | 1494 | // |this| may have been deleted. |
michael@0 | 1495 | return 0; |
michael@0 | 1496 | } |
michael@0 | 1497 | return mRefCnt; |
michael@0 | 1498 | } |
michael@0 | 1499 | |
michael@0 | 1500 | int32_t GetRefCount() { return mRefCnt; } |
michael@0 | 1501 | |
michael@0 | 1502 | // options to specify the kind of AA to be used when creating a font |
michael@0 | 1503 | typedef enum { |
michael@0 | 1504 | kAntialiasDefault, |
michael@0 | 1505 | kAntialiasNone, |
michael@0 | 1506 | kAntialiasGrayscale, |
michael@0 | 1507 | kAntialiasSubpixel |
michael@0 | 1508 | } AntialiasOption; |
michael@0 | 1509 | |
michael@0 | 1510 | protected: |
michael@0 | 1511 | nsAutoRefCnt mRefCnt; |
michael@0 | 1512 | cairo_scaled_font_t *mScaledFont; |
michael@0 | 1513 | |
michael@0 | 1514 | void NotifyReleased() { |
michael@0 | 1515 | gfxFontCache *cache = gfxFontCache::GetCache(); |
michael@0 | 1516 | if (cache) { |
michael@0 | 1517 | // Don't delete just yet; return the object to the cache for |
michael@0 | 1518 | // possibly recycling within some time limit |
michael@0 | 1519 | cache->NotifyReleased(this); |
michael@0 | 1520 | } else { |
michael@0 | 1521 | // The cache may have already been shut down. |
michael@0 | 1522 | delete this; |
michael@0 | 1523 | } |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle, |
michael@0 | 1527 | AntialiasOption anAAOption = kAntialiasDefault, |
michael@0 | 1528 | cairo_scaled_font_t *aScaledFont = nullptr); |
michael@0 | 1529 | |
michael@0 | 1530 | public: |
michael@0 | 1531 | virtual ~gfxFont(); |
michael@0 | 1532 | |
michael@0 | 1533 | bool Valid() const { |
michael@0 | 1534 | return mIsValid; |
michael@0 | 1535 | } |
michael@0 | 1536 | |
michael@0 | 1537 | // options for the kind of bounding box to return from measurement |
michael@0 | 1538 | typedef enum { |
michael@0 | 1539 | LOOSE_INK_EXTENTS, |
michael@0 | 1540 | // A box that encloses all the painted pixels, and may |
michael@0 | 1541 | // include sidebearings and/or additional ascent/descent |
michael@0 | 1542 | // within the glyph cell even if the ink is smaller. |
michael@0 | 1543 | TIGHT_INK_EXTENTS, |
michael@0 | 1544 | // A box that tightly encloses all the painted pixels |
michael@0 | 1545 | // (although actually on Windows, at least, it may be |
michael@0 | 1546 | // slightly larger than strictly necessary because |
michael@0 | 1547 | // we can't get precise extents with ClearType). |
michael@0 | 1548 | TIGHT_HINTED_OUTLINE_EXTENTS |
michael@0 | 1549 | // A box that tightly encloses the glyph outline, |
michael@0 | 1550 | // ignoring possible antialiasing pixels that extend |
michael@0 | 1551 | // beyond this. |
michael@0 | 1552 | // NOTE: The default implementation of gfxFont::Measure(), |
michael@0 | 1553 | // which works with the glyph extents cache, does not |
michael@0 | 1554 | // differentiate between this and TIGHT_INK_EXTENTS. |
michael@0 | 1555 | // Whether the distinction is important depends on the |
michael@0 | 1556 | // antialiasing behavior of the platform; currently the |
michael@0 | 1557 | // distinction is only implemented in the gfxWindowsFont |
michael@0 | 1558 | // subclass, because of ClearType's tendency to paint |
michael@0 | 1559 | // outside the hinted outline. |
michael@0 | 1560 | // Also NOTE: it is relatively expensive to request this, |
michael@0 | 1561 | // as it does not use cached glyph extents in the font. |
michael@0 | 1562 | } BoundingBoxType; |
michael@0 | 1563 | |
michael@0 | 1564 | const nsString& GetName() const { return mFontEntry->Name(); } |
michael@0 | 1565 | const gfxFontStyle *GetStyle() const { return &mStyle; } |
michael@0 | 1566 | |
michael@0 | 1567 | virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; } |
michael@0 | 1568 | |
michael@0 | 1569 | virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) { |
michael@0 | 1570 | // platforms where this actually matters should override |
michael@0 | 1571 | return nullptr; |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | virtual gfxFloat GetAdjustedSize() { |
michael@0 | 1575 | return mAdjustedSize > 0.0 ? mAdjustedSize : mStyle.size; |
michael@0 | 1576 | } |
michael@0 | 1577 | |
michael@0 | 1578 | float FUnitsToDevUnitsFactor() const { |
michael@0 | 1579 | // check this was set up during font initialization |
michael@0 | 1580 | NS_ASSERTION(mFUnitsConvFactor > 0.0f, "mFUnitsConvFactor not valid"); |
michael@0 | 1581 | return mFUnitsConvFactor; |
michael@0 | 1582 | } |
michael@0 | 1583 | |
michael@0 | 1584 | // check whether this is an sfnt we can potentially use with harfbuzz |
michael@0 | 1585 | bool FontCanSupportHarfBuzz() { |
michael@0 | 1586 | return mFontEntry->HasCmapTable(); |
michael@0 | 1587 | } |
michael@0 | 1588 | |
michael@0 | 1589 | // check whether this is an sfnt we can potentially use with Graphite |
michael@0 | 1590 | bool FontCanSupportGraphite() { |
michael@0 | 1591 | return mFontEntry->HasGraphiteTables(); |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | // Subclasses may choose to look up glyph ids for characters. |
michael@0 | 1595 | // If they do not override this, gfxHarfBuzzShaper will fetch the cmap |
michael@0 | 1596 | // table and use that. |
michael@0 | 1597 | virtual bool ProvidesGetGlyph() const { |
michael@0 | 1598 | return false; |
michael@0 | 1599 | } |
michael@0 | 1600 | // Map unicode character to glyph ID. |
michael@0 | 1601 | // Only used if ProvidesGetGlyph() returns true. |
michael@0 | 1602 | virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) { |
michael@0 | 1603 | return 0; |
michael@0 | 1604 | } |
michael@0 | 1605 | // Return the horizontal advance of a glyph. |
michael@0 | 1606 | gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID); |
michael@0 | 1607 | |
michael@0 | 1608 | // Return Azure GlyphRenderingOptions for drawing this font. |
michael@0 | 1609 | virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> |
michael@0 | 1610 | GetGlyphRenderingOptions() { return nullptr; } |
michael@0 | 1611 | |
michael@0 | 1612 | gfxFloat SynthesizeSpaceWidth(uint32_t aCh); |
michael@0 | 1613 | |
michael@0 | 1614 | // Font metrics |
michael@0 | 1615 | struct Metrics { |
michael@0 | 1616 | gfxFloat xHeight; |
michael@0 | 1617 | gfxFloat superscriptOffset; |
michael@0 | 1618 | gfxFloat subscriptOffset; |
michael@0 | 1619 | gfxFloat strikeoutSize; |
michael@0 | 1620 | gfxFloat strikeoutOffset; |
michael@0 | 1621 | gfxFloat underlineSize; |
michael@0 | 1622 | gfxFloat underlineOffset; |
michael@0 | 1623 | |
michael@0 | 1624 | gfxFloat internalLeading; |
michael@0 | 1625 | gfxFloat externalLeading; |
michael@0 | 1626 | |
michael@0 | 1627 | gfxFloat emHeight; |
michael@0 | 1628 | gfxFloat emAscent; |
michael@0 | 1629 | gfxFloat emDescent; |
michael@0 | 1630 | gfxFloat maxHeight; |
michael@0 | 1631 | gfxFloat maxAscent; |
michael@0 | 1632 | gfxFloat maxDescent; |
michael@0 | 1633 | gfxFloat maxAdvance; |
michael@0 | 1634 | |
michael@0 | 1635 | gfxFloat aveCharWidth; |
michael@0 | 1636 | gfxFloat spaceWidth; |
michael@0 | 1637 | gfxFloat zeroOrAveCharWidth; // width of '0', or if there is |
michael@0 | 1638 | // no '0' glyph in this font, |
michael@0 | 1639 | // equal to .aveCharWidth |
michael@0 | 1640 | }; |
michael@0 | 1641 | virtual const gfxFont::Metrics& GetMetrics() = 0; |
michael@0 | 1642 | |
michael@0 | 1643 | /** |
michael@0 | 1644 | * We let layout specify spacing on either side of any |
michael@0 | 1645 | * character. We need to specify both before and after |
michael@0 | 1646 | * spacing so that substring measurement can do the right things. |
michael@0 | 1647 | * These values are in appunits. They're always an integral number of |
michael@0 | 1648 | * appunits, but we specify them in floats in case very large spacing |
michael@0 | 1649 | * values are required. |
michael@0 | 1650 | */ |
michael@0 | 1651 | struct Spacing { |
michael@0 | 1652 | gfxFloat mBefore; |
michael@0 | 1653 | gfxFloat mAfter; |
michael@0 | 1654 | }; |
michael@0 | 1655 | /** |
michael@0 | 1656 | * Metrics for a particular string |
michael@0 | 1657 | */ |
michael@0 | 1658 | struct RunMetrics { |
michael@0 | 1659 | RunMetrics() { |
michael@0 | 1660 | mAdvanceWidth = mAscent = mDescent = 0.0; |
michael@0 | 1661 | } |
michael@0 | 1662 | |
michael@0 | 1663 | void CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft); |
michael@0 | 1664 | |
michael@0 | 1665 | // can be negative (partly due to negative spacing). |
michael@0 | 1666 | // Advance widths should be additive: the advance width of the |
michael@0 | 1667 | // (offset1, length1) plus the advance width of (offset1 + length1, |
michael@0 | 1668 | // length2) should be the advance width of (offset1, length1 + length2) |
michael@0 | 1669 | gfxFloat mAdvanceWidth; |
michael@0 | 1670 | |
michael@0 | 1671 | // For zero-width substrings, these must be zero! |
michael@0 | 1672 | gfxFloat mAscent; // always non-negative |
michael@0 | 1673 | gfxFloat mDescent; // always non-negative |
michael@0 | 1674 | |
michael@0 | 1675 | // Bounding box that is guaranteed to include everything drawn. |
michael@0 | 1676 | // If a tight boundingBox was requested when these metrics were |
michael@0 | 1677 | // generated, this will tightly wrap the glyphs, otherwise it is |
michael@0 | 1678 | // "loose" and may be larger than the true bounding box. |
michael@0 | 1679 | // Coordinates are relative to the baseline left origin, so typically |
michael@0 | 1680 | // mBoundingBox.y == -mAscent |
michael@0 | 1681 | gfxRect mBoundingBox; |
michael@0 | 1682 | }; |
michael@0 | 1683 | |
michael@0 | 1684 | /** |
michael@0 | 1685 | * Draw a series of glyphs to aContext. The direction of aTextRun must |
michael@0 | 1686 | * be honoured. |
michael@0 | 1687 | * @param aStart the first character to draw |
michael@0 | 1688 | * @param aEnd draw characters up to here |
michael@0 | 1689 | * @param aBaselineOrigin the baseline origin; the left end of the baseline |
michael@0 | 1690 | * for LTR textruns, the right end of the baseline for RTL textruns. On return, |
michael@0 | 1691 | * this should be updated to the other end of the baseline. In application |
michael@0 | 1692 | * units, really! |
michael@0 | 1693 | * @param aSpacing spacing to insert before and after characters (for RTL |
michael@0 | 1694 | * glyphs, before-spacing is inserted to the right of characters). There |
michael@0 | 1695 | * are aEnd - aStart elements in this array, unless it's null to indicate |
michael@0 | 1696 | * that there is no spacing. |
michael@0 | 1697 | * @param aDrawMode specifies whether the fill or stroke of the glyph should be |
michael@0 | 1698 | * drawn, or if it should be drawn into the current path |
michael@0 | 1699 | * @param aContextPaint information about how to construct the fill and |
michael@0 | 1700 | * stroke pattern. Can be nullptr if we are not stroking the text, which |
michael@0 | 1701 | * indicates that the current source from aContext should be used for filling |
michael@0 | 1702 | * |
michael@0 | 1703 | * Callers guarantee: |
michael@0 | 1704 | * -- aStart and aEnd are aligned to cluster and ligature boundaries |
michael@0 | 1705 | * -- all glyphs use this font |
michael@0 | 1706 | * |
michael@0 | 1707 | * The default implementation builds a cairo glyph array and |
michael@0 | 1708 | * calls cairo_show_glyphs or cairo_glyph_path. |
michael@0 | 1709 | */ |
michael@0 | 1710 | virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, |
michael@0 | 1711 | gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin, |
michael@0 | 1712 | Spacing *aSpacing, gfxTextContextPaint *aContextPaint, |
michael@0 | 1713 | gfxTextRunDrawCallbacks *aCallbacks); |
michael@0 | 1714 | |
michael@0 | 1715 | /** |
michael@0 | 1716 | * Measure a run of characters. See gfxTextRun::Metrics. |
michael@0 | 1717 | * @param aTight if false, then return the union of the glyph extents |
michael@0 | 1718 | * with the font-box for the characters (the rectangle with x=0,width= |
michael@0 | 1719 | * the advance width for the character run,y=-(font ascent), and height= |
michael@0 | 1720 | * font ascent + font descent). Otherwise, we must return as tight as possible |
michael@0 | 1721 | * an approximation to the area actually painted by glyphs. |
michael@0 | 1722 | * @param aContextForTightBoundingBox when aTight is true, this must |
michael@0 | 1723 | * be non-null. |
michael@0 | 1724 | * @param aSpacing spacing to insert before and after glyphs. The bounding box |
michael@0 | 1725 | * need not include the spacing itself, but the spacing affects the glyph |
michael@0 | 1726 | * positions. null if there is no spacing. |
michael@0 | 1727 | * |
michael@0 | 1728 | * Callers guarantee: |
michael@0 | 1729 | * -- aStart and aEnd are aligned to cluster and ligature boundaries |
michael@0 | 1730 | * -- all glyphs use this font |
michael@0 | 1731 | * |
michael@0 | 1732 | * The default implementation just uses font metrics and aTextRun's |
michael@0 | 1733 | * advances, and assumes no characters fall outside the font box. In |
michael@0 | 1734 | * general this is insufficient, because that assumption is not always true. |
michael@0 | 1735 | */ |
michael@0 | 1736 | virtual RunMetrics Measure(gfxTextRun *aTextRun, |
michael@0 | 1737 | uint32_t aStart, uint32_t aEnd, |
michael@0 | 1738 | BoundingBoxType aBoundingBoxType, |
michael@0 | 1739 | gfxContext *aContextForTightBoundingBox, |
michael@0 | 1740 | Spacing *aSpacing); |
michael@0 | 1741 | /** |
michael@0 | 1742 | * Line breaks have been changed at the beginning and/or end of a substring |
michael@0 | 1743 | * of the text. Reshaping may be required; glyph updating is permitted. |
michael@0 | 1744 | * @return true if anything was changed, false otherwise |
michael@0 | 1745 | */ |
michael@0 | 1746 | bool NotifyLineBreaksChanged(gfxTextRun *aTextRun, |
michael@0 | 1747 | uint32_t aStart, uint32_t aLength) |
michael@0 | 1748 | { return false; } |
michael@0 | 1749 | |
michael@0 | 1750 | // Expiration tracking |
michael@0 | 1751 | nsExpirationState *GetExpirationState() { return &mExpirationState; } |
michael@0 | 1752 | |
michael@0 | 1753 | // Get the glyphID of a space |
michael@0 | 1754 | virtual uint32_t GetSpaceGlyph() = 0; |
michael@0 | 1755 | |
michael@0 | 1756 | gfxGlyphExtents *GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit); |
michael@0 | 1757 | |
michael@0 | 1758 | // You need to call SetupCairoFont on the aCR just before calling this |
michael@0 | 1759 | virtual void SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, |
michael@0 | 1760 | bool aNeedTight, gfxGlyphExtents *aExtents); |
michael@0 | 1761 | |
michael@0 | 1762 | // This is called by the default Draw() implementation above. |
michael@0 | 1763 | virtual bool SetupCairoFont(gfxContext *aContext) = 0; |
michael@0 | 1764 | |
michael@0 | 1765 | virtual bool AllowSubpixelAA() { return true; } |
michael@0 | 1766 | |
michael@0 | 1767 | bool IsSyntheticBold() { return mApplySyntheticBold; } |
michael@0 | 1768 | |
michael@0 | 1769 | // Amount by which synthetic bold "fattens" the glyphs: |
michael@0 | 1770 | // For size S up to a threshold size T, we use (0.25 + 3S / 4T), |
michael@0 | 1771 | // so that the result ranges from 0.25 to 1.0; thereafter, |
michael@0 | 1772 | // simply use (S / T). |
michael@0 | 1773 | gfxFloat GetSyntheticBoldOffset() { |
michael@0 | 1774 | gfxFloat size = GetAdjustedSize(); |
michael@0 | 1775 | const gfxFloat threshold = 48.0; |
michael@0 | 1776 | return size < threshold ? (0.25 + 0.75 * size / threshold) : |
michael@0 | 1777 | (size / threshold); |
michael@0 | 1778 | } |
michael@0 | 1779 | |
michael@0 | 1780 | gfxFontEntry *GetFontEntry() { return mFontEntry.get(); } |
michael@0 | 1781 | bool HasCharacter(uint32_t ch) { |
michael@0 | 1782 | if (!mIsValid) |
michael@0 | 1783 | return false; |
michael@0 | 1784 | return mFontEntry->HasCharacter(ch); |
michael@0 | 1785 | } |
michael@0 | 1786 | |
michael@0 | 1787 | uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) { |
michael@0 | 1788 | if (!mIsValid) { |
michael@0 | 1789 | return 0; |
michael@0 | 1790 | } |
michael@0 | 1791 | return mFontEntry->GetUVSGlyph(aCh, aVS); |
michael@0 | 1792 | } |
michael@0 | 1793 | |
michael@0 | 1794 | // call the (virtual) InitTextRun method to do glyph generation/shaping, |
michael@0 | 1795 | // limiting the length of text passed by processing the run in multiple |
michael@0 | 1796 | // segments if necessary |
michael@0 | 1797 | template<typename T> |
michael@0 | 1798 | bool SplitAndInitTextRun(gfxContext *aContext, |
michael@0 | 1799 | gfxTextRun *aTextRun, |
michael@0 | 1800 | const T *aString, |
michael@0 | 1801 | uint32_t aRunStart, |
michael@0 | 1802 | uint32_t aRunLength, |
michael@0 | 1803 | int32_t aRunScript); |
michael@0 | 1804 | |
michael@0 | 1805 | // Get a ShapedWord representing the given text (either 8- or 16-bit) |
michael@0 | 1806 | // for use in setting up a gfxTextRun. |
michael@0 | 1807 | template<typename T> |
michael@0 | 1808 | gfxShapedWord* GetShapedWord(gfxContext *aContext, |
michael@0 | 1809 | const T *aText, |
michael@0 | 1810 | uint32_t aLength, |
michael@0 | 1811 | uint32_t aHash, |
michael@0 | 1812 | int32_t aRunScript, |
michael@0 | 1813 | int32_t aAppUnitsPerDevUnit, |
michael@0 | 1814 | uint32_t aFlags, |
michael@0 | 1815 | gfxTextPerfMetrics *aTextPerf); |
michael@0 | 1816 | |
michael@0 | 1817 | // Ensure the ShapedWord cache is initialized. This MUST be called before |
michael@0 | 1818 | // any attempt to use GetShapedWord(). |
michael@0 | 1819 | void InitWordCache() { |
michael@0 | 1820 | if (!mWordCache) { |
michael@0 | 1821 | mWordCache = new nsTHashtable<CacheHashEntry>; |
michael@0 | 1822 | } |
michael@0 | 1823 | } |
michael@0 | 1824 | |
michael@0 | 1825 | // Called by the gfxFontCache timer to increment the age of all the words, |
michael@0 | 1826 | // so that they'll expire after a sufficient period of non-use |
michael@0 | 1827 | void AgeCachedWords() { |
michael@0 | 1828 | if (mWordCache) { |
michael@0 | 1829 | (void)mWordCache->EnumerateEntries(AgeCacheEntry, this); |
michael@0 | 1830 | } |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | // Discard all cached word records; called on memory-pressure notification. |
michael@0 | 1834 | void ClearCachedWords() { |
michael@0 | 1835 | if (mWordCache) { |
michael@0 | 1836 | mWordCache->Clear(); |
michael@0 | 1837 | } |
michael@0 | 1838 | } |
michael@0 | 1839 | |
michael@0 | 1840 | // Glyph rendering/geometry has changed, so invalidate data as necessary. |
michael@0 | 1841 | void NotifyGlyphsChanged(); |
michael@0 | 1842 | |
michael@0 | 1843 | virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 1844 | FontCacheSizes* aSizes) const; |
michael@0 | 1845 | virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 1846 | FontCacheSizes* aSizes) const; |
michael@0 | 1847 | |
michael@0 | 1848 | typedef enum { |
michael@0 | 1849 | FONT_TYPE_DWRITE, |
michael@0 | 1850 | FONT_TYPE_GDI, |
michael@0 | 1851 | FONT_TYPE_FT2, |
michael@0 | 1852 | FONT_TYPE_MAC, |
michael@0 | 1853 | FONT_TYPE_OS2, |
michael@0 | 1854 | FONT_TYPE_CAIRO |
michael@0 | 1855 | } FontType; |
michael@0 | 1856 | |
michael@0 | 1857 | virtual FontType GetType() const = 0; |
michael@0 | 1858 | |
michael@0 | 1859 | virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(mozilla::gfx::DrawTarget *aTarget) |
michael@0 | 1860 | { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); } |
michael@0 | 1861 | |
michael@0 | 1862 | bool KerningDisabled() { |
michael@0 | 1863 | return mKerningSet && !mKerningEnabled; |
michael@0 | 1864 | } |
michael@0 | 1865 | |
michael@0 | 1866 | /** |
michael@0 | 1867 | * Subclass this object to be notified of glyph changes. Delete the object |
michael@0 | 1868 | * when no longer needed. |
michael@0 | 1869 | */ |
michael@0 | 1870 | class GlyphChangeObserver { |
michael@0 | 1871 | public: |
michael@0 | 1872 | virtual ~GlyphChangeObserver() |
michael@0 | 1873 | { |
michael@0 | 1874 | if (mFont) { |
michael@0 | 1875 | mFont->RemoveGlyphChangeObserver(this); |
michael@0 | 1876 | } |
michael@0 | 1877 | } |
michael@0 | 1878 | // This gets called when the gfxFont dies. |
michael@0 | 1879 | void ForgetFont() { mFont = nullptr; } |
michael@0 | 1880 | virtual void NotifyGlyphsChanged() = 0; |
michael@0 | 1881 | protected: |
michael@0 | 1882 | GlyphChangeObserver(gfxFont *aFont) : mFont(aFont) |
michael@0 | 1883 | { |
michael@0 | 1884 | mFont->AddGlyphChangeObserver(this); |
michael@0 | 1885 | } |
michael@0 | 1886 | gfxFont* mFont; |
michael@0 | 1887 | }; |
michael@0 | 1888 | friend class GlyphChangeObserver; |
michael@0 | 1889 | |
michael@0 | 1890 | bool GlyphsMayChange() |
michael@0 | 1891 | { |
michael@0 | 1892 | // Currently only fonts with SVG glyphs can have animated glyphs |
michael@0 | 1893 | return mFontEntry->TryGetSVGData(this); |
michael@0 | 1894 | } |
michael@0 | 1895 | |
michael@0 | 1896 | static void DestroySingletons() { |
michael@0 | 1897 | delete sScriptTagToCode; |
michael@0 | 1898 | delete sDefaultFeatures; |
michael@0 | 1899 | } |
michael@0 | 1900 | |
michael@0 | 1901 | protected: |
michael@0 | 1902 | // subclasses may provide (possibly hinted) glyph widths (in font units); |
michael@0 | 1903 | // if they do not override this, harfbuzz will use unhinted widths |
michael@0 | 1904 | // derived from the font tables |
michael@0 | 1905 | virtual bool ProvidesGlyphWidths() { |
michael@0 | 1906 | return false; |
michael@0 | 1907 | } |
michael@0 | 1908 | |
michael@0 | 1909 | // The return value is interpreted as a horizontal advance in 16.16 fixed |
michael@0 | 1910 | // point format. |
michael@0 | 1911 | virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) { |
michael@0 | 1912 | return -1; |
michael@0 | 1913 | } |
michael@0 | 1914 | |
michael@0 | 1915 | void AddGlyphChangeObserver(GlyphChangeObserver *aObserver); |
michael@0 | 1916 | void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver); |
michael@0 | 1917 | |
michael@0 | 1918 | // whether font contains substitution lookups containing spaces |
michael@0 | 1919 | bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript); |
michael@0 | 1920 | |
michael@0 | 1921 | // do spaces participate in shaping rules? if so, can't used word cache |
michael@0 | 1922 | bool SpaceMayParticipateInShaping(int32_t aRunScript); |
michael@0 | 1923 | |
michael@0 | 1924 | // For 8-bit text, expand to 16-bit and then call the following method. |
michael@0 | 1925 | bool ShapeText(gfxContext *aContext, |
michael@0 | 1926 | const uint8_t *aText, |
michael@0 | 1927 | uint32_t aOffset, // dest offset in gfxShapedText |
michael@0 | 1928 | uint32_t aLength, |
michael@0 | 1929 | int32_t aScript, |
michael@0 | 1930 | gfxShapedText *aShapedText, // where to store the result |
michael@0 | 1931 | bool aPreferPlatformShaping = false); |
michael@0 | 1932 | |
michael@0 | 1933 | // Call the appropriate shaper to generate glyphs for aText and store |
michael@0 | 1934 | // them into aShapedText. |
michael@0 | 1935 | virtual bool ShapeText(gfxContext *aContext, |
michael@0 | 1936 | const char16_t *aText, |
michael@0 | 1937 | uint32_t aOffset, |
michael@0 | 1938 | uint32_t aLength, |
michael@0 | 1939 | int32_t aScript, |
michael@0 | 1940 | gfxShapedText *aShapedText, |
michael@0 | 1941 | bool aPreferPlatformShaping = false); |
michael@0 | 1942 | |
michael@0 | 1943 | // Helper to adjust for synthetic bold and set character-type flags |
michael@0 | 1944 | // in the shaped text; implementations of ShapeText should call this |
michael@0 | 1945 | // after glyph shaping has been completed. |
michael@0 | 1946 | void PostShapingFixup(gfxContext *aContext, |
michael@0 | 1947 | const char16_t *aText, |
michael@0 | 1948 | uint32_t aOffset, // position within aShapedText |
michael@0 | 1949 | uint32_t aLength, |
michael@0 | 1950 | gfxShapedText *aShapedText); |
michael@0 | 1951 | |
michael@0 | 1952 | // Shape text directly into a range within a textrun, without using the |
michael@0 | 1953 | // font's word cache. Intended for use when the font has layout features |
michael@0 | 1954 | // that involve space, and therefore require shaping complete runs rather |
michael@0 | 1955 | // than isolated words, or for long strings that are inefficient to cache. |
michael@0 | 1956 | // This will split the text on "invalid" characters (tab/newline) that are |
michael@0 | 1957 | // not handled via normal shaping, but does not otherwise divide up the |
michael@0 | 1958 | // text. |
michael@0 | 1959 | template<typename T> |
michael@0 | 1960 | bool ShapeTextWithoutWordCache(gfxContext *aContext, |
michael@0 | 1961 | const T *aText, |
michael@0 | 1962 | uint32_t aOffset, |
michael@0 | 1963 | uint32_t aLength, |
michael@0 | 1964 | int32_t aScript, |
michael@0 | 1965 | gfxTextRun *aTextRun); |
michael@0 | 1966 | |
michael@0 | 1967 | // Shape a fragment of text (a run that is known to contain only |
michael@0 | 1968 | // "valid" characters, no newlines/tabs/other control chars). |
michael@0 | 1969 | // All non-wordcache shaping goes through here; this is the function |
michael@0 | 1970 | // that will ensure we don't pass excessively long runs to the various |
michael@0 | 1971 | // platform shapers. |
michael@0 | 1972 | template<typename T> |
michael@0 | 1973 | bool ShapeFragmentWithoutWordCache(gfxContext *aContext, |
michael@0 | 1974 | const T *aText, |
michael@0 | 1975 | uint32_t aOffset, |
michael@0 | 1976 | uint32_t aLength, |
michael@0 | 1977 | int32_t aScript, |
michael@0 | 1978 | gfxTextRun *aTextRun); |
michael@0 | 1979 | |
michael@0 | 1980 | void CheckForFeaturesInvolvingSpace(); |
michael@0 | 1981 | |
michael@0 | 1982 | // whether a given feature is included in feature settings from both the |
michael@0 | 1983 | // font and the style. aFeatureOn set if resolved feature value is non-zero |
michael@0 | 1984 | bool HasFeatureSet(uint32_t aFeature, bool& aFeatureOn); |
michael@0 | 1985 | |
michael@0 | 1986 | // used when analyzing whether a font has space contextual lookups |
michael@0 | 1987 | static nsDataHashtable<nsUint32HashKey, int32_t> *sScriptTagToCode; |
michael@0 | 1988 | static nsTHashtable<nsUint32HashKey> *sDefaultFeatures; |
michael@0 | 1989 | |
michael@0 | 1990 | nsRefPtr<gfxFontEntry> mFontEntry; |
michael@0 | 1991 | |
michael@0 | 1992 | struct CacheHashKey { |
michael@0 | 1993 | union { |
michael@0 | 1994 | const uint8_t *mSingle; |
michael@0 | 1995 | const char16_t *mDouble; |
michael@0 | 1996 | } mText; |
michael@0 | 1997 | uint32_t mLength; |
michael@0 | 1998 | uint32_t mFlags; |
michael@0 | 1999 | int32_t mScript; |
michael@0 | 2000 | int32_t mAppUnitsPerDevUnit; |
michael@0 | 2001 | PLDHashNumber mHashKey; |
michael@0 | 2002 | bool mTextIs8Bit; |
michael@0 | 2003 | |
michael@0 | 2004 | CacheHashKey(const uint8_t *aText, uint32_t aLength, |
michael@0 | 2005 | uint32_t aStringHash, |
michael@0 | 2006 | int32_t aScriptCode, int32_t aAppUnitsPerDevUnit, |
michael@0 | 2007 | uint32_t aFlags) |
michael@0 | 2008 | : mLength(aLength), |
michael@0 | 2009 | mFlags(aFlags), |
michael@0 | 2010 | mScript(aScriptCode), |
michael@0 | 2011 | mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), |
michael@0 | 2012 | mHashKey(aStringHash + aScriptCode + |
michael@0 | 2013 | aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000), |
michael@0 | 2014 | mTextIs8Bit(true) |
michael@0 | 2015 | { |
michael@0 | 2016 | NS_ASSERTION(aFlags & gfxTextRunFactory::TEXT_IS_8BIT, |
michael@0 | 2017 | "8-bit flag should have been set"); |
michael@0 | 2018 | mText.mSingle = aText; |
michael@0 | 2019 | } |
michael@0 | 2020 | |
michael@0 | 2021 | CacheHashKey(const char16_t *aText, uint32_t aLength, |
michael@0 | 2022 | uint32_t aStringHash, |
michael@0 | 2023 | int32_t aScriptCode, int32_t aAppUnitsPerDevUnit, |
michael@0 | 2024 | uint32_t aFlags) |
michael@0 | 2025 | : mLength(aLength), |
michael@0 | 2026 | mFlags(aFlags), |
michael@0 | 2027 | mScript(aScriptCode), |
michael@0 | 2028 | mAppUnitsPerDevUnit(aAppUnitsPerDevUnit), |
michael@0 | 2029 | mHashKey(aStringHash + aScriptCode + |
michael@0 | 2030 | aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000), |
michael@0 | 2031 | mTextIs8Bit(false) |
michael@0 | 2032 | { |
michael@0 | 2033 | // We can NOT assert that TEXT_IS_8BIT is false in aFlags here, |
michael@0 | 2034 | // because this might be an 8bit-only word from a 16-bit textrun, |
michael@0 | 2035 | // in which case the text we're passed is still in 16-bit form, |
michael@0 | 2036 | // and we'll have to use an 8-to-16bit comparison in KeyEquals. |
michael@0 | 2037 | mText.mDouble = aText; |
michael@0 | 2038 | } |
michael@0 | 2039 | }; |
michael@0 | 2040 | |
michael@0 | 2041 | class CacheHashEntry : public PLDHashEntryHdr { |
michael@0 | 2042 | public: |
michael@0 | 2043 | typedef const CacheHashKey &KeyType; |
michael@0 | 2044 | typedef const CacheHashKey *KeyTypePointer; |
michael@0 | 2045 | |
michael@0 | 2046 | // When constructing a new entry in the hashtable, the caller of Put() |
michael@0 | 2047 | // will fill us in. |
michael@0 | 2048 | CacheHashEntry(KeyTypePointer aKey) { } |
michael@0 | 2049 | CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); } |
michael@0 | 2050 | ~CacheHashEntry() { } |
michael@0 | 2051 | |
michael@0 | 2052 | bool KeyEquals(const KeyTypePointer aKey) const; |
michael@0 | 2053 | |
michael@0 | 2054 | static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } |
michael@0 | 2055 | |
michael@0 | 2056 | static PLDHashNumber HashKey(const KeyTypePointer aKey) { |
michael@0 | 2057 | return aKey->mHashKey; |
michael@0 | 2058 | } |
michael@0 | 2059 | |
michael@0 | 2060 | enum { ALLOW_MEMMOVE = true }; |
michael@0 | 2061 | |
michael@0 | 2062 | nsAutoPtr<gfxShapedWord> mShapedWord; |
michael@0 | 2063 | }; |
michael@0 | 2064 | |
michael@0 | 2065 | static size_t |
michael@0 | 2066 | WordCacheEntrySizeOfExcludingThis(CacheHashEntry* aHashEntry, |
michael@0 | 2067 | mozilla::MallocSizeOf aMallocSizeOf, |
michael@0 | 2068 | void* aUserArg); |
michael@0 | 2069 | |
michael@0 | 2070 | nsAutoPtr<nsTHashtable<CacheHashEntry> > mWordCache; |
michael@0 | 2071 | |
michael@0 | 2072 | static PLDHashOperator AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData); |
michael@0 | 2073 | static const uint32_t kShapedWordCacheMaxAge = 3; |
michael@0 | 2074 | |
michael@0 | 2075 | bool mIsValid; |
michael@0 | 2076 | |
michael@0 | 2077 | // use synthetic bolding for environments where this is not supported |
michael@0 | 2078 | // by the platform |
michael@0 | 2079 | bool mApplySyntheticBold; |
michael@0 | 2080 | |
michael@0 | 2081 | bool mKerningSet; // kerning explicitly set? |
michael@0 | 2082 | bool mKerningEnabled; // if set, on or off? |
michael@0 | 2083 | |
michael@0 | 2084 | nsExpirationState mExpirationState; |
michael@0 | 2085 | gfxFontStyle mStyle; |
michael@0 | 2086 | nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray; |
michael@0 | 2087 | nsAutoPtr<nsTHashtable<nsPtrHashKey<GlyphChangeObserver> > > mGlyphChangeObservers; |
michael@0 | 2088 | |
michael@0 | 2089 | gfxFloat mAdjustedSize; |
michael@0 | 2090 | |
michael@0 | 2091 | float mFUnitsConvFactor; // conversion factor from font units to dev units |
michael@0 | 2092 | |
michael@0 | 2093 | // the AA setting requested for this font - may affect glyph bounds |
michael@0 | 2094 | AntialiasOption mAntialiasOption; |
michael@0 | 2095 | |
michael@0 | 2096 | // a copy of the font without antialiasing, if needed for separate |
michael@0 | 2097 | // measurement by mathml code |
michael@0 | 2098 | nsAutoPtr<gfxFont> mNonAAFont; |
michael@0 | 2099 | |
michael@0 | 2100 | // we may switch between these shapers on the fly, based on the script |
michael@0 | 2101 | // of the text run being shaped |
michael@0 | 2102 | nsAutoPtr<gfxFontShaper> mPlatformShaper; |
michael@0 | 2103 | nsAutoPtr<gfxFontShaper> mHarfBuzzShaper; |
michael@0 | 2104 | nsAutoPtr<gfxFontShaper> mGraphiteShaper; |
michael@0 | 2105 | |
michael@0 | 2106 | mozilla::RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont; |
michael@0 | 2107 | |
michael@0 | 2108 | // Create a default platform text shaper for this font. |
michael@0 | 2109 | // (TODO: This should become pure virtual once all font backends have |
michael@0 | 2110 | // been updated.) |
michael@0 | 2111 | virtual void CreatePlatformShaper() { } |
michael@0 | 2112 | |
michael@0 | 2113 | // Helper for subclasses that want to initialize standard metrics from the |
michael@0 | 2114 | // tables of sfnt (TrueType/OpenType) fonts. |
michael@0 | 2115 | // This will use mFUnitsConvFactor if it is already set, else compute it |
michael@0 | 2116 | // from mAdjustedSize and the unitsPerEm in the font's 'head' table. |
michael@0 | 2117 | // Returns TRUE and sets mIsValid=TRUE if successful; |
michael@0 | 2118 | // Returns TRUE but leaves mIsValid=FALSE if the font seems to be broken. |
michael@0 | 2119 | // Returns FALSE if the font does not appear to be an sfnt at all, |
michael@0 | 2120 | // and should be handled (if possible) using other APIs. |
michael@0 | 2121 | bool InitMetricsFromSfntTables(Metrics& aMetrics); |
michael@0 | 2122 | |
michael@0 | 2123 | // Helper to calculate various derived metrics from the results of |
michael@0 | 2124 | // InitMetricsFromSfntTables or equivalent platform code |
michael@0 | 2125 | void CalculateDerivedMetrics(Metrics& aMetrics); |
michael@0 | 2126 | |
michael@0 | 2127 | // some fonts have bad metrics, this method sanitize them. |
michael@0 | 2128 | // if this font has bad underline offset, aIsBadUnderlineFont should be true. |
michael@0 | 2129 | void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont); |
michael@0 | 2130 | |
michael@0 | 2131 | bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, |
michael@0 | 2132 | uint32_t aGlyphId, gfxTextContextPaint *aContextPaint); |
michael@0 | 2133 | bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, |
michael@0 | 2134 | uint32_t aGlyphId, gfxTextContextPaint *aContextPaint, |
michael@0 | 2135 | gfxTextRunDrawCallbacks *aCallbacks, |
michael@0 | 2136 | bool& aEmittedGlyphs); |
michael@0 | 2137 | |
michael@0 | 2138 | // Bug 674909. When synthetic bolding text by drawing twice, need to |
michael@0 | 2139 | // render using a pixel offset in device pixels, otherwise text |
michael@0 | 2140 | // doesn't appear bolded, it appears as if a bad text shadow exists |
michael@0 | 2141 | // when a non-identity transform exists. Use an offset factor so that |
michael@0 | 2142 | // the second draw occurs at a constant offset in device pixels. |
michael@0 | 2143 | // This helper calculates the scale factor we need to apply to the |
michael@0 | 2144 | // synthetic-bold offset. |
michael@0 | 2145 | static double CalcXScale(gfxContext *aContext); |
michael@0 | 2146 | }; |
michael@0 | 2147 | |
michael@0 | 2148 | // proportion of ascent used for x-height, if unable to read value from font |
michael@0 | 2149 | #define DEFAULT_XHEIGHT_FACTOR 0.56f |
michael@0 | 2150 | |
michael@0 | 2151 | /* |
michael@0 | 2152 | * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun. |
michael@0 | 2153 | * These are objects that store a list of zero or more glyphs for each character. |
michael@0 | 2154 | * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets. |
michael@0 | 2155 | * The idea is that a string is rendered by a loop that draws each glyph |
michael@0 | 2156 | * at its designated offset from the current point, then advances the current |
michael@0 | 2157 | * point by the glyph's advance in the direction of the textrun (LTR or RTL). |
michael@0 | 2158 | * Each glyph advance is always rounded to the nearest appunit; this ensures |
michael@0 | 2159 | * consistent results when dividing the text in a textrun into multiple text |
michael@0 | 2160 | * frames (frame boundaries are always aligned to appunits). We optimize |
michael@0 | 2161 | * for the case where a character has a single glyph and zero xoffset and yoffset, |
michael@0 | 2162 | * and the glyph ID and advance are in a reasonable range so we can pack all |
michael@0 | 2163 | * necessary data into 32 bits. |
michael@0 | 2164 | * |
michael@0 | 2165 | * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont) |
michael@0 | 2166 | * or directly into a gfxTextRun (for cases where we want to shape textruns in |
michael@0 | 2167 | * their entirety rather than using cached words, because there may be layout |
michael@0 | 2168 | * features that depend on the inter-word spaces). |
michael@0 | 2169 | */ |
michael@0 | 2170 | class gfxShapedText |
michael@0 | 2171 | { |
michael@0 | 2172 | public: |
michael@0 | 2173 | gfxShapedText(uint32_t aLength, uint32_t aFlags, |
michael@0 | 2174 | int32_t aAppUnitsPerDevUnit) |
michael@0 | 2175 | : mLength(aLength) |
michael@0 | 2176 | , mFlags(aFlags) |
michael@0 | 2177 | , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) |
michael@0 | 2178 | { } |
michael@0 | 2179 | |
michael@0 | 2180 | virtual ~gfxShapedText() { } |
michael@0 | 2181 | |
michael@0 | 2182 | /** |
michael@0 | 2183 | * This class records the information associated with a character in the |
michael@0 | 2184 | * input string. It's optimized for the case where there is one glyph |
michael@0 | 2185 | * representing that character alone. |
michael@0 | 2186 | * |
michael@0 | 2187 | * A character can have zero or more associated glyphs. Each glyph |
michael@0 | 2188 | * has an advance width and an x and y offset. |
michael@0 | 2189 | * A character may be the start of a cluster. |
michael@0 | 2190 | * A character may be the start of a ligature group. |
michael@0 | 2191 | * A character can be "missing", indicating that the system is unable |
michael@0 | 2192 | * to render the character. |
michael@0 | 2193 | * |
michael@0 | 2194 | * All characters in a ligature group conceptually share all the glyphs |
michael@0 | 2195 | * associated with the characters in a group. |
michael@0 | 2196 | */ |
michael@0 | 2197 | class CompressedGlyph { |
michael@0 | 2198 | public: |
michael@0 | 2199 | CompressedGlyph() { mValue = 0; } |
michael@0 | 2200 | |
michael@0 | 2201 | enum { |
michael@0 | 2202 | // Indicates that a cluster and ligature group starts at this |
michael@0 | 2203 | // character; this character has a single glyph with a reasonable |
michael@0 | 2204 | // advance and zero offsets. A "reasonable" advance |
michael@0 | 2205 | // is one that fits in the available bits (currently 12) (specified |
michael@0 | 2206 | // in appunits). |
michael@0 | 2207 | FLAG_IS_SIMPLE_GLYPH = 0x80000000U, |
michael@0 | 2208 | |
michael@0 | 2209 | // Indicates whether a linebreak is allowed before this character; |
michael@0 | 2210 | // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value |
michael@0 | 2211 | // indicating the kind of linebreak (if any) allowed here. |
michael@0 | 2212 | FLAGS_CAN_BREAK_BEFORE = 0x60000000U, |
michael@0 | 2213 | |
michael@0 | 2214 | FLAGS_CAN_BREAK_SHIFT = 29, |
michael@0 | 2215 | FLAG_BREAK_TYPE_NONE = 0, |
michael@0 | 2216 | FLAG_BREAK_TYPE_NORMAL = 1, |
michael@0 | 2217 | FLAG_BREAK_TYPE_HYPHEN = 2, |
michael@0 | 2218 | |
michael@0 | 2219 | FLAG_CHAR_IS_SPACE = 0x10000000U, |
michael@0 | 2220 | |
michael@0 | 2221 | // The advance is stored in appunits |
michael@0 | 2222 | ADVANCE_MASK = 0x0FFF0000U, |
michael@0 | 2223 | ADVANCE_SHIFT = 16, |
michael@0 | 2224 | |
michael@0 | 2225 | GLYPH_MASK = 0x0000FFFFU, |
michael@0 | 2226 | |
michael@0 | 2227 | // Non-simple glyphs may or may not have glyph data in the |
michael@0 | 2228 | // corresponding mDetailedGlyphs entry. They have the following |
michael@0 | 2229 | // flag bits: |
michael@0 | 2230 | |
michael@0 | 2231 | // When NOT set, indicates that this character corresponds to a |
michael@0 | 2232 | // missing glyph and should be skipped (or possibly, render the character |
michael@0 | 2233 | // Unicode value in some special way). If there are glyphs, |
michael@0 | 2234 | // the mGlyphID is actually the UTF16 character code. The bit is |
michael@0 | 2235 | // inverted so we can memset the array to zero to indicate all missing. |
michael@0 | 2236 | FLAG_NOT_MISSING = 0x01, |
michael@0 | 2237 | FLAG_NOT_CLUSTER_START = 0x02, |
michael@0 | 2238 | FLAG_NOT_LIGATURE_GROUP_START = 0x04, |
michael@0 | 2239 | |
michael@0 | 2240 | FLAG_CHAR_IS_TAB = 0x08, |
michael@0 | 2241 | FLAG_CHAR_IS_NEWLINE = 0x10, |
michael@0 | 2242 | FLAG_CHAR_IS_LOW_SURROGATE = 0x20, |
michael@0 | 2243 | CHAR_IDENTITY_FLAGS_MASK = 0x38, |
michael@0 | 2244 | |
michael@0 | 2245 | GLYPH_COUNT_MASK = 0x00FFFF00U, |
michael@0 | 2246 | GLYPH_COUNT_SHIFT = 8 |
michael@0 | 2247 | }; |
michael@0 | 2248 | |
michael@0 | 2249 | // "Simple glyphs" have a simple glyph ID, simple advance and their |
michael@0 | 2250 | // x and y offsets are zero. Also the glyph extents do not overflow |
michael@0 | 2251 | // the font-box defined by the font ascent, descent and glyph advance width. |
michael@0 | 2252 | // These case is optimized to avoid storing DetailedGlyphs. |
michael@0 | 2253 | |
michael@0 | 2254 | // Returns true if the glyph ID aGlyph fits into the compressed representation |
michael@0 | 2255 | static bool IsSimpleGlyphID(uint32_t aGlyph) { |
michael@0 | 2256 | return (aGlyph & GLYPH_MASK) == aGlyph; |
michael@0 | 2257 | } |
michael@0 | 2258 | // Returns true if the advance aAdvance fits into the compressed representation. |
michael@0 | 2259 | // aAdvance is in appunits. |
michael@0 | 2260 | static bool IsSimpleAdvance(uint32_t aAdvance) { |
michael@0 | 2261 | return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance; |
michael@0 | 2262 | } |
michael@0 | 2263 | |
michael@0 | 2264 | bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; } |
michael@0 | 2265 | uint32_t GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; } |
michael@0 | 2266 | uint32_t GetSimpleGlyph() const { return mValue & GLYPH_MASK; } |
michael@0 | 2267 | |
michael@0 | 2268 | bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; } |
michael@0 | 2269 | bool IsClusterStart() const { |
michael@0 | 2270 | return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START); |
michael@0 | 2271 | } |
michael@0 | 2272 | bool IsLigatureGroupStart() const { |
michael@0 | 2273 | return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START); |
michael@0 | 2274 | } |
michael@0 | 2275 | bool IsLigatureContinuation() const { |
michael@0 | 2276 | return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 && |
michael@0 | 2277 | (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) == |
michael@0 | 2278 | (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING); |
michael@0 | 2279 | } |
michael@0 | 2280 | |
michael@0 | 2281 | // Return true if the original character was a normal (breakable, |
michael@0 | 2282 | // trimmable) space (U+0020). Not true for other characters that |
michael@0 | 2283 | // may happen to map to the space glyph (U+00A0). |
michael@0 | 2284 | bool CharIsSpace() const { |
michael@0 | 2285 | return (mValue & FLAG_CHAR_IS_SPACE) != 0; |
michael@0 | 2286 | } |
michael@0 | 2287 | |
michael@0 | 2288 | bool CharIsTab() const { |
michael@0 | 2289 | return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0; |
michael@0 | 2290 | } |
michael@0 | 2291 | bool CharIsNewline() const { |
michael@0 | 2292 | return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0; |
michael@0 | 2293 | } |
michael@0 | 2294 | bool CharIsLowSurrogate() const { |
michael@0 | 2295 | return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_LOW_SURROGATE) != 0; |
michael@0 | 2296 | } |
michael@0 | 2297 | |
michael@0 | 2298 | uint32_t CharIdentityFlags() const { |
michael@0 | 2299 | return IsSimpleGlyph() ? 0 : (mValue & CHAR_IDENTITY_FLAGS_MASK); |
michael@0 | 2300 | } |
michael@0 | 2301 | |
michael@0 | 2302 | void SetClusterStart(bool aIsClusterStart) { |
michael@0 | 2303 | NS_ASSERTION(!IsSimpleGlyph(), |
michael@0 | 2304 | "can't call SetClusterStart on simple glyphs"); |
michael@0 | 2305 | if (aIsClusterStart) { |
michael@0 | 2306 | mValue &= ~FLAG_NOT_CLUSTER_START; |
michael@0 | 2307 | } else { |
michael@0 | 2308 | mValue |= FLAG_NOT_CLUSTER_START; |
michael@0 | 2309 | } |
michael@0 | 2310 | } |
michael@0 | 2311 | |
michael@0 | 2312 | uint8_t CanBreakBefore() const { |
michael@0 | 2313 | return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT; |
michael@0 | 2314 | } |
michael@0 | 2315 | // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise |
michael@0 | 2316 | uint32_t SetCanBreakBefore(uint8_t aCanBreakBefore) { |
michael@0 | 2317 | NS_ASSERTION(aCanBreakBefore <= 2, |
michael@0 | 2318 | "Bogus break-before value!"); |
michael@0 | 2319 | uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT); |
michael@0 | 2320 | uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE); |
michael@0 | 2321 | mValue ^= toggle; |
michael@0 | 2322 | return toggle; |
michael@0 | 2323 | } |
michael@0 | 2324 | |
michael@0 | 2325 | CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) { |
michael@0 | 2326 | NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow"); |
michael@0 | 2327 | NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow"); |
michael@0 | 2328 | NS_ASSERTION(!CharIdentityFlags(), "Char identity flags lost"); |
michael@0 | 2329 | mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) | |
michael@0 | 2330 | FLAG_IS_SIMPLE_GLYPH | |
michael@0 | 2331 | (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph; |
michael@0 | 2332 | return *this; |
michael@0 | 2333 | } |
michael@0 | 2334 | CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart, |
michael@0 | 2335 | uint32_t aGlyphCount) { |
michael@0 | 2336 | mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) | |
michael@0 | 2337 | FLAG_NOT_MISSING | |
michael@0 | 2338 | CharIdentityFlags() | |
michael@0 | 2339 | (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) | |
michael@0 | 2340 | (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) | |
michael@0 | 2341 | (aGlyphCount << GLYPH_COUNT_SHIFT); |
michael@0 | 2342 | return *this; |
michael@0 | 2343 | } |
michael@0 | 2344 | /** |
michael@0 | 2345 | * Missing glyphs are treated as ligature group starts; don't mess with |
michael@0 | 2346 | * the cluster-start flag (see bugs 618870 and 619286). |
michael@0 | 2347 | */ |
michael@0 | 2348 | CompressedGlyph& SetMissing(uint32_t aGlyphCount) { |
michael@0 | 2349 | mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START | |
michael@0 | 2350 | FLAG_CHAR_IS_SPACE)) | |
michael@0 | 2351 | CharIdentityFlags() | |
michael@0 | 2352 | (aGlyphCount << GLYPH_COUNT_SHIFT); |
michael@0 | 2353 | return *this; |
michael@0 | 2354 | } |
michael@0 | 2355 | uint32_t GetGlyphCount() const { |
michael@0 | 2356 | NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); |
michael@0 | 2357 | return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT; |
michael@0 | 2358 | } |
michael@0 | 2359 | |
michael@0 | 2360 | void SetIsSpace() { |
michael@0 | 2361 | mValue |= FLAG_CHAR_IS_SPACE; |
michael@0 | 2362 | } |
michael@0 | 2363 | void SetIsTab() { |
michael@0 | 2364 | NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); |
michael@0 | 2365 | mValue |= FLAG_CHAR_IS_TAB; |
michael@0 | 2366 | } |
michael@0 | 2367 | void SetIsNewline() { |
michael@0 | 2368 | NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); |
michael@0 | 2369 | mValue |= FLAG_CHAR_IS_NEWLINE; |
michael@0 | 2370 | } |
michael@0 | 2371 | void SetIsLowSurrogate() { |
michael@0 | 2372 | NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); |
michael@0 | 2373 | mValue |= FLAG_CHAR_IS_LOW_SURROGATE; |
michael@0 | 2374 | } |
michael@0 | 2375 | |
michael@0 | 2376 | private: |
michael@0 | 2377 | uint32_t mValue; |
michael@0 | 2378 | }; |
michael@0 | 2379 | |
michael@0 | 2380 | // Accessor for the array of CompressedGlyph records, which will be in |
michael@0 | 2381 | // a different place in gfxShapedWord vs gfxTextRun |
michael@0 | 2382 | virtual CompressedGlyph *GetCharacterGlyphs() = 0; |
michael@0 | 2383 | |
michael@0 | 2384 | /** |
michael@0 | 2385 | * When the glyphs for a character don't fit into a CompressedGlyph record |
michael@0 | 2386 | * in SimpleGlyph format, we use an array of DetailedGlyphs instead. |
michael@0 | 2387 | */ |
michael@0 | 2388 | struct DetailedGlyph { |
michael@0 | 2389 | /** The glyphID, or the Unicode character |
michael@0 | 2390 | * if this is a missing glyph */ |
michael@0 | 2391 | uint32_t mGlyphID; |
michael@0 | 2392 | /** The advance, x-offset and y-offset of the glyph, in appunits |
michael@0 | 2393 | * mAdvance is in the text direction (RTL or LTR) |
michael@0 | 2394 | * mXOffset is always from left to right |
michael@0 | 2395 | * mYOffset is always from top to bottom */ |
michael@0 | 2396 | int32_t mAdvance; |
michael@0 | 2397 | float mXOffset, mYOffset; |
michael@0 | 2398 | }; |
michael@0 | 2399 | |
michael@0 | 2400 | void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph, |
michael@0 | 2401 | const DetailedGlyph *aGlyphs); |
michael@0 | 2402 | |
michael@0 | 2403 | void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont); |
michael@0 | 2404 | |
michael@0 | 2405 | void SetIsSpace(uint32_t aIndex) { |
michael@0 | 2406 | GetCharacterGlyphs()[aIndex].SetIsSpace(); |
michael@0 | 2407 | } |
michael@0 | 2408 | |
michael@0 | 2409 | void SetIsLowSurrogate(uint32_t aIndex) { |
michael@0 | 2410 | SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr); |
michael@0 | 2411 | GetCharacterGlyphs()[aIndex].SetIsLowSurrogate(); |
michael@0 | 2412 | } |
michael@0 | 2413 | |
michael@0 | 2414 | bool HasDetailedGlyphs() const { |
michael@0 | 2415 | return mDetailedGlyphs != nullptr; |
michael@0 | 2416 | } |
michael@0 | 2417 | |
michael@0 | 2418 | bool IsClusterStart(uint32_t aPos) { |
michael@0 | 2419 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2420 | return GetCharacterGlyphs()[aPos].IsClusterStart(); |
michael@0 | 2421 | } |
michael@0 | 2422 | |
michael@0 | 2423 | bool IsLigatureGroupStart(uint32_t aPos) { |
michael@0 | 2424 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2425 | return GetCharacterGlyphs()[aPos].IsLigatureGroupStart(); |
michael@0 | 2426 | } |
michael@0 | 2427 | |
michael@0 | 2428 | // NOTE that this must not be called for a character offset that does |
michael@0 | 2429 | // not have any DetailedGlyph records; callers must have verified that |
michael@0 | 2430 | // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero. |
michael@0 | 2431 | DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) { |
michael@0 | 2432 | NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() && |
michael@0 | 2433 | !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() && |
michael@0 | 2434 | GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0, |
michael@0 | 2435 | "invalid use of GetDetailedGlyphs; check the caller!"); |
michael@0 | 2436 | return mDetailedGlyphs->Get(aCharIndex); |
michael@0 | 2437 | } |
michael@0 | 2438 | |
michael@0 | 2439 | void AdjustAdvancesForSyntheticBold(float aSynBoldOffset, |
michael@0 | 2440 | uint32_t aOffset, uint32_t aLength); |
michael@0 | 2441 | |
michael@0 | 2442 | // Mark clusters in the CompressedGlyph records, starting at aOffset, |
michael@0 | 2443 | // based on the Unicode properties of the text in aString. |
michael@0 | 2444 | // This is also responsible to set the IsSpace flag for space characters. |
michael@0 | 2445 | void SetupClusterBoundaries(uint32_t aOffset, |
michael@0 | 2446 | const char16_t *aString, |
michael@0 | 2447 | uint32_t aLength); |
michael@0 | 2448 | // In 8-bit text, there won't actually be any clusters, but we still need |
michael@0 | 2449 | // the space-marking functionality. |
michael@0 | 2450 | void SetupClusterBoundaries(uint32_t aOffset, |
michael@0 | 2451 | const uint8_t *aString, |
michael@0 | 2452 | uint32_t aLength); |
michael@0 | 2453 | |
michael@0 | 2454 | uint32_t Flags() const { |
michael@0 | 2455 | return mFlags; |
michael@0 | 2456 | } |
michael@0 | 2457 | |
michael@0 | 2458 | bool IsRightToLeft() const { |
michael@0 | 2459 | return (Flags() & gfxTextRunFactory::TEXT_IS_RTL) != 0; |
michael@0 | 2460 | } |
michael@0 | 2461 | |
michael@0 | 2462 | float GetDirection() const { |
michael@0 | 2463 | return IsRightToLeft() ? -1.0f : 1.0f; |
michael@0 | 2464 | } |
michael@0 | 2465 | |
michael@0 | 2466 | bool DisableLigatures() const { |
michael@0 | 2467 | return (Flags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0; |
michael@0 | 2468 | } |
michael@0 | 2469 | |
michael@0 | 2470 | bool TextIs8Bit() const { |
michael@0 | 2471 | return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0; |
michael@0 | 2472 | } |
michael@0 | 2473 | |
michael@0 | 2474 | int32_t GetAppUnitsPerDevUnit() const { |
michael@0 | 2475 | return mAppUnitsPerDevUnit; |
michael@0 | 2476 | } |
michael@0 | 2477 | |
michael@0 | 2478 | uint32_t GetLength() const { |
michael@0 | 2479 | return mLength; |
michael@0 | 2480 | } |
michael@0 | 2481 | |
michael@0 | 2482 | bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh); |
michael@0 | 2483 | |
michael@0 | 2484 | protected: |
michael@0 | 2485 | // Allocate aCount DetailedGlyphs for the given index |
michael@0 | 2486 | DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, |
michael@0 | 2487 | uint32_t aCount); |
michael@0 | 2488 | |
michael@0 | 2489 | // For characters whose glyph data does not fit the "simple" glyph criteria |
michael@0 | 2490 | // in CompressedGlyph, we use a sorted array to store the association |
michael@0 | 2491 | // between the source character offset and an index into an array |
michael@0 | 2492 | // DetailedGlyphs. The CompressedGlyph record includes a count of |
michael@0 | 2493 | // the number of DetailedGlyph records that belong to the character, |
michael@0 | 2494 | // starting at the given index. |
michael@0 | 2495 | class DetailedGlyphStore { |
michael@0 | 2496 | public: |
michael@0 | 2497 | DetailedGlyphStore() |
michael@0 | 2498 | : mLastUsed(0) |
michael@0 | 2499 | { } |
michael@0 | 2500 | |
michael@0 | 2501 | // This is optimized for the most common calling patterns: |
michael@0 | 2502 | // we rarely need random access to the records, access is most commonly |
michael@0 | 2503 | // sequential through the textRun, so we record the last-used index |
michael@0 | 2504 | // and check whether the caller wants the same record again, or the |
michael@0 | 2505 | // next; if not, it's most likely we're starting over from the start |
michael@0 | 2506 | // of the run, so we check the first entry before resorting to binary |
michael@0 | 2507 | // search as a last resort. |
michael@0 | 2508 | // NOTE that this must not be called for a character offset that does |
michael@0 | 2509 | // not have any DetailedGlyph records; callers must have verified that |
michael@0 | 2510 | // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero |
michael@0 | 2511 | // before calling this, otherwise the assertions here will fire (in a |
michael@0 | 2512 | // debug build), and we'll probably crash. |
michael@0 | 2513 | DetailedGlyph* Get(uint32_t aOffset) { |
michael@0 | 2514 | NS_ASSERTION(mOffsetToIndex.Length() > 0, |
michael@0 | 2515 | "no detailed glyph records!"); |
michael@0 | 2516 | DetailedGlyph* details = mDetails.Elements(); |
michael@0 | 2517 | // check common cases (fwd iteration, initial entry, etc) first |
michael@0 | 2518 | if (mLastUsed < mOffsetToIndex.Length() - 1 && |
michael@0 | 2519 | aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) { |
michael@0 | 2520 | ++mLastUsed; |
michael@0 | 2521 | } else if (aOffset == mOffsetToIndex[0].mOffset) { |
michael@0 | 2522 | mLastUsed = 0; |
michael@0 | 2523 | } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) { |
michael@0 | 2524 | // do nothing |
michael@0 | 2525 | } else if (mLastUsed > 0 && |
michael@0 | 2526 | aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) { |
michael@0 | 2527 | --mLastUsed; |
michael@0 | 2528 | } else { |
michael@0 | 2529 | mLastUsed = |
michael@0 | 2530 | mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset()); |
michael@0 | 2531 | } |
michael@0 | 2532 | NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex, |
michael@0 | 2533 | "detailed glyph record missing!"); |
michael@0 | 2534 | return details + mOffsetToIndex[mLastUsed].mIndex; |
michael@0 | 2535 | } |
michael@0 | 2536 | |
michael@0 | 2537 | DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) { |
michael@0 | 2538 | uint32_t detailIndex = mDetails.Length(); |
michael@0 | 2539 | DetailedGlyph *details = mDetails.AppendElements(aCount); |
michael@0 | 2540 | if (!details) { |
michael@0 | 2541 | return nullptr; |
michael@0 | 2542 | } |
michael@0 | 2543 | // We normally set up glyph records sequentially, so the common case |
michael@0 | 2544 | // here is to append new records to the mOffsetToIndex array; |
michael@0 | 2545 | // test for that before falling back to the InsertElementSorted |
michael@0 | 2546 | // method. |
michael@0 | 2547 | if (mOffsetToIndex.Length() == 0 || |
michael@0 | 2548 | aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) { |
michael@0 | 2549 | if (!mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex))) { |
michael@0 | 2550 | return nullptr; |
michael@0 | 2551 | } |
michael@0 | 2552 | } else { |
michael@0 | 2553 | if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex), |
michael@0 | 2554 | CompareRecordOffsets())) { |
michael@0 | 2555 | return nullptr; |
michael@0 | 2556 | } |
michael@0 | 2557 | } |
michael@0 | 2558 | return details; |
michael@0 | 2559 | } |
michael@0 | 2560 | |
michael@0 | 2561 | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { |
michael@0 | 2562 | return aMallocSizeOf(this) + |
michael@0 | 2563 | mDetails.SizeOfExcludingThis(aMallocSizeOf) + |
michael@0 | 2564 | mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 2565 | } |
michael@0 | 2566 | |
michael@0 | 2567 | private: |
michael@0 | 2568 | struct DGRec { |
michael@0 | 2569 | DGRec(const uint32_t& aOffset, const uint32_t& aIndex) |
michael@0 | 2570 | : mOffset(aOffset), mIndex(aIndex) { } |
michael@0 | 2571 | uint32_t mOffset; // source character offset in the textrun |
michael@0 | 2572 | uint32_t mIndex; // index where this char's DetailedGlyphs begin |
michael@0 | 2573 | }; |
michael@0 | 2574 | |
michael@0 | 2575 | struct CompareToOffset { |
michael@0 | 2576 | bool Equals(const DGRec& a, const uint32_t& b) const { |
michael@0 | 2577 | return a.mOffset == b; |
michael@0 | 2578 | } |
michael@0 | 2579 | bool LessThan(const DGRec& a, const uint32_t& b) const { |
michael@0 | 2580 | return a.mOffset < b; |
michael@0 | 2581 | } |
michael@0 | 2582 | }; |
michael@0 | 2583 | |
michael@0 | 2584 | struct CompareRecordOffsets { |
michael@0 | 2585 | bool Equals(const DGRec& a, const DGRec& b) const { |
michael@0 | 2586 | return a.mOffset == b.mOffset; |
michael@0 | 2587 | } |
michael@0 | 2588 | bool LessThan(const DGRec& a, const DGRec& b) const { |
michael@0 | 2589 | return a.mOffset < b.mOffset; |
michael@0 | 2590 | } |
michael@0 | 2591 | }; |
michael@0 | 2592 | |
michael@0 | 2593 | // Concatenated array of all the DetailedGlyph records needed for the |
michael@0 | 2594 | // textRun; individual character offsets are associated with indexes |
michael@0 | 2595 | // into this array via the mOffsetToIndex table. |
michael@0 | 2596 | nsTArray<DetailedGlyph> mDetails; |
michael@0 | 2597 | |
michael@0 | 2598 | // For each character offset that needs DetailedGlyphs, we record the |
michael@0 | 2599 | // index in mDetails where the list of glyphs begins. This array is |
michael@0 | 2600 | // sorted by mOffset. |
michael@0 | 2601 | nsTArray<DGRec> mOffsetToIndex; |
michael@0 | 2602 | |
michael@0 | 2603 | // Records the most recently used index into mOffsetToIndex, so that |
michael@0 | 2604 | // we can support sequential access more quickly than just doing |
michael@0 | 2605 | // a binary search each time. |
michael@0 | 2606 | nsTArray<DGRec>::index_type mLastUsed; |
michael@0 | 2607 | }; |
michael@0 | 2608 | |
michael@0 | 2609 | nsAutoPtr<DetailedGlyphStore> mDetailedGlyphs; |
michael@0 | 2610 | |
michael@0 | 2611 | // Number of char16_t characters and CompressedGlyph glyph records |
michael@0 | 2612 | uint32_t mLength; |
michael@0 | 2613 | |
michael@0 | 2614 | // Shaping flags (direction, ligature-suppression) |
michael@0 | 2615 | uint32_t mFlags; |
michael@0 | 2616 | |
michael@0 | 2617 | int32_t mAppUnitsPerDevUnit; |
michael@0 | 2618 | }; |
michael@0 | 2619 | |
michael@0 | 2620 | /* |
michael@0 | 2621 | * gfxShapedWord: an individual (space-delimited) run of text shaped with a |
michael@0 | 2622 | * particular font, without regard to external context. |
michael@0 | 2623 | * |
michael@0 | 2624 | * The glyph data is copied into gfxTextRuns as needed from the cache of |
michael@0 | 2625 | * ShapedWords associated with each gfxFont instance. |
michael@0 | 2626 | */ |
michael@0 | 2627 | class gfxShapedWord : public gfxShapedText |
michael@0 | 2628 | { |
michael@0 | 2629 | public: |
michael@0 | 2630 | // Create a ShapedWord that can hold glyphs for aLength characters, |
michael@0 | 2631 | // with mCharacterGlyphs sized appropriately. |
michael@0 | 2632 | // |
michael@0 | 2633 | // Returns null on allocation failure (does NOT use infallible alloc) |
michael@0 | 2634 | // so caller must check for success. |
michael@0 | 2635 | // |
michael@0 | 2636 | // This does NOT perform shaping, so the returned word contains no |
michael@0 | 2637 | // glyph data; the caller must call gfxFont::ShapeText() with appropriate |
michael@0 | 2638 | // parameters to set up the glyphs. |
michael@0 | 2639 | static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength, |
michael@0 | 2640 | int32_t aRunScript, |
michael@0 | 2641 | int32_t aAppUnitsPerDevUnit, |
michael@0 | 2642 | uint32_t aFlags) { |
michael@0 | 2643 | NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(), |
michael@0 | 2644 | "excessive length for gfxShapedWord!"); |
michael@0 | 2645 | |
michael@0 | 2646 | // Compute size needed including the mCharacterGlyphs array |
michael@0 | 2647 | // and a copy of the original text |
michael@0 | 2648 | uint32_t size = |
michael@0 | 2649 | offsetof(gfxShapedWord, mCharGlyphsStorage) + |
michael@0 | 2650 | aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t)); |
michael@0 | 2651 | void *storage = moz_malloc(size); |
michael@0 | 2652 | if (!storage) { |
michael@0 | 2653 | return nullptr; |
michael@0 | 2654 | } |
michael@0 | 2655 | |
michael@0 | 2656 | // Construct in the pre-allocated storage, using placement new |
michael@0 | 2657 | return new (storage) gfxShapedWord(aText, aLength, aRunScript, |
michael@0 | 2658 | aAppUnitsPerDevUnit, aFlags); |
michael@0 | 2659 | } |
michael@0 | 2660 | |
michael@0 | 2661 | static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength, |
michael@0 | 2662 | int32_t aRunScript, |
michael@0 | 2663 | int32_t aAppUnitsPerDevUnit, |
michael@0 | 2664 | uint32_t aFlags) { |
michael@0 | 2665 | NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(), |
michael@0 | 2666 | "excessive length for gfxShapedWord!"); |
michael@0 | 2667 | |
michael@0 | 2668 | // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set, |
michael@0 | 2669 | // then we convert the text to an 8-bit version and call the 8-bit |
michael@0 | 2670 | // Create function instead. |
michael@0 | 2671 | if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) { |
michael@0 | 2672 | nsAutoCString narrowText; |
michael@0 | 2673 | LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength), |
michael@0 | 2674 | narrowText); |
michael@0 | 2675 | return Create((const uint8_t*)(narrowText.BeginReading()), |
michael@0 | 2676 | aLength, aRunScript, aAppUnitsPerDevUnit, aFlags); |
michael@0 | 2677 | } |
michael@0 | 2678 | |
michael@0 | 2679 | uint32_t size = |
michael@0 | 2680 | offsetof(gfxShapedWord, mCharGlyphsStorage) + |
michael@0 | 2681 | aLength * (sizeof(CompressedGlyph) + sizeof(char16_t)); |
michael@0 | 2682 | void *storage = moz_malloc(size); |
michael@0 | 2683 | if (!storage) { |
michael@0 | 2684 | return nullptr; |
michael@0 | 2685 | } |
michael@0 | 2686 | |
michael@0 | 2687 | return new (storage) gfxShapedWord(aText, aLength, aRunScript, |
michael@0 | 2688 | aAppUnitsPerDevUnit, aFlags); |
michael@0 | 2689 | } |
michael@0 | 2690 | |
michael@0 | 2691 | // Override operator delete to properly free the object that was |
michael@0 | 2692 | // allocated via moz_malloc. |
michael@0 | 2693 | void operator delete(void* p) { |
michael@0 | 2694 | moz_free(p); |
michael@0 | 2695 | } |
michael@0 | 2696 | |
michael@0 | 2697 | CompressedGlyph *GetCharacterGlyphs() { |
michael@0 | 2698 | return &mCharGlyphsStorage[0]; |
michael@0 | 2699 | } |
michael@0 | 2700 | |
michael@0 | 2701 | const uint8_t* Text8Bit() const { |
michael@0 | 2702 | NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()"); |
michael@0 | 2703 | return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength()); |
michael@0 | 2704 | } |
michael@0 | 2705 | |
michael@0 | 2706 | const char16_t* TextUnicode() const { |
michael@0 | 2707 | NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()"); |
michael@0 | 2708 | return reinterpret_cast<const char16_t*>(mCharGlyphsStorage + GetLength()); |
michael@0 | 2709 | } |
michael@0 | 2710 | |
michael@0 | 2711 | char16_t GetCharAt(uint32_t aOffset) const { |
michael@0 | 2712 | NS_ASSERTION(aOffset < GetLength(), "aOffset out of range"); |
michael@0 | 2713 | return TextIs8Bit() ? |
michael@0 | 2714 | char16_t(Text8Bit()[aOffset]) : TextUnicode()[aOffset]; |
michael@0 | 2715 | } |
michael@0 | 2716 | |
michael@0 | 2717 | int32_t Script() const { |
michael@0 | 2718 | return mScript; |
michael@0 | 2719 | } |
michael@0 | 2720 | |
michael@0 | 2721 | void ResetAge() { |
michael@0 | 2722 | mAgeCounter = 0; |
michael@0 | 2723 | } |
michael@0 | 2724 | uint32_t IncrementAge() { |
michael@0 | 2725 | return ++mAgeCounter; |
michael@0 | 2726 | } |
michael@0 | 2727 | |
michael@0 | 2728 | private: |
michael@0 | 2729 | // so that gfxTextRun can share our DetailedGlyphStore class |
michael@0 | 2730 | friend class gfxTextRun; |
michael@0 | 2731 | |
michael@0 | 2732 | // Construct storage for a ShapedWord, ready to receive glyph data |
michael@0 | 2733 | gfxShapedWord(const uint8_t *aText, uint32_t aLength, |
michael@0 | 2734 | int32_t aRunScript, int32_t aAppUnitsPerDevUnit, |
michael@0 | 2735 | uint32_t aFlags) |
michael@0 | 2736 | : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT, |
michael@0 | 2737 | aAppUnitsPerDevUnit) |
michael@0 | 2738 | , mScript(aRunScript) |
michael@0 | 2739 | , mAgeCounter(0) |
michael@0 | 2740 | { |
michael@0 | 2741 | memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph)); |
michael@0 | 2742 | uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]); |
michael@0 | 2743 | memcpy(text, aText, aLength * sizeof(uint8_t)); |
michael@0 | 2744 | } |
michael@0 | 2745 | |
michael@0 | 2746 | gfxShapedWord(const char16_t *aText, uint32_t aLength, |
michael@0 | 2747 | int32_t aRunScript, int32_t aAppUnitsPerDevUnit, |
michael@0 | 2748 | uint32_t aFlags) |
michael@0 | 2749 | : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit) |
michael@0 | 2750 | , mScript(aRunScript) |
michael@0 | 2751 | , mAgeCounter(0) |
michael@0 | 2752 | { |
michael@0 | 2753 | memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph)); |
michael@0 | 2754 | char16_t *text = reinterpret_cast<char16_t*>(&mCharGlyphsStorage[aLength]); |
michael@0 | 2755 | memcpy(text, aText, aLength * sizeof(char16_t)); |
michael@0 | 2756 | SetupClusterBoundaries(0, aText, aLength); |
michael@0 | 2757 | } |
michael@0 | 2758 | |
michael@0 | 2759 | int32_t mScript; |
michael@0 | 2760 | |
michael@0 | 2761 | uint32_t mAgeCounter; |
michael@0 | 2762 | |
michael@0 | 2763 | // The mCharGlyphsStorage array is actually a variable-size member; |
michael@0 | 2764 | // when the ShapedWord is created, its size will be increased as necessary |
michael@0 | 2765 | // to allow the proper number of glyphs to be stored. |
michael@0 | 2766 | // The original text, in either 8-bit or 16-bit form, will be stored |
michael@0 | 2767 | // immediately following the CompressedGlyphs. |
michael@0 | 2768 | CompressedGlyph mCharGlyphsStorage[1]; |
michael@0 | 2769 | }; |
michael@0 | 2770 | |
michael@0 | 2771 | /** |
michael@0 | 2772 | * Callback for Draw() to use when drawing text with mode |
michael@0 | 2773 | * DrawMode::GLYPH_PATH. |
michael@0 | 2774 | */ |
michael@0 | 2775 | struct gfxTextRunDrawCallbacks { |
michael@0 | 2776 | |
michael@0 | 2777 | /** |
michael@0 | 2778 | * Constructs a new DrawCallbacks object. |
michael@0 | 2779 | * |
michael@0 | 2780 | * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be |
michael@0 | 2781 | * painted and the NotifyBeforeSVGGlyphPainted/NotifyAfterSVGGlyphPainted |
michael@0 | 2782 | * callbacks will be invoked for each SVG glyph. If false, SVG glyphs |
michael@0 | 2783 | * will not be painted; fallback plain glyphs are not emitted either. |
michael@0 | 2784 | */ |
michael@0 | 2785 | gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false) |
michael@0 | 2786 | : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs) |
michael@0 | 2787 | { |
michael@0 | 2788 | } |
michael@0 | 2789 | |
michael@0 | 2790 | /** |
michael@0 | 2791 | * Called when a path has been emitted to the gfxContext when |
michael@0 | 2792 | * painting a text run. This can be called any number of times, |
michael@0 | 2793 | * due to partial ligatures and intervening SVG glyphs. |
michael@0 | 2794 | */ |
michael@0 | 2795 | virtual void NotifyGlyphPathEmitted() = 0; |
michael@0 | 2796 | |
michael@0 | 2797 | /** |
michael@0 | 2798 | * Called just before an SVG glyph has been painted to the gfxContext. |
michael@0 | 2799 | */ |
michael@0 | 2800 | virtual void NotifyBeforeSVGGlyphPainted() { } |
michael@0 | 2801 | |
michael@0 | 2802 | /** |
michael@0 | 2803 | * Called just after an SVG glyph has been painted to the gfxContext. |
michael@0 | 2804 | */ |
michael@0 | 2805 | virtual void NotifyAfterSVGGlyphPainted() { } |
michael@0 | 2806 | |
michael@0 | 2807 | bool mShouldPaintSVGGlyphs; |
michael@0 | 2808 | }; |
michael@0 | 2809 | |
michael@0 | 2810 | /** |
michael@0 | 2811 | * gfxTextRun is an abstraction for drawing and measuring substrings of a run |
michael@0 | 2812 | * of text. It stores runs of positioned glyph data, each run having a single |
michael@0 | 2813 | * gfxFont. The glyphs are associated with a string of source text, and the |
michael@0 | 2814 | * gfxTextRun APIs take parameters that are offsets into that source text. |
michael@0 | 2815 | * |
michael@0 | 2816 | * gfxTextRuns are not refcounted. They should be deleted when no longer required. |
michael@0 | 2817 | * |
michael@0 | 2818 | * gfxTextRuns are mostly immutable. The only things that can change are |
michael@0 | 2819 | * inter-cluster spacing and line break placement. Spacing is always obtained |
michael@0 | 2820 | * lazily by methods that need it, it is not cached. Line breaks are stored |
michael@0 | 2821 | * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does |
michael@0 | 2822 | * not actually do anything to explicitly account for line breaks). Initially |
michael@0 | 2823 | * there are no line breaks. The textrun can record line breaks before or after |
michael@0 | 2824 | * any given cluster. (Line breaks specified inside clusters are ignored.) |
michael@0 | 2825 | * |
michael@0 | 2826 | * It is important that zero-length substrings are handled correctly. This will |
michael@0 | 2827 | * be on the test! |
michael@0 | 2828 | */ |
michael@0 | 2829 | class gfxTextRun : public gfxShapedText { |
michael@0 | 2830 | public: |
michael@0 | 2831 | |
michael@0 | 2832 | // Override operator delete to properly free the object that was |
michael@0 | 2833 | // allocated via moz_malloc. |
michael@0 | 2834 | void operator delete(void* p) { |
michael@0 | 2835 | moz_free(p); |
michael@0 | 2836 | } |
michael@0 | 2837 | |
michael@0 | 2838 | virtual ~gfxTextRun(); |
michael@0 | 2839 | |
michael@0 | 2840 | typedef gfxFont::RunMetrics Metrics; |
michael@0 | 2841 | |
michael@0 | 2842 | // Public textrun API for general use |
michael@0 | 2843 | |
michael@0 | 2844 | bool IsClusterStart(uint32_t aPos) { |
michael@0 | 2845 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2846 | return mCharacterGlyphs[aPos].IsClusterStart(); |
michael@0 | 2847 | } |
michael@0 | 2848 | bool IsLigatureGroupStart(uint32_t aPos) { |
michael@0 | 2849 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2850 | return mCharacterGlyphs[aPos].IsLigatureGroupStart(); |
michael@0 | 2851 | } |
michael@0 | 2852 | bool CanBreakLineBefore(uint32_t aPos) { |
michael@0 | 2853 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2854 | return mCharacterGlyphs[aPos].CanBreakBefore() == |
michael@0 | 2855 | CompressedGlyph::FLAG_BREAK_TYPE_NORMAL; |
michael@0 | 2856 | } |
michael@0 | 2857 | bool CanHyphenateBefore(uint32_t aPos) { |
michael@0 | 2858 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2859 | return mCharacterGlyphs[aPos].CanBreakBefore() == |
michael@0 | 2860 | CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN; |
michael@0 | 2861 | } |
michael@0 | 2862 | |
michael@0 | 2863 | bool CharIsSpace(uint32_t aPos) { |
michael@0 | 2864 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2865 | return mCharacterGlyphs[aPos].CharIsSpace(); |
michael@0 | 2866 | } |
michael@0 | 2867 | bool CharIsTab(uint32_t aPos) { |
michael@0 | 2868 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2869 | return mCharacterGlyphs[aPos].CharIsTab(); |
michael@0 | 2870 | } |
michael@0 | 2871 | bool CharIsNewline(uint32_t aPos) { |
michael@0 | 2872 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2873 | return mCharacterGlyphs[aPos].CharIsNewline(); |
michael@0 | 2874 | } |
michael@0 | 2875 | bool CharIsLowSurrogate(uint32_t aPos) { |
michael@0 | 2876 | NS_ASSERTION(aPos < GetLength(), "aPos out of range"); |
michael@0 | 2877 | return mCharacterGlyphs[aPos].CharIsLowSurrogate(); |
michael@0 | 2878 | } |
michael@0 | 2879 | |
michael@0 | 2880 | uint32_t GetLength() { return mLength; } |
michael@0 | 2881 | |
michael@0 | 2882 | // All uint32_t aStart, uint32_t aLength ranges below are restricted to |
michael@0 | 2883 | // grapheme cluster boundaries! All offsets are in terms of the string |
michael@0 | 2884 | // passed into MakeTextRun. |
michael@0 | 2885 | |
michael@0 | 2886 | // All coordinates are in layout/app units |
michael@0 | 2887 | |
michael@0 | 2888 | /** |
michael@0 | 2889 | * Set the potential linebreaks for a substring of the textrun. These are |
michael@0 | 2890 | * the "allow break before" points. Initially, there are no potential |
michael@0 | 2891 | * linebreaks. |
michael@0 | 2892 | * |
michael@0 | 2893 | * This can change glyphs and/or geometry! Some textruns' shapes |
michael@0 | 2894 | * depend on potential line breaks (e.g., title-case-converting textruns). |
michael@0 | 2895 | * This function is virtual so that those textruns can reshape themselves. |
michael@0 | 2896 | * |
michael@0 | 2897 | * @return true if this changed the linebreaks, false if the new line |
michael@0 | 2898 | * breaks are the same as the old |
michael@0 | 2899 | */ |
michael@0 | 2900 | virtual bool SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength, |
michael@0 | 2901 | uint8_t *aBreakBefore, |
michael@0 | 2902 | gfxContext *aRefContext); |
michael@0 | 2903 | |
michael@0 | 2904 | /** |
michael@0 | 2905 | * Layout provides PropertyProvider objects. These allow detection of |
michael@0 | 2906 | * potential line break points and computation of spacing. We pass the data |
michael@0 | 2907 | * this way to allow lazy data acquisition; for example BreakAndMeasureText |
michael@0 | 2908 | * will want to only ask for properties of text it's actually looking at. |
michael@0 | 2909 | * |
michael@0 | 2910 | * NOTE that requested spacing may not actually be applied, if the textrun |
michael@0 | 2911 | * is unable to apply it in some context. Exception: spacing around a |
michael@0 | 2912 | * whitespace character MUST always be applied. |
michael@0 | 2913 | */ |
michael@0 | 2914 | class PropertyProvider { |
michael@0 | 2915 | public: |
michael@0 | 2916 | // Detect hyphenation break opportunities in the given range; breaks |
michael@0 | 2917 | // not at cluster boundaries will be ignored. |
michael@0 | 2918 | virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength, |
michael@0 | 2919 | bool *aBreakBefore) = 0; |
michael@0 | 2920 | |
michael@0 | 2921 | // Returns the provider's hyphenation setting, so callers can decide |
michael@0 | 2922 | // whether it is necessary to call GetHyphenationBreaks. |
michael@0 | 2923 | // Result is an NS_STYLE_HYPHENS_* value. |
michael@0 | 2924 | virtual int8_t GetHyphensOption() = 0; |
michael@0 | 2925 | |
michael@0 | 2926 | // Returns the extra width that will be consumed by a hyphen. This should |
michael@0 | 2927 | // be constant for a given textrun. |
michael@0 | 2928 | virtual gfxFloat GetHyphenWidth() = 0; |
michael@0 | 2929 | |
michael@0 | 2930 | typedef gfxFont::Spacing Spacing; |
michael@0 | 2931 | |
michael@0 | 2932 | /** |
michael@0 | 2933 | * Get the spacing around the indicated characters. Spacing must be zero |
michael@0 | 2934 | * inside clusters. In other words, if character i is not |
michael@0 | 2935 | * CLUSTER_START, then character i-1 must have zero after-spacing and |
michael@0 | 2936 | * character i must have zero before-spacing. |
michael@0 | 2937 | */ |
michael@0 | 2938 | virtual void GetSpacing(uint32_t aStart, uint32_t aLength, |
michael@0 | 2939 | Spacing *aSpacing) = 0; |
michael@0 | 2940 | |
michael@0 | 2941 | // Returns a gfxContext that can be used to measure the hyphen glyph. |
michael@0 | 2942 | // Only called if the hyphen width is requested. |
michael@0 | 2943 | virtual already_AddRefed<gfxContext> GetContext() = 0; |
michael@0 | 2944 | |
michael@0 | 2945 | // Return the appUnitsPerDevUnit value to be used when measuring. |
michael@0 | 2946 | // Only called if the hyphen width is requested. |
michael@0 | 2947 | virtual uint32_t GetAppUnitsPerDevUnit() = 0; |
michael@0 | 2948 | }; |
michael@0 | 2949 | |
michael@0 | 2950 | class ClusterIterator { |
michael@0 | 2951 | public: |
michael@0 | 2952 | ClusterIterator(gfxTextRun *aTextRun); |
michael@0 | 2953 | |
michael@0 | 2954 | void Reset(); |
michael@0 | 2955 | |
michael@0 | 2956 | bool NextCluster(); |
michael@0 | 2957 | |
michael@0 | 2958 | uint32_t Position() const { |
michael@0 | 2959 | return mCurrentChar; |
michael@0 | 2960 | } |
michael@0 | 2961 | |
michael@0 | 2962 | uint32_t ClusterLength() const; |
michael@0 | 2963 | |
michael@0 | 2964 | gfxFloat ClusterAdvance(PropertyProvider *aProvider) const; |
michael@0 | 2965 | |
michael@0 | 2966 | private: |
michael@0 | 2967 | gfxTextRun *mTextRun; |
michael@0 | 2968 | uint32_t mCurrentChar; |
michael@0 | 2969 | }; |
michael@0 | 2970 | |
michael@0 | 2971 | /** |
michael@0 | 2972 | * Draws a substring. Uses only GetSpacing from aBreakProvider. |
michael@0 | 2973 | * The provided point is the baseline origin on the left of the string |
michael@0 | 2974 | * for LTR, on the right of the string for RTL. |
michael@0 | 2975 | * @param aAdvanceWidth if non-null, the advance width of the substring |
michael@0 | 2976 | * is returned here. |
michael@0 | 2977 | * |
michael@0 | 2978 | * Drawing should respect advance widths in the sense that for LTR runs, |
michael@0 | 2979 | * Draw(ctx, pt, offset1, length1, dirty, &provider, &advance) followed by |
michael@0 | 2980 | * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1 + length1, length2, |
michael@0 | 2981 | * dirty, &provider, nullptr) should have the same effect as |
michael@0 | 2982 | * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr). |
michael@0 | 2983 | * For RTL runs the rule is: |
michael@0 | 2984 | * Draw(ctx, pt, offset1 + length1, length2, dirty, &provider, &advance) followed by |
michael@0 | 2985 | * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1, length1, |
michael@0 | 2986 | * dirty, &provider, nullptr) should have the same effect as |
michael@0 | 2987 | * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr). |
michael@0 | 2988 | * |
michael@0 | 2989 | * Glyphs should be drawn in logical content order, which can be significant |
michael@0 | 2990 | * if they overlap (perhaps due to negative spacing). |
michael@0 | 2991 | */ |
michael@0 | 2992 | void Draw(gfxContext *aContext, gfxPoint aPt, |
michael@0 | 2993 | DrawMode aDrawMode, |
michael@0 | 2994 | uint32_t aStart, uint32_t aLength, |
michael@0 | 2995 | PropertyProvider *aProvider, |
michael@0 | 2996 | gfxFloat *aAdvanceWidth, gfxTextContextPaint *aContextPaint, |
michael@0 | 2997 | gfxTextRunDrawCallbacks *aCallbacks = nullptr); |
michael@0 | 2998 | |
michael@0 | 2999 | /** |
michael@0 | 3000 | * Computes the ReflowMetrics for a substring. |
michael@0 | 3001 | * Uses GetSpacing from aBreakProvider. |
michael@0 | 3002 | * @param aBoundingBoxType which kind of bounding box (loose/tight) |
michael@0 | 3003 | */ |
michael@0 | 3004 | Metrics MeasureText(uint32_t aStart, uint32_t aLength, |
michael@0 | 3005 | gfxFont::BoundingBoxType aBoundingBoxType, |
michael@0 | 3006 | gfxContext *aRefContextForTightBoundingBox, |
michael@0 | 3007 | PropertyProvider *aProvider); |
michael@0 | 3008 | |
michael@0 | 3009 | /** |
michael@0 | 3010 | * Computes just the advance width for a substring. |
michael@0 | 3011 | * Uses GetSpacing from aBreakProvider. |
michael@0 | 3012 | */ |
michael@0 | 3013 | gfxFloat GetAdvanceWidth(uint32_t aStart, uint32_t aLength, |
michael@0 | 3014 | PropertyProvider *aProvider); |
michael@0 | 3015 | |
michael@0 | 3016 | /** |
michael@0 | 3017 | * Clear all stored line breaks for the given range (both before and after), |
michael@0 | 3018 | * and then set the line-break state before aStart to aBreakBefore and |
michael@0 | 3019 | * after the last cluster to aBreakAfter. |
michael@0 | 3020 | * |
michael@0 | 3021 | * We require that before and after line breaks be consistent. For clusters |
michael@0 | 3022 | * i and i+1, we require that if there is a break after cluster i, a break |
michael@0 | 3023 | * will be specified before cluster i+1. This may be temporarily violated |
michael@0 | 3024 | * (e.g. after reflowing line L and before reflowing line L+1); to handle |
michael@0 | 3025 | * these temporary violations, we say that there is a break betwen i and i+1 |
michael@0 | 3026 | * if a break is specified after i OR a break is specified before i+1. |
michael@0 | 3027 | * |
michael@0 | 3028 | * This can change textrun geometry! The existence of a linebreak can affect |
michael@0 | 3029 | * the advance width of the cluster before the break (when kerning) or the |
michael@0 | 3030 | * geometry of one cluster before the break or any number of clusters |
michael@0 | 3031 | * after the break. (The one-cluster-before-the-break limit is somewhat |
michael@0 | 3032 | * arbitrary; if some scripts require breaking it, then we need to |
michael@0 | 3033 | * alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase |
michael@0 | 3034 | * it could affect the layout of frames before it...) |
michael@0 | 3035 | * |
michael@0 | 3036 | * We return true if glyphs or geometry changed, false otherwise. This |
michael@0 | 3037 | * function is virtual so that gfxTextRun subclasses can reshape |
michael@0 | 3038 | * properly. |
michael@0 | 3039 | * |
michael@0 | 3040 | * @param aAdvanceWidthDelta if non-null, returns the change in advance |
michael@0 | 3041 | * width of the given range. |
michael@0 | 3042 | */ |
michael@0 | 3043 | virtual bool SetLineBreaks(uint32_t aStart, uint32_t aLength, |
michael@0 | 3044 | bool aLineBreakBefore, bool aLineBreakAfter, |
michael@0 | 3045 | gfxFloat *aAdvanceWidthDelta, |
michael@0 | 3046 | gfxContext *aRefContext); |
michael@0 | 3047 | |
michael@0 | 3048 | /** |
michael@0 | 3049 | * Finds the longest substring that will fit into the given width. |
michael@0 | 3050 | * Uses GetHyphenationBreaks and GetSpacing from aBreakProvider. |
michael@0 | 3051 | * Guarantees the following: |
michael@0 | 3052 | * -- 0 <= result <= aMaxLength |
michael@0 | 3053 | * -- result is the maximal value of N such that either |
michael@0 | 3054 | * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth |
michael@0 | 3055 | * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth |
michael@0 | 3056 | * OR N == aMaxLength && GetAdvanceWidth(aStart, N) <= aWidth |
michael@0 | 3057 | * where GetAdvanceWidth assumes the effect of |
michael@0 | 3058 | * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider) |
michael@0 | 3059 | * -- if no such N exists, then result is the smallest N such that |
michael@0 | 3060 | * N < aMaxLength && line break at N |
michael@0 | 3061 | * OR N < aMaxLength && hyphen break at N |
michael@0 | 3062 | * OR N == aMaxLength |
michael@0 | 3063 | * |
michael@0 | 3064 | * The call has the effect of |
michael@0 | 3065 | * SetLineBreaks(aStart, result, aLineBreakBefore, result < aMaxLength, aProvider) |
michael@0 | 3066 | * and the returned metrics and the invariants above reflect this. |
michael@0 | 3067 | * |
michael@0 | 3068 | * @param aMaxLength this can be UINT32_MAX, in which case the length used |
michael@0 | 3069 | * is up to the end of the string |
michael@0 | 3070 | * @param aLineBreakBefore set to true if and only if there is an actual |
michael@0 | 3071 | * line break at the start of this string. |
michael@0 | 3072 | * @param aSuppressInitialBreak if true, then we assume there is no possible |
michael@0 | 3073 | * linebreak before aStart. If false, then we will check the internal |
michael@0 | 3074 | * line break opportunity state before deciding whether to return 0 as the |
michael@0 | 3075 | * character to break before. |
michael@0 | 3076 | * @param aTrimWhitespace if non-null, then we allow a trailing run of |
michael@0 | 3077 | * spaces to be trimmed; the width of the space(s) will not be included in |
michael@0 | 3078 | * the measured string width for comparison with the limit aWidth, and |
michael@0 | 3079 | * trimmed spaces will not be included in returned metrics. The width |
michael@0 | 3080 | * of the trimmed spaces will be returned in aTrimWhitespace. |
michael@0 | 3081 | * Trimmed spaces are still counted in the "characters fit" result. |
michael@0 | 3082 | * @param aMetrics if non-null, we fill this in for the returned substring. |
michael@0 | 3083 | * If a hyphenation break was used, the hyphen is NOT included in the returned metrics. |
michael@0 | 3084 | * @param aBoundingBoxType whether to make the bounding box in aMetrics tight |
michael@0 | 3085 | * @param aRefContextForTightBoundingBox a reference context to get the |
michael@0 | 3086 | * tight bounding box, if requested |
michael@0 | 3087 | * @param aUsedHyphenation if non-null, records if we selected a hyphenation break |
michael@0 | 3088 | * @param aLastBreak if non-null and result is aMaxLength, we set this to |
michael@0 | 3089 | * the maximal N such that |
michael@0 | 3090 | * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth |
michael@0 | 3091 | * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth |
michael@0 | 3092 | * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes |
michael@0 | 3093 | * the effect of |
michael@0 | 3094 | * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider) |
michael@0 | 3095 | * |
michael@0 | 3096 | * @param aCanWordWrap true if we can break between any two grapheme |
michael@0 | 3097 | * clusters. This is set by word-wrap: break-word |
michael@0 | 3098 | * |
michael@0 | 3099 | * @param aBreakPriority in/out the priority of the break opportunity |
michael@0 | 3100 | * saved in the line. If we are prioritizing break opportunities, we will |
michael@0 | 3101 | * not set a break with a lower priority. @see gfxBreakPriority. |
michael@0 | 3102 | * |
michael@0 | 3103 | * Note that negative advance widths are possible especially if negative |
michael@0 | 3104 | * spacing is provided. |
michael@0 | 3105 | */ |
michael@0 | 3106 | uint32_t BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength, |
michael@0 | 3107 | bool aLineBreakBefore, gfxFloat aWidth, |
michael@0 | 3108 | PropertyProvider *aProvider, |
michael@0 | 3109 | bool aSuppressInitialBreak, |
michael@0 | 3110 | gfxFloat *aTrimWhitespace, |
michael@0 | 3111 | Metrics *aMetrics, |
michael@0 | 3112 | gfxFont::BoundingBoxType aBoundingBoxType, |
michael@0 | 3113 | gfxContext *aRefContextForTightBoundingBox, |
michael@0 | 3114 | bool *aUsedHyphenation, |
michael@0 | 3115 | uint32_t *aLastBreak, |
michael@0 | 3116 | bool aCanWordWrap, |
michael@0 | 3117 | gfxBreakPriority *aBreakPriority); |
michael@0 | 3118 | |
michael@0 | 3119 | /** |
michael@0 | 3120 | * Update the reference context. |
michael@0 | 3121 | * XXX this is a hack. New text frame does not call this. Use only |
michael@0 | 3122 | * temporarily for old text frame. |
michael@0 | 3123 | */ |
michael@0 | 3124 | void SetContext(gfxContext *aContext) {} |
michael@0 | 3125 | |
michael@0 | 3126 | // Utility getters |
michael@0 | 3127 | |
michael@0 | 3128 | gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; } |
michael@0 | 3129 | void *GetUserData() const { return mUserData; } |
michael@0 | 3130 | void SetUserData(void *aUserData) { mUserData = aUserData; } |
michael@0 | 3131 | uint32_t GetFlags() const { return mFlags; } |
michael@0 | 3132 | void SetFlagBits(uint32_t aFlags) { |
michael@0 | 3133 | NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), |
michael@0 | 3134 | "Only user flags should be mutable"); |
michael@0 | 3135 | mFlags |= aFlags; |
michael@0 | 3136 | } |
michael@0 | 3137 | void ClearFlagBits(uint32_t aFlags) { |
michael@0 | 3138 | NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS), |
michael@0 | 3139 | "Only user flags should be mutable"); |
michael@0 | 3140 | mFlags &= ~aFlags; |
michael@0 | 3141 | } |
michael@0 | 3142 | const gfxSkipChars& GetSkipChars() const { return mSkipChars; } |
michael@0 | 3143 | gfxFontGroup *GetFontGroup() const { return mFontGroup; } |
michael@0 | 3144 | |
michael@0 | 3145 | |
michael@0 | 3146 | // Call this, don't call "new gfxTextRun" directly. This does custom |
michael@0 | 3147 | // allocation and initialization |
michael@0 | 3148 | static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams, |
michael@0 | 3149 | uint32_t aLength, gfxFontGroup *aFontGroup, |
michael@0 | 3150 | uint32_t aFlags); |
michael@0 | 3151 | |
michael@0 | 3152 | // The text is divided into GlyphRuns as necessary |
michael@0 | 3153 | struct GlyphRun { |
michael@0 | 3154 | nsRefPtr<gfxFont> mFont; // never null |
michael@0 | 3155 | uint32_t mCharacterOffset; // into original UTF16 string |
michael@0 | 3156 | uint8_t mMatchType; |
michael@0 | 3157 | }; |
michael@0 | 3158 | |
michael@0 | 3159 | class GlyphRunIterator { |
michael@0 | 3160 | public: |
michael@0 | 3161 | GlyphRunIterator(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aLength) |
michael@0 | 3162 | : mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) { |
michael@0 | 3163 | mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart); |
michael@0 | 3164 | } |
michael@0 | 3165 | bool NextRun(); |
michael@0 | 3166 | GlyphRun *GetGlyphRun() { return mGlyphRun; } |
michael@0 | 3167 | uint32_t GetStringStart() { return mStringStart; } |
michael@0 | 3168 | uint32_t GetStringEnd() { return mStringEnd; } |
michael@0 | 3169 | private: |
michael@0 | 3170 | gfxTextRun *mTextRun; |
michael@0 | 3171 | GlyphRun *mGlyphRun; |
michael@0 | 3172 | uint32_t mStringStart; |
michael@0 | 3173 | uint32_t mStringEnd; |
michael@0 | 3174 | uint32_t mNextIndex; |
michael@0 | 3175 | uint32_t mStartOffset; |
michael@0 | 3176 | uint32_t mEndOffset; |
michael@0 | 3177 | }; |
michael@0 | 3178 | |
michael@0 | 3179 | class GlyphRunOffsetComparator { |
michael@0 | 3180 | public: |
michael@0 | 3181 | bool Equals(const GlyphRun& a, |
michael@0 | 3182 | const GlyphRun& b) const |
michael@0 | 3183 | { |
michael@0 | 3184 | return a.mCharacterOffset == b.mCharacterOffset; |
michael@0 | 3185 | } |
michael@0 | 3186 | |
michael@0 | 3187 | bool LessThan(const GlyphRun& a, |
michael@0 | 3188 | const GlyphRun& b) const |
michael@0 | 3189 | { |
michael@0 | 3190 | return a.mCharacterOffset < b.mCharacterOffset; |
michael@0 | 3191 | } |
michael@0 | 3192 | }; |
michael@0 | 3193 | |
michael@0 | 3194 | friend class GlyphRunIterator; |
michael@0 | 3195 | friend class FontSelector; |
michael@0 | 3196 | |
michael@0 | 3197 | // API for setting up the textrun glyphs. Should only be called by |
michael@0 | 3198 | // things that construct textruns. |
michael@0 | 3199 | /** |
michael@0 | 3200 | * We've found a run of text that should use a particular font. Call this |
michael@0 | 3201 | * only during initialization when font substitution has been computed. |
michael@0 | 3202 | * Call it before setting up the glyphs for the characters in this run; |
michael@0 | 3203 | * SetMissingGlyph requires that the correct glyphrun be installed. |
michael@0 | 3204 | * |
michael@0 | 3205 | * If aForceNewRun, a new glyph run will be added, even if the |
michael@0 | 3206 | * previously added run uses the same font. If glyph runs are |
michael@0 | 3207 | * added out of strictly increasing aStartCharIndex order (via |
michael@0 | 3208 | * force), then SortGlyphRuns must be called after all glyph runs |
michael@0 | 3209 | * are added before any further operations are performed with this |
michael@0 | 3210 | * TextRun. |
michael@0 | 3211 | */ |
michael@0 | 3212 | nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType, |
michael@0 | 3213 | uint32_t aStartCharIndex, bool aForceNewRun); |
michael@0 | 3214 | void ResetGlyphRuns() { mGlyphRuns.Clear(); } |
michael@0 | 3215 | void SortGlyphRuns(); |
michael@0 | 3216 | void SanitizeGlyphRuns(); |
michael@0 | 3217 | |
michael@0 | 3218 | CompressedGlyph* GetCharacterGlyphs() { |
michael@0 | 3219 | NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs"); |
michael@0 | 3220 | return mCharacterGlyphs; |
michael@0 | 3221 | } |
michael@0 | 3222 | |
michael@0 | 3223 | void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex); |
michael@0 | 3224 | |
michael@0 | 3225 | // Set the glyph data for the given character index to the font's |
michael@0 | 3226 | // space glyph, IF this can be done as a "simple" glyph record |
michael@0 | 3227 | // (not requiring a DetailedGlyph entry). This avoids the need to call |
michael@0 | 3228 | // the font shaper and go through the shaped-word cache for most spaces. |
michael@0 | 3229 | // |
michael@0 | 3230 | // The parameter aSpaceChar is the original character code for which |
michael@0 | 3231 | // this space glyph is being used; if this is U+0020, we need to record |
michael@0 | 3232 | // that it could be trimmed at a run edge, whereas other kinds of space |
michael@0 | 3233 | // (currently just U+00A0) would not be trimmable/breakable. |
michael@0 | 3234 | // |
michael@0 | 3235 | // Returns true if it was able to set simple glyph data for the space; |
michael@0 | 3236 | // if it returns false, the caller needs to fall back to some other |
michael@0 | 3237 | // means to create the necessary (detailed) glyph data. |
michael@0 | 3238 | bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, |
michael@0 | 3239 | uint32_t aCharIndex, char16_t aSpaceChar); |
michael@0 | 3240 | |
michael@0 | 3241 | // Record the positions of specific characters that layout may need to |
michael@0 | 3242 | // detect in the textrun, even though it doesn't have an explicit copy |
michael@0 | 3243 | // of the original text. These are recorded using flag bits in the |
michael@0 | 3244 | // CompressedGlyph record; if necessary, we convert "simple" glyph records |
michael@0 | 3245 | // to "complex" ones as the Tab and Newline flags are not present in |
michael@0 | 3246 | // simple CompressedGlyph records. |
michael@0 | 3247 | void SetIsTab(uint32_t aIndex) { |
michael@0 | 3248 | CompressedGlyph *g = &mCharacterGlyphs[aIndex]; |
michael@0 | 3249 | if (g->IsSimpleGlyph()) { |
michael@0 | 3250 | DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); |
michael@0 | 3251 | details->mGlyphID = g->GetSimpleGlyph(); |
michael@0 | 3252 | details->mAdvance = g->GetSimpleAdvance(); |
michael@0 | 3253 | details->mXOffset = details->mYOffset = 0; |
michael@0 | 3254 | SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details); |
michael@0 | 3255 | } |
michael@0 | 3256 | g->SetIsTab(); |
michael@0 | 3257 | } |
michael@0 | 3258 | void SetIsNewline(uint32_t aIndex) { |
michael@0 | 3259 | CompressedGlyph *g = &mCharacterGlyphs[aIndex]; |
michael@0 | 3260 | if (g->IsSimpleGlyph()) { |
michael@0 | 3261 | DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); |
michael@0 | 3262 | details->mGlyphID = g->GetSimpleGlyph(); |
michael@0 | 3263 | details->mAdvance = g->GetSimpleAdvance(); |
michael@0 | 3264 | details->mXOffset = details->mYOffset = 0; |
michael@0 | 3265 | SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details); |
michael@0 | 3266 | } |
michael@0 | 3267 | g->SetIsNewline(); |
michael@0 | 3268 | } |
michael@0 | 3269 | void SetIsLowSurrogate(uint32_t aIndex) { |
michael@0 | 3270 | SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr); |
michael@0 | 3271 | mCharacterGlyphs[aIndex].SetIsLowSurrogate(); |
michael@0 | 3272 | } |
michael@0 | 3273 | |
michael@0 | 3274 | /** |
michael@0 | 3275 | * Prefetch all the glyph extents needed to ensure that Measure calls |
michael@0 | 3276 | * on this textrun not requesting tight boundingBoxes will succeed. Note |
michael@0 | 3277 | * that some glyph extents might not be fetched due to OOM or other |
michael@0 | 3278 | * errors. |
michael@0 | 3279 | */ |
michael@0 | 3280 | void FetchGlyphExtents(gfxContext *aRefContext); |
michael@0 | 3281 | |
michael@0 | 3282 | uint32_t CountMissingGlyphs(); |
michael@0 | 3283 | const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) { |
michael@0 | 3284 | *aNumGlyphRuns = mGlyphRuns.Length(); |
michael@0 | 3285 | return mGlyphRuns.Elements(); |
michael@0 | 3286 | } |
michael@0 | 3287 | // Returns the index of the GlyphRun containing the given offset. |
michael@0 | 3288 | // Returns mGlyphRuns.Length() when aOffset is mCharacterCount. |
michael@0 | 3289 | uint32_t FindFirstGlyphRunContaining(uint32_t aOffset); |
michael@0 | 3290 | |
michael@0 | 3291 | // Copy glyph data from a ShapedWord into this textrun. |
michael@0 | 3292 | void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart); |
michael@0 | 3293 | |
michael@0 | 3294 | // Copy glyph data for a range of characters from aSource to this |
michael@0 | 3295 | // textrun. |
michael@0 | 3296 | void CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart, |
michael@0 | 3297 | uint32_t aLength, uint32_t aDest); |
michael@0 | 3298 | |
michael@0 | 3299 | nsExpirationState *GetExpirationState() { return &mExpirationState; } |
michael@0 | 3300 | |
michael@0 | 3301 | // Tell the textrun to release its reference to its creating gfxFontGroup |
michael@0 | 3302 | // immediately, rather than on destruction. This is used for textruns |
michael@0 | 3303 | // that are actually owned by a gfxFontGroup, so that they don't keep it |
michael@0 | 3304 | // permanently alive due to a circular reference. (The caller of this is |
michael@0 | 3305 | // taking responsibility for ensuring the textrun will not outlive its |
michael@0 | 3306 | // mFontGroup.) |
michael@0 | 3307 | void ReleaseFontGroup(); |
michael@0 | 3308 | |
michael@0 | 3309 | struct LigatureData { |
michael@0 | 3310 | // textrun offsets of the start and end of the containing ligature |
michael@0 | 3311 | uint32_t mLigatureStart; |
michael@0 | 3312 | uint32_t mLigatureEnd; |
michael@0 | 3313 | // appunits advance to the start of the ligature part within the ligature; |
michael@0 | 3314 | // never includes any spacing |
michael@0 | 3315 | gfxFloat mPartAdvance; |
michael@0 | 3316 | // appunits width of the ligature part; includes before-spacing |
michael@0 | 3317 | // when the part is at the start of the ligature, and after-spacing |
michael@0 | 3318 | // when the part is as the end of the ligature |
michael@0 | 3319 | gfxFloat mPartWidth; |
michael@0 | 3320 | |
michael@0 | 3321 | bool mClipBeforePart; |
michael@0 | 3322 | bool mClipAfterPart; |
michael@0 | 3323 | }; |
michael@0 | 3324 | |
michael@0 | 3325 | // return storage used by this run, for memory reporter; |
michael@0 | 3326 | // nsTransformedTextRun needs to override this as it holds additional data |
michael@0 | 3327 | virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) |
michael@0 | 3328 | MOZ_MUST_OVERRIDE; |
michael@0 | 3329 | virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) |
michael@0 | 3330 | MOZ_MUST_OVERRIDE; |
michael@0 | 3331 | |
michael@0 | 3332 | // Get the size, if it hasn't already been gotten, marking as it goes. |
michael@0 | 3333 | size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { |
michael@0 | 3334 | if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) { |
michael@0 | 3335 | return 0; |
michael@0 | 3336 | } |
michael@0 | 3337 | mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; |
michael@0 | 3338 | return SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 3339 | } |
michael@0 | 3340 | void ResetSizeOfAccountingFlags() { |
michael@0 | 3341 | mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED; |
michael@0 | 3342 | } |
michael@0 | 3343 | |
michael@0 | 3344 | #ifdef DEBUG |
michael@0 | 3345 | void Dump(FILE* aOutput); |
michael@0 | 3346 | #endif |
michael@0 | 3347 | |
michael@0 | 3348 | protected: |
michael@0 | 3349 | /** |
michael@0 | 3350 | * Create a textrun, and set its mCharacterGlyphs to point immediately |
michael@0 | 3351 | * after the base object; this is ONLY used in conjunction with placement |
michael@0 | 3352 | * new, after allocating a block large enough for the glyph records to |
michael@0 | 3353 | * follow the base textrun object. |
michael@0 | 3354 | */ |
michael@0 | 3355 | gfxTextRun(const gfxTextRunFactory::Parameters *aParams, |
michael@0 | 3356 | uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags); |
michael@0 | 3357 | |
michael@0 | 3358 | /** |
michael@0 | 3359 | * Helper for the Create() factory method to allocate the required |
michael@0 | 3360 | * glyph storage for a textrun object with the basic size aSize, |
michael@0 | 3361 | * plus room for aLength glyph records. |
michael@0 | 3362 | */ |
michael@0 | 3363 | static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength); |
michael@0 | 3364 | |
michael@0 | 3365 | // Pointer to the array of CompressedGlyph records; must be initialized |
michael@0 | 3366 | // when the object is constructed. |
michael@0 | 3367 | CompressedGlyph *mCharacterGlyphs; |
michael@0 | 3368 | |
michael@0 | 3369 | private: |
michael@0 | 3370 | // **** general helpers **** |
michael@0 | 3371 | |
michael@0 | 3372 | // Allocate aCount DetailedGlyphs for the given index |
michael@0 | 3373 | DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex, uint32_t aCount); |
michael@0 | 3374 | |
michael@0 | 3375 | // Get the total advance for a range of glyphs. |
michael@0 | 3376 | int32_t GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd); |
michael@0 | 3377 | |
michael@0 | 3378 | // Spacing for characters outside the range aSpacingStart/aSpacingEnd |
michael@0 | 3379 | // is assumed to be zero; such characters are not passed to aProvider. |
michael@0 | 3380 | // This is useful to protect aProvider from being passed character indices |
michael@0 | 3381 | // it is not currently able to handle. |
michael@0 | 3382 | bool GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd, |
michael@0 | 3383 | PropertyProvider *aProvider, |
michael@0 | 3384 | uint32_t aSpacingStart, uint32_t aSpacingEnd, |
michael@0 | 3385 | nsTArray<PropertyProvider::Spacing> *aSpacing); |
michael@0 | 3386 | |
michael@0 | 3387 | // **** ligature helpers **** |
michael@0 | 3388 | // (Platforms do the actual ligaturization, but we need to do a bunch of stuff |
michael@0 | 3389 | // to handle requests that begin or end inside a ligature) |
michael@0 | 3390 | |
michael@0 | 3391 | // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero |
michael@0 | 3392 | LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd, |
michael@0 | 3393 | PropertyProvider *aProvider); |
michael@0 | 3394 | gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd, |
michael@0 | 3395 | PropertyProvider *aProvider); |
michael@0 | 3396 | void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, |
michael@0 | 3397 | uint32_t aStart, uint32_t aEnd, gfxPoint *aPt, |
michael@0 | 3398 | PropertyProvider *aProvider, |
michael@0 | 3399 | gfxTextRunDrawCallbacks *aCallbacks); |
michael@0 | 3400 | // Advance aStart to the start of the nearest ligature; back up aEnd |
michael@0 | 3401 | // to the nearest ligature end; may result in *aStart == *aEnd |
michael@0 | 3402 | void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd); |
michael@0 | 3403 | // result in appunits |
michael@0 | 3404 | gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider); |
michael@0 | 3405 | void AccumulatePartialLigatureMetrics(gfxFont *aFont, |
michael@0 | 3406 | uint32_t aStart, uint32_t aEnd, |
michael@0 | 3407 | gfxFont::BoundingBoxType aBoundingBoxType, |
michael@0 | 3408 | gfxContext *aRefContext, |
michael@0 | 3409 | PropertyProvider *aProvider, |
michael@0 | 3410 | Metrics *aMetrics); |
michael@0 | 3411 | |
michael@0 | 3412 | // **** measurement helper **** |
michael@0 | 3413 | void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd, |
michael@0 | 3414 | gfxFont::BoundingBoxType aBoundingBoxType, |
michael@0 | 3415 | gfxContext *aRefContext, |
michael@0 | 3416 | PropertyProvider *aProvider, |
michael@0 | 3417 | uint32_t aSpacingStart, uint32_t aSpacingEnd, |
michael@0 | 3418 | Metrics *aMetrics); |
michael@0 | 3419 | |
michael@0 | 3420 | // **** drawing helper **** |
michael@0 | 3421 | void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, |
michael@0 | 3422 | DrawMode aDrawMode, gfxPoint *aPt, |
michael@0 | 3423 | gfxTextContextPaint *aContextPaint, uint32_t aStart, |
michael@0 | 3424 | uint32_t aEnd, PropertyProvider *aProvider, |
michael@0 | 3425 | uint32_t aSpacingStart, uint32_t aSpacingEnd, |
michael@0 | 3426 | gfxTextRunDrawCallbacks *aCallbacks); |
michael@0 | 3427 | |
michael@0 | 3428 | // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*, |
michael@0 | 3429 | // for smaller size especially in the super-common one-glyphrun case |
michael@0 | 3430 | nsAutoTArray<GlyphRun,1> mGlyphRuns; |
michael@0 | 3431 | |
michael@0 | 3432 | void *mUserData; |
michael@0 | 3433 | gfxFontGroup *mFontGroup; // addrefed on creation, but our reference |
michael@0 | 3434 | // may be released by ReleaseFontGroup() |
michael@0 | 3435 | gfxSkipChars mSkipChars; |
michael@0 | 3436 | nsExpirationState mExpirationState; |
michael@0 | 3437 | |
michael@0 | 3438 | bool mSkipDrawing; // true if the font group we used had a user font |
michael@0 | 3439 | // download that's in progress, so we should hide text |
michael@0 | 3440 | // until the download completes (or timeout fires) |
michael@0 | 3441 | bool mReleasedFontGroup; // we already called NS_RELEASE on |
michael@0 | 3442 | // mFontGroup, so don't do it again |
michael@0 | 3443 | }; |
michael@0 | 3444 | |
michael@0 | 3445 | class gfxFontGroup : public gfxTextRunFactory { |
michael@0 | 3446 | public: |
michael@0 | 3447 | class FamilyFace { |
michael@0 | 3448 | public: |
michael@0 | 3449 | FamilyFace() { } |
michael@0 | 3450 | |
michael@0 | 3451 | FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont) |
michael@0 | 3452 | : mFamily(aFamily), mFont(aFont) |
michael@0 | 3453 | { |
michael@0 | 3454 | NS_ASSERTION(aFont, "font pointer must not be null"); |
michael@0 | 3455 | NS_ASSERTION(!aFamily || |
michael@0 | 3456 | aFamily->ContainsFace(aFont->GetFontEntry()), |
michael@0 | 3457 | "font is not a member of the given family"); |
michael@0 | 3458 | } |
michael@0 | 3459 | |
michael@0 | 3460 | gfxFontFamily* Family() const { return mFamily.get(); } |
michael@0 | 3461 | gfxFont* Font() const { return mFont.get(); } |
michael@0 | 3462 | |
michael@0 | 3463 | private: |
michael@0 | 3464 | nsRefPtr<gfxFontFamily> mFamily; |
michael@0 | 3465 | nsRefPtr<gfxFont> mFont; |
michael@0 | 3466 | }; |
michael@0 | 3467 | |
michael@0 | 3468 | static void Shutdown(); // platform must call this to release the languageAtomService |
michael@0 | 3469 | |
michael@0 | 3470 | gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet = nullptr); |
michael@0 | 3471 | |
michael@0 | 3472 | virtual ~gfxFontGroup(); |
michael@0 | 3473 | |
michael@0 | 3474 | virtual gfxFont *GetFontAt(int32_t i) { |
michael@0 | 3475 | // If it turns out to be hard for all clients that cache font |
michael@0 | 3476 | // groups to call UpdateFontList at appropriate times, we could |
michael@0 | 3477 | // instead consider just calling UpdateFontList from someplace |
michael@0 | 3478 | // more central (such as here). |
michael@0 | 3479 | NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(), |
michael@0 | 3480 | "Whoever was caching this font group should have " |
michael@0 | 3481 | "called UpdateFontList on it"); |
michael@0 | 3482 | NS_ASSERTION(mFonts.Length() > uint32_t(i) && mFonts[i].Font(), |
michael@0 | 3483 | "Requesting a font index that doesn't exist"); |
michael@0 | 3484 | |
michael@0 | 3485 | return mFonts[i].Font(); |
michael@0 | 3486 | } |
michael@0 | 3487 | |
michael@0 | 3488 | uint32_t FontListLength() const { |
michael@0 | 3489 | return mFonts.Length(); |
michael@0 | 3490 | } |
michael@0 | 3491 | |
michael@0 | 3492 | bool Equals(const gfxFontGroup& other) const { |
michael@0 | 3493 | return mFamilies.Equals(other.mFamilies) && |
michael@0 | 3494 | mStyle.Equals(other.mStyle); |
michael@0 | 3495 | } |
michael@0 | 3496 | |
michael@0 | 3497 | const gfxFontStyle *GetStyle() const { return &mStyle; } |
michael@0 | 3498 | |
michael@0 | 3499 | virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle); |
michael@0 | 3500 | |
michael@0 | 3501 | /** |
michael@0 | 3502 | * The listed characters should be treated as invisible and zero-width |
michael@0 | 3503 | * when creating textruns. |
michael@0 | 3504 | */ |
michael@0 | 3505 | static bool IsInvalidChar(uint8_t ch); |
michael@0 | 3506 | static bool IsInvalidChar(char16_t ch); |
michael@0 | 3507 | |
michael@0 | 3508 | /** |
michael@0 | 3509 | * Make a textrun for a given string. |
michael@0 | 3510 | * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the |
michael@0 | 3511 | * textrun will copy it. |
michael@0 | 3512 | * This calls FetchGlyphExtents on the textrun. |
michael@0 | 3513 | */ |
michael@0 | 3514 | virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength, |
michael@0 | 3515 | const Parameters *aParams, uint32_t aFlags); |
michael@0 | 3516 | /** |
michael@0 | 3517 | * Make a textrun for a given string. |
michael@0 | 3518 | * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the |
michael@0 | 3519 | * textrun will copy it. |
michael@0 | 3520 | * This calls FetchGlyphExtents on the textrun. |
michael@0 | 3521 | */ |
michael@0 | 3522 | virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength, |
michael@0 | 3523 | const Parameters *aParams, uint32_t aFlags); |
michael@0 | 3524 | |
michael@0 | 3525 | /** |
michael@0 | 3526 | * Textrun creation helper for clients that don't want to pass |
michael@0 | 3527 | * a full Parameters record. |
michael@0 | 3528 | */ |
michael@0 | 3529 | template<typename T> |
michael@0 | 3530 | gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength, |
michael@0 | 3531 | gfxContext *aRefContext, |
michael@0 | 3532 | int32_t aAppUnitsPerDevUnit, |
michael@0 | 3533 | uint32_t aFlags) |
michael@0 | 3534 | { |
michael@0 | 3535 | gfxTextRunFactory::Parameters params = { |
michael@0 | 3536 | aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit |
michael@0 | 3537 | }; |
michael@0 | 3538 | return MakeTextRun(aString, aLength, ¶ms, aFlags); |
michael@0 | 3539 | } |
michael@0 | 3540 | |
michael@0 | 3541 | /** |
michael@0 | 3542 | * Get the (possibly-cached) width of the hyphen character. |
michael@0 | 3543 | * The aCtx and aAppUnitsPerDevUnit parameters will be used only if |
michael@0 | 3544 | * needed to initialize the cached hyphen width; otherwise they are |
michael@0 | 3545 | * ignored. |
michael@0 | 3546 | */ |
michael@0 | 3547 | gfxFloat GetHyphenWidth(gfxTextRun::PropertyProvider* aProvider); |
michael@0 | 3548 | |
michael@0 | 3549 | /** |
michael@0 | 3550 | * Make a text run representing a single hyphen character. |
michael@0 | 3551 | * This will use U+2010 HYPHEN if available in the first font, |
michael@0 | 3552 | * otherwise fall back to U+002D HYPHEN-MINUS. |
michael@0 | 3553 | * The caller is responsible for deleting the returned text run |
michael@0 | 3554 | * when no longer required. |
michael@0 | 3555 | */ |
michael@0 | 3556 | gfxTextRun *MakeHyphenTextRun(gfxContext *aCtx, |
michael@0 | 3557 | uint32_t aAppUnitsPerDevUnit); |
michael@0 | 3558 | |
michael@0 | 3559 | /* helper function for splitting font families on commas and |
michael@0 | 3560 | * calling a function for each family to fill the mFonts array |
michael@0 | 3561 | */ |
michael@0 | 3562 | typedef bool (*FontCreationCallback) (const nsAString& aName, |
michael@0 | 3563 | const nsACString& aGenericName, |
michael@0 | 3564 | bool aUseFontSet, |
michael@0 | 3565 | void *closure); |
michael@0 | 3566 | bool ForEachFont(const nsAString& aFamilies, |
michael@0 | 3567 | nsIAtom *aLanguage, |
michael@0 | 3568 | FontCreationCallback fc, |
michael@0 | 3569 | void *closure); |
michael@0 | 3570 | bool ForEachFont(FontCreationCallback fc, void *closure); |
michael@0 | 3571 | |
michael@0 | 3572 | /** |
michael@0 | 3573 | * Check whether a given font (specified by its gfxFontEntry) |
michael@0 | 3574 | * is already in the fontgroup's list of actual fonts |
michael@0 | 3575 | */ |
michael@0 | 3576 | bool HasFont(const gfxFontEntry *aFontEntry); |
michael@0 | 3577 | |
michael@0 | 3578 | const nsString& GetFamilies() { return mFamilies; } |
michael@0 | 3579 | |
michael@0 | 3580 | // This returns the preferred underline for this font group. |
michael@0 | 3581 | // Some CJK fonts have wrong underline offset in its metrics. |
michael@0 | 3582 | // If this group has such "bad" font, each platform's gfxFontGroup initialized mUnderlineOffset. |
michael@0 | 3583 | // The value should be lower value of first font's metrics and the bad font's metrics. |
michael@0 | 3584 | // Otherwise, this returns from first font's metrics. |
michael@0 | 3585 | enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX }; |
michael@0 | 3586 | virtual gfxFloat GetUnderlineOffset() { |
michael@0 | 3587 | if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) |
michael@0 | 3588 | mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset; |
michael@0 | 3589 | return mUnderlineOffset; |
michael@0 | 3590 | } |
michael@0 | 3591 | |
michael@0 | 3592 | virtual already_AddRefed<gfxFont> |
michael@0 | 3593 | FindFontForChar(uint32_t ch, uint32_t prevCh, int32_t aRunScript, |
michael@0 | 3594 | gfxFont *aPrevMatchedFont, |
michael@0 | 3595 | uint8_t *aMatchType); |
michael@0 | 3596 | |
michael@0 | 3597 | // search through pref fonts for a character, return nullptr if no matching pref font |
michael@0 | 3598 | virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh); |
michael@0 | 3599 | |
michael@0 | 3600 | virtual already_AddRefed<gfxFont> |
michael@0 | 3601 | WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript); |
michael@0 | 3602 | |
michael@0 | 3603 | template<typename T> |
michael@0 | 3604 | void ComputeRanges(nsTArray<gfxTextRange>& mRanges, |
michael@0 | 3605 | const T *aString, uint32_t aLength, |
michael@0 | 3606 | int32_t aRunScript); |
michael@0 | 3607 | |
michael@0 | 3608 | gfxUserFontSet* GetUserFontSet(); |
michael@0 | 3609 | |
michael@0 | 3610 | // With downloadable fonts, the composition of the font group can change as fonts are downloaded |
michael@0 | 3611 | // for each change in state of the user font set, the generation value is bumped to avoid picking up |
michael@0 | 3612 | // previously created text runs in the text run word cache. For font groups based on stylesheets |
michael@0 | 3613 | // with no @font-face rule, this always returns 0. |
michael@0 | 3614 | uint64_t GetGeneration(); |
michael@0 | 3615 | |
michael@0 | 3616 | // used when logging text performance |
michael@0 | 3617 | gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; } |
michael@0 | 3618 | void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; } |
michael@0 | 3619 | |
michael@0 | 3620 | // This will call UpdateFontList() if the user font set is changed. |
michael@0 | 3621 | void SetUserFontSet(gfxUserFontSet *aUserFontSet); |
michael@0 | 3622 | |
michael@0 | 3623 | // If there is a user font set, check to see whether the font list or any |
michael@0 | 3624 | // caches need updating. |
michael@0 | 3625 | virtual void UpdateFontList(); |
michael@0 | 3626 | |
michael@0 | 3627 | bool ShouldSkipDrawing() const { |
michael@0 | 3628 | return mSkipDrawing; |
michael@0 | 3629 | } |
michael@0 | 3630 | |
michael@0 | 3631 | class LazyReferenceContextGetter { |
michael@0 | 3632 | public: |
michael@0 | 3633 | virtual already_AddRefed<gfxContext> GetRefContext() = 0; |
michael@0 | 3634 | }; |
michael@0 | 3635 | // The gfxFontGroup keeps ownership of this textrun. |
michael@0 | 3636 | // It is only guaranteed to exist until the next call to GetEllipsisTextRun |
michael@0 | 3637 | // (which might use a different appUnitsPerDev value) for the font group, |
michael@0 | 3638 | // or until UpdateFontList is called, or the fontgroup is destroyed. |
michael@0 | 3639 | // Get it/use it/forget it :) - don't keep a reference that might go stale. |
michael@0 | 3640 | gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, |
michael@0 | 3641 | LazyReferenceContextGetter& aRefContextGetter); |
michael@0 | 3642 | |
michael@0 | 3643 | protected: |
michael@0 | 3644 | nsString mFamilies; |
michael@0 | 3645 | gfxFontStyle mStyle; |
michael@0 | 3646 | nsTArray<FamilyFace> mFonts; |
michael@0 | 3647 | gfxFloat mUnderlineOffset; |
michael@0 | 3648 | gfxFloat mHyphenWidth; |
michael@0 | 3649 | |
michael@0 | 3650 | nsRefPtr<gfxUserFontSet> mUserFontSet; |
michael@0 | 3651 | uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed |
michael@0 | 3652 | |
michael@0 | 3653 | gfxTextPerfMetrics *mTextPerf; |
michael@0 | 3654 | |
michael@0 | 3655 | // Cache a textrun representing an ellipsis (useful for CSS text-overflow) |
michael@0 | 3656 | // at a specific appUnitsPerDevPixel size |
michael@0 | 3657 | nsAutoPtr<gfxTextRun> mCachedEllipsisTextRun; |
michael@0 | 3658 | |
michael@0 | 3659 | // cache the most recent pref font to avoid general pref font lookup |
michael@0 | 3660 | nsRefPtr<gfxFontFamily> mLastPrefFamily; |
michael@0 | 3661 | nsRefPtr<gfxFont> mLastPrefFont; |
michael@0 | 3662 | eFontPrefLang mLastPrefLang; // lang group for last pref font |
michael@0 | 3663 | eFontPrefLang mPageLang; |
michael@0 | 3664 | bool mLastPrefFirstFont; // is this the first font in the list of pref fonts for this lang group? |
michael@0 | 3665 | |
michael@0 | 3666 | bool mSkipDrawing; // hide text while waiting for a font |
michael@0 | 3667 | // download to complete (or fallback |
michael@0 | 3668 | // timer to fire) |
michael@0 | 3669 | |
michael@0 | 3670 | /** |
michael@0 | 3671 | * Textrun creation short-cuts for special cases where we don't need to |
michael@0 | 3672 | * call a font shaper to generate glyphs. |
michael@0 | 3673 | */ |
michael@0 | 3674 | gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags); |
michael@0 | 3675 | gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags); |
michael@0 | 3676 | gfxTextRun *MakeBlankTextRun(uint32_t aLength, |
michael@0 | 3677 | const Parameters *aParams, uint32_t aFlags); |
michael@0 | 3678 | |
michael@0 | 3679 | // Initialize the list of fonts |
michael@0 | 3680 | void BuildFontList(); |
michael@0 | 3681 | |
michael@0 | 3682 | // Init this font group's font metrics. If there no bad fonts, you don't need to call this. |
michael@0 | 3683 | // But if there are one or more bad fonts which have bad underline offset, |
michael@0 | 3684 | // you should call this with the *first* bad font. |
michael@0 | 3685 | void InitMetricsForBadFont(gfxFont* aBadFont); |
michael@0 | 3686 | |
michael@0 | 3687 | // Set up the textrun glyphs for an entire text run: |
michael@0 | 3688 | // find script runs, and then call InitScriptRun for each |
michael@0 | 3689 | template<typename T> |
michael@0 | 3690 | void InitTextRun(gfxContext *aContext, |
michael@0 | 3691 | gfxTextRun *aTextRun, |
michael@0 | 3692 | const T *aString, |
michael@0 | 3693 | uint32_t aLength); |
michael@0 | 3694 | |
michael@0 | 3695 | // InitTextRun helper to handle a single script run, by finding font ranges |
michael@0 | 3696 | // and calling each font's InitTextRun() as appropriate |
michael@0 | 3697 | template<typename T> |
michael@0 | 3698 | void InitScriptRun(gfxContext *aContext, |
michael@0 | 3699 | gfxTextRun *aTextRun, |
michael@0 | 3700 | const T *aString, |
michael@0 | 3701 | uint32_t aScriptRunStart, |
michael@0 | 3702 | uint32_t aScriptRunEnd, |
michael@0 | 3703 | int32_t aRunScript); |
michael@0 | 3704 | |
michael@0 | 3705 | /* If aResolveGeneric is true, then CSS/Gecko generic family names are |
michael@0 | 3706 | * replaced with preferred fonts. |
michael@0 | 3707 | * |
michael@0 | 3708 | * If aResolveFontName is true then fc() is called only for existing fonts |
michael@0 | 3709 | * and with actual font names. If false then fc() is called with each |
michael@0 | 3710 | * family name in aFamilies (after resolving CSS/Gecko generic family names |
michael@0 | 3711 | * if aResolveGeneric). |
michael@0 | 3712 | * If aUseFontSet is true, the fontgroup's user font set is checked; |
michael@0 | 3713 | * if false then it is skipped. |
michael@0 | 3714 | */ |
michael@0 | 3715 | bool ForEachFontInternal(const nsAString& aFamilies, |
michael@0 | 3716 | nsIAtom *aLanguage, |
michael@0 | 3717 | bool aResolveGeneric, |
michael@0 | 3718 | bool aResolveFontName, |
michael@0 | 3719 | bool aUseFontSet, |
michael@0 | 3720 | FontCreationCallback fc, |
michael@0 | 3721 | void *closure); |
michael@0 | 3722 | |
michael@0 | 3723 | // Helper for font-matching: |
michael@0 | 3724 | // see if aCh is supported in any of the faces from aFamily; |
michael@0 | 3725 | // if so return the best style match, else return null. |
michael@0 | 3726 | already_AddRefed<gfxFont> TryAllFamilyMembers(gfxFontFamily* aFamily, |
michael@0 | 3727 | uint32_t aCh); |
michael@0 | 3728 | |
michael@0 | 3729 | static bool FontResolverProc(const nsAString& aName, void *aClosure); |
michael@0 | 3730 | |
michael@0 | 3731 | static bool FindPlatformFont(const nsAString& aName, |
michael@0 | 3732 | const nsACString& aGenericName, |
michael@0 | 3733 | bool aUseFontSet, |
michael@0 | 3734 | void *closure); |
michael@0 | 3735 | |
michael@0 | 3736 | static NS_HIDDEN_(nsILanguageAtomService*) gLangService; |
michael@0 | 3737 | }; |
michael@0 | 3738 | #endif |