--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -6,1862 +6,85 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/MathAlgorithms.h"
#ifdef MOZ_LOGGING
#define FORCE_PR_LOG /* Allow logging in the release build */
#endif
#include "prlog.h"
-#include "nsServiceManagerUtils.h"
#include "nsExpirationTracker.h"
-#include "nsILanguageAtomService.h"
#include "nsITimer.h"
#include "gfxFont.h"
+#include "gfxGlyphExtents.h"
#include "gfxPlatform.h"
+#include "gfxTextRun.h"
#include "nsGkAtoms.h"
#include "gfxTypes.h"
#include "gfxContext.h"
#include "gfxFontMissingGlyphs.h"
#include "gfxGraphiteShaper.h"
#include "gfxHarfBuzzShaper.h"
#include "gfxUserFontSet.h"
-#include "gfxPlatformFontList.h"
-#include "gfxScriptItemizer.h"
#include "nsSpecialCasingData.h"
#include "nsTextRunTransformations.h"
#include "nsUnicodeProperties.h"
-#include "nsMathUtils.h"
-#include "nsBidiUtils.h"
-#include "nsUnicodeRange.h"
#include "nsStyleConsts.h"
#include "mozilla/AppUnits.h"
-#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#include "gfxSVGGlyphs.h"
-#include "gfxMathTable.h"
#include "gfx2DGlue.h"
#include "GreekCasing.h"
-#if defined(XP_MACOSX)
-#include "nsCocoaFeatures.h"
-#endif
-
#include "cairo.h"
-#include "gfxFontTest.h"
#include "harfbuzz/hb.h"
#include "harfbuzz/hb-ot.h"
#include "graphite2/Font.h"
-#include "nsCRT.h"
-#include "GeckoProfiler.h"
-#include "gfxFontConstants.h"
-
#include <algorithm>
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::unicode;
using mozilla::services::GetObserverService;
gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
-static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
-static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
-
#ifdef DEBUG_roc
#define DEBUG_TEXT_RUN_STORAGE_METRICS
#endif
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
-static uint32_t gTextRunStorageHighWaterMark = 0;
-static uint32_t gTextRunStorage = 0;
-static uint32_t gFontCount = 0;
-static uint32_t gGlyphExtentsCount = 0;
-static uint32_t gGlyphExtentsWidthsTotalSize = 0;
-static uint32_t gGlyphExtentsSetupEagerSimple = 0;
-static uint32_t gGlyphExtentsSetupEagerTight = 0;
-static uint32_t gGlyphExtentsSetupLazyTight = 0;
-static uint32_t gGlyphExtentsSetupFallBackToTight = 0;
+uint32_t gTextRunStorageHighWaterMark = 0;
+uint32_t gTextRunStorage = 0;
+uint32_t gFontCount = 0;
+uint32_t gGlyphExtentsCount = 0;
+uint32_t gGlyphExtentsWidthsTotalSize = 0;
+uint32_t gGlyphExtentsSetupEagerSimple = 0;
+uint32_t gGlyphExtentsSetupEagerTight = 0;
+uint32_t gGlyphExtentsSetupLazyTight = 0;
+uint32_t gGlyphExtentsSetupFallBackToTight = 0;
#endif
#ifdef PR_LOGGING
#define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
PR_LOG_DEBUG, args)
#define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
gfxPlatform::GetLog(eGfxLog_fontinit), \
PR_LOG_DEBUG)
#endif // PR_LOGGING
-void
-gfxCharacterMap::NotifyReleased()
-{
- gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
- if (mShared) {
- fontlist->RemoveCmap(this);
- }
- delete this;
-}
-
-gfxFontEntry::gfxFontEntry() :
- mItalic(false), mFixedPitch(false),
- mIsValid(true),
- mIsBadUnderlineFont(false),
- mIsUserFontContainer(false),
- mIsDataUserFont(false),
- mIsLocalUserFont(false),
- mStandardFace(false),
- mSymbolFont(false),
- mIgnoreGDEF(false),
- mIgnoreGSUB(false),
- mSVGInitialized(false),
- mMathInitialized(false),
- mHasSpaceFeaturesInitialized(false),
- mHasSpaceFeatures(false),
- mHasSpaceFeaturesKerning(false),
- mHasSpaceFeaturesNonKerning(false),
- mSkipDefaultFeatureSpaceCheck(false),
- mCheckedForGraphiteTables(false),
- mHasCmapTable(false),
- mGrFaceInitialized(false),
- mCheckedForColorGlyph(false),
- mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
- mUVSOffset(0), mUVSData(nullptr),
- mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
- mCOLR(nullptr),
- mCPAL(nullptr),
- mUnitsPerEm(0),
- mHBFace(nullptr),
- mGrFace(nullptr),
- mGrFaceRefCnt(0)
-{
- memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
- memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
-}
-
-gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
- mName(aName), mItalic(false), mFixedPitch(false),
- mIsValid(true),
- mIsBadUnderlineFont(false),
- mIsUserFontContainer(false),
- mIsDataUserFont(false),
- mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
- mSymbolFont(false),
- mIgnoreGDEF(false),
- mIgnoreGSUB(false),
- mSVGInitialized(false),
- mMathInitialized(false),
- mHasSpaceFeaturesInitialized(false),
- mHasSpaceFeatures(false),
- mHasSpaceFeaturesKerning(false),
- mHasSpaceFeaturesNonKerning(false),
- mSkipDefaultFeatureSpaceCheck(false),
- mCheckedForGraphiteTables(false),
- mHasCmapTable(false),
- mGrFaceInitialized(false),
- mCheckedForColorGlyph(false),
- mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
- mUVSOffset(0), mUVSData(nullptr),
- mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
- mCOLR(nullptr),
- mCPAL(nullptr),
- mUnitsPerEm(0),
- mHBFace(nullptr),
- mGrFace(nullptr),
- mGrFaceRefCnt(0)
-{
- memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
- memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
-}
-
-static PLDHashOperator
-DestroyHBSet(const uint32_t& aTag, hb_set_t*& aSet, void *aUserArg)
-{
- hb_set_destroy(aSet);
- return PL_DHASH_NEXT;
-}
-
-gfxFontEntry::~gfxFontEntry()
-{
- if (mCOLR) {
- hb_blob_destroy(mCOLR);
- }
-
- if (mCPAL) {
- hb_blob_destroy(mCPAL);
- }
-
- // For downloaded fonts, we need to tell the user font cache that this
- // entry is being deleted.
- if (mIsDataUserFont) {
- gfxUserFontSet::UserFontCache::ForgetFont(this);
- }
-
- if (mFeatureInputs) {
- mFeatureInputs->Enumerate(DestroyHBSet, nullptr);
- }
-
- // By the time the entry is destroyed, all font instances that were
- // using it should already have been deleted, and so the HB and/or Gr
- // face objects should have been released.
- MOZ_ASSERT(!mHBFace);
- MOZ_ASSERT(!mGrFaceInitialized);
-}
-
-bool gfxFontEntry::IsSymbolFont()
-{
- return mSymbolFont;
-}
-
-bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
-{
- if (!mCharacterMap) {
- ReadCMAP();
- NS_ASSERTION(mCharacterMap, "failed to initialize character map");
- }
- return mCharacterMap->test(aCh);
-}
-
-nsresult gfxFontEntry::InitializeUVSMap()
-{
- // mUVSOffset will not be initialized
- // until cmap is initialized.
- if (!mCharacterMap) {
- ReadCMAP();
- NS_ASSERTION(mCharacterMap, "failed to initialize character map");
- }
-
- if (!mUVSOffset) {
- return NS_ERROR_FAILURE;
- }
-
- if (!mUVSData) {
- const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
- AutoTable cmapTable(this, kCmapTag);
- if (!cmapTable) {
- mUVSOffset = 0; // don't bother to read the table again
- return NS_ERROR_FAILURE;
- }
-
- uint8_t* uvsData;
- unsigned int cmapLen;
- const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
- nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
- (const uint8_t*)cmapData + mUVSOffset,
- cmapLen - mUVSOffset, uvsData);
-
- if (NS_FAILED(rv)) {
- mUVSOffset = 0; // don't bother to read the table again
- return rv;
- }
-
- mUVSData = uvsData;
- }
-
- return NS_OK;
-}
-
-uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
-{
- InitializeUVSMap();
-
- if (mUVSData) {
- return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
- }
-
- return 0;
-}
-
-bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
-{
- hb_face_t *face = GetHBFace();
- if (!face) {
- return false;
- }
-
- unsigned int index;
- hb_tag_t chosenScript;
- bool found =
- hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
- aScriptTags, &index, &chosenScript);
- hb_face_destroy(face);
-
- return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
-}
-
-nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
-{
- NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
- mCharacterMap = new gfxCharacterMap();
- return NS_OK;
-}
-
-nsString
-gfxFontEntry::RealFaceName()
-{
- AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
- if (nameTable) {
- nsAutoString name;
- nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
- if (NS_SUCCEEDED(rv)) {
- return name;
- }
- }
- return Name();
-}
-
-already_AddRefed<gfxFont>
-gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
-{
- // the font entry name is the psname, not the family name
- nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
-
- if (!font) {
- gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
- if (!newFont)
- return nullptr;
- if (!newFont->Valid()) {
- delete newFont;
- return nullptr;
- }
- font = newFont;
- gfxFontCache::GetCache()->AddNew(font);
- }
- return font.forget();
-}
-
-uint16_t
-gfxFontEntry::UnitsPerEm()
-{
- if (!mUnitsPerEm) {
- AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
- if (headTable) {
- uint32_t len;
- const HeadTable* head =
- reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
- &len));
- if (len >= sizeof(HeadTable)) {
- mUnitsPerEm = head->unitsPerEm;
- }
- }
-
- // if we didn't find a usable 'head' table, or if the value was
- // outside the valid range, record it as invalid
- if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
- mUnitsPerEm = kInvalidUPEM;
- }
- }
- return mUnitsPerEm;
-}
-
-bool
-gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
-{
- NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
- return mSVGGlyphs->HasSVGGlyph(aGlyphId);
-}
-
-bool
-gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
- gfxRect *aResult)
-{
- NS_ABORT_IF_FALSE(mSVGInitialized,
- "SVG data has not yet been loaded. TryGetSVGData() first.");
- NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
- "font has invalid unitsPerEm");
-
- gfxContextAutoSaveRestore matrixRestore(aContext);
- cairo_matrix_t fontMatrix;
- cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
-
- gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
- svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
-
- return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
-}
-
-bool
-gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
- int aDrawMode, gfxTextContextPaint *aContextPaint)
-{
- NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
- return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
- aContextPaint);
-}
-
-bool
-gfxFontEntry::TryGetSVGData(gfxFont* aFont)
-{
- if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
- return false;
- }
-
- if (!mSVGInitialized) {
- mSVGInitialized = true;
-
- // If UnitsPerEm is not known/valid, we can't use SVG glyphs
- if (UnitsPerEm() == kInvalidUPEM) {
- return false;
- }
-
- // We don't use AutoTable here because we'll pass ownership of this
- // blob to the gfxSVGGlyphs, once we've confirmed the table exists
- hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
- if (!svgTable) {
- return false;
- }
-
- // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
- // with it.
- mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
- }
-
- if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
- mFontsUsingSVGGlyphs.AppendElement(aFont);
- }
-
- return !!mSVGGlyphs;
-}
-
-void
-gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
-{
- mFontsUsingSVGGlyphs.RemoveElement(aFont);
-}
-
-void
-gfxFontEntry::NotifyGlyphsChanged()
-{
- for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
- gfxFont* font = mFontsUsingSVGGlyphs[i];
- font->NotifyGlyphsChanged();
- }
-}
-
-bool
-gfxFontEntry::TryGetMathTable()
-{
- if (!mMathInitialized) {
- mMathInitialized = true;
-
- // If UnitsPerEm is not known/valid, we can't use MATH table
- if (UnitsPerEm() == kInvalidUPEM) {
- return false;
- }
-
- // We don't use AutoTable here because we'll pass ownership of this
- // blob to the gfxMathTable, once we've confirmed the table exists
- hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
- if (!mathTable) {
- return false;
- }
-
- // gfxMathTable will hb_blob_destroy() the table when it is finished
- // with it.
- mMathTable = new gfxMathTable(mathTable);
- if (!mMathTable->HasValidHeaders()) {
- mMathTable = nullptr;
- return false;
- }
- }
-
- return !!mMathTable;
-}
-
-gfxFloat
-gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
-{
- NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
- gfxFloat value = mMathTable->GetMathConstant(aConstant);
- if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
- aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
- aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
- return value / 100.0;
- }
- return value / mUnitsPerEm;
-}
-
-bool
-gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
- gfxFloat* aItalicCorrection)
-{
- NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
- int16_t italicCorrection;
- if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
- return false;
- }
- *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
- return true;
-}
-
-uint32_t
-gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
- uint16_t aSize)
-{
- NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
- return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
-}
-
-bool
-gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
- uint32_t aGlyphs[4])
-{
- NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
- return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
-}
-
-bool
-gfxFontEntry::TryGetColorGlyphs()
-{
- if (mCheckedForColorGlyph) {
- return (mCOLR && mCPAL);
- }
-
- mCheckedForColorGlyph = true;
-
- mCOLR = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
- if (!mCOLR) {
- return false;
- }
-
- mCPAL = GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L'));
- if (!mCPAL) {
- hb_blob_destroy(mCOLR);
- mCOLR = nullptr;
- return false;
- }
-
- // validation COLR and CPAL table
- if (gfxFontUtils::ValidateColorGlyphs(mCOLR, mCPAL)) {
- return true;
- }
-
- hb_blob_destroy(mCOLR);
- hb_blob_destroy(mCPAL);
- mCOLR = nullptr;
- mCPAL = nullptr;
- return false;
-}
-
-/**
- * FontTableBlobData
- *
- * See FontTableHashEntry for the general strategy.
- */
-
-class gfxFontEntry::FontTableBlobData {
-public:
- // Adopts the content of aBuffer.
- explicit FontTableBlobData(FallibleTArray<uint8_t>& aBuffer)
- : mHashtable(nullptr), mHashKey(0)
- {
- MOZ_COUNT_CTOR(FontTableBlobData);
- mTableData.SwapElements(aBuffer);
- }
-
- ~FontTableBlobData() {
- MOZ_COUNT_DTOR(FontTableBlobData);
- if (mHashtable && mHashKey) {
- mHashtable->RemoveEntry(mHashKey);
- }
- }
-
- // Useful for creating blobs
- const char *GetTable() const
- {
- return reinterpret_cast<const char*>(mTableData.Elements());
- }
- uint32_t GetTableLength() const { return mTableData.Length(); }
-
- // Tell this FontTableBlobData to remove the HashEntry when this is
- // destroyed.
- void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
- uint32_t aHashKey)
- {
- mHashtable = aHashtable;
- mHashKey = aHashKey;
- }
-
- // Disconnect from the HashEntry (because the blob has already been
- // removed from the hashtable).
- void ForgetHashEntry()
- {
- mHashtable = nullptr;
- mHashKey = 0;
- }
-
- size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
- return mTableData.SizeOfExcludingThis(aMallocSizeOf);
- }
- size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
- return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
- }
-
-private:
- // The font table data block, owned (via adoption)
- FallibleTArray<uint8_t> mTableData;
-
- // The blob destroy function needs to know the owning hashtable
- // and the hashtable key, so that it can remove the entry.
- nsTHashtable<FontTableHashEntry> *mHashtable;
- uint32_t mHashKey;
-
- // not implemented
- FontTableBlobData(const FontTableBlobData&);
-};
-
-hb_blob_t *
-gfxFontEntry::FontTableHashEntry::
-ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
- nsTHashtable<FontTableHashEntry> *aHashtable)
-{
- Clear();
- // adopts elements of aTable
- mSharedBlobData = new FontTableBlobData(aTable);
- mBlob = hb_blob_create(mSharedBlobData->GetTable(),
- mSharedBlobData->GetTableLength(),
- HB_MEMORY_MODE_READONLY,
- mSharedBlobData, DeleteFontTableBlobData);
- if (!mSharedBlobData) {
- // The FontTableBlobData was destroyed during hb_blob_create().
- // The (empty) blob is still be held in the hashtable with a strong
- // reference.
- return hb_blob_reference(mBlob);
- }
-
- // Tell the FontTableBlobData to remove this hash entry when destroyed.
- // The hashtable does not keep a strong reference.
- mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
- return mBlob;
-}
-
-void
-gfxFontEntry::FontTableHashEntry::Clear()
-{
- // If the FontTableBlobData is managing the hash entry, then the blob is
- // not owned by this HashEntry; otherwise there is strong reference to the
- // blob that must be removed.
- if (mSharedBlobData) {
- mSharedBlobData->ForgetHashEntry();
- mSharedBlobData = nullptr;
- } else if (mBlob) {
- hb_blob_destroy(mBlob);
- }
- mBlob = nullptr;
-}
-
-// a hb_destroy_func for hb_blob_create
-
-/* static */ void
-gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
-{
- delete static_cast<FontTableBlobData*>(aBlobData);
-}
-
-hb_blob_t *
-gfxFontEntry::FontTableHashEntry::GetBlob() const
-{
- return hb_blob_reference(mBlob);
-}
-
-bool
-gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
-{
- if (!mFontTableCache) {
- // we do this here rather than on fontEntry construction
- // because not all shapers will access the table cache at all
- mFontTableCache = new nsTHashtable<FontTableHashEntry>(8);
- }
-
- FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
- if (!entry) {
- return false;
- }
-
- *aBlob = entry->GetBlob();
- return true;
-}
-
-hb_blob_t *
-gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
- FallibleTArray<uint8_t>* aBuffer)
-{
- if (MOZ_UNLIKELY(!mFontTableCache)) {
- // we do this here rather than on fontEntry construction
- // because not all shapers will access the table cache at all
- mFontTableCache = new nsTHashtable<FontTableHashEntry>(8);
- }
-
- FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
- if (MOZ_UNLIKELY(!entry)) { // OOM
- return nullptr;
- }
-
- if (!aBuffer) {
- // ensure the entry is null
- entry->Clear();
- return nullptr;
- }
-
- return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache);
-}
-
-static int
-DirEntryCmp(const void* aKey, const void* aItem)
-{
- int32_t tag = *static_cast<const int32_t*>(aKey);
- const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
- return tag - int32_t(entry->tag);
-}
-
-hb_blob_t*
-gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
-{
- const SFNTHeader* header =
- reinterpret_cast<const SFNTHeader*>(aFontData);
- const TableDirEntry* dir =
- reinterpret_cast<const TableDirEntry*>(header + 1);
- dir = static_cast<const TableDirEntry*>
- (bsearch(&aTableTag, dir, uint16_t(header->numTables),
- sizeof(TableDirEntry), DirEntryCmp));
- if (dir) {
- return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
- dir->offset, dir->length,
- HB_MEMORY_MODE_READONLY, nullptr, nullptr);
-
- }
- return nullptr;
-}
-
-already_AddRefed<gfxCharacterMap>
-gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
- uint32_t& aUVSOffset,
- bool& aSymbolFont)
-{
- if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
- return nullptr;
- }
-
- return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
-}
-
-hb_blob_t *
-gfxFontEntry::GetFontTable(uint32_t aTag)
-{
- hb_blob_t *blob;
- if (GetExistingFontTable(aTag, &blob)) {
- return blob;
- }
-
- FallibleTArray<uint8_t> buffer;
- bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
-
- return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
-}
-
-// callback for HarfBuzz to get a font table (in hb_blob_t form)
-// from the font entry (passed as aUserData)
-/*static*/ hb_blob_t *
-gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
-{
- gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
-
- // bug 589682 - ignore the GDEF table in buggy fonts (applies to
- // Italic and BoldItalic faces of Times New Roman)
- if (aTag == TRUETYPE_TAG('G','D','E','F') &&
- fontEntry->IgnoreGDEF()) {
- return nullptr;
- }
-
- // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
- // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
- if (aTag == TRUETYPE_TAG('G','S','U','B') &&
- fontEntry->IgnoreGSUB()) {
- return nullptr;
- }
-
- return fontEntry->GetFontTable(aTag);
-}
-
-/*static*/ void
-gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
-{
- gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
- fe->ForgetHBFace();
-}
-
-void
-gfxFontEntry::ForgetHBFace()
-{
- mHBFace = nullptr;
-}
-
-hb_face_t*
-gfxFontEntry::GetHBFace()
-{
- if (!mHBFace) {
- mHBFace = hb_face_create_for_tables(HBGetTable, this,
- HBFaceDeletedCallback);
- return mHBFace;
- }
- return hb_face_reference(mHBFace);
-}
-
-/*static*/ const void*
-gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
- size_t *aLen)
-{
- gfxFontEntry *fontEntry =
- static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
- hb_blob_t *blob = fontEntry->GetFontTable(aName);
- if (blob) {
- unsigned int blobLength;
- const void *tableData = hb_blob_get_data(blob, &blobLength);
- fontEntry->mGrTableMap->Put(tableData, blob);
- *aLen = blobLength;
- return tableData;
- }
- *aLen = 0;
- return nullptr;
-}
-
-/*static*/ void
-gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
- const void *aTableBuffer)
-{
- gfxFontEntry *fontEntry =
- static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
- void *data;
- if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
- fontEntry->mGrTableMap->Remove(aTableBuffer);
- hb_blob_destroy(static_cast<hb_blob_t*>(data));
- }
-}
-
-gr_face*
-gfxFontEntry::GetGrFace()
-{
- if (!mGrFaceInitialized) {
- gr_face_ops faceOps = {
- sizeof(gr_face_ops),
- GrGetTable,
- GrReleaseTable
- };
- mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
- mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
- mGrFaceInitialized = true;
- }
- ++mGrFaceRefCnt;
- return mGrFace;
-}
-
-void
-gfxFontEntry::ReleaseGrFace(gr_face *aFace)
-{
- MOZ_ASSERT(aFace == mGrFace); // sanity-check
- MOZ_ASSERT(mGrFaceRefCnt > 0);
- if (--mGrFaceRefCnt == 0) {
- gr_face_destroy(mGrFace);
- mGrFace = nullptr;
- mGrFaceInitialized = false;
- delete mGrTableMap;
- mGrTableMap = nullptr;
- }
-}
-
-void
-gfxFontEntry::DisconnectSVG()
-{
- if (mSVGInitialized && mSVGGlyphs) {
- mSVGGlyphs = nullptr;
- mSVGInitialized = false;
- }
-}
-
-bool
-gfxFontEntry::HasFontTable(uint32_t aTableTag)
-{
- AutoTable table(this, aTableTag);
- return table && hb_blob_get_length(table) > 0;
-}
-
-void
-gfxFontEntry::CheckForGraphiteTables()
-{
- mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
-}
-
-
-#define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
-
-// check for too many script codes
-PR_STATIC_ASSERT(MOZ_NUM_SCRIPT_CODES <= FEATURE_SCRIPT_MASK);
-
-// high-order three bytes of tag with script in low-order byte
-#define SCRIPT_FEATURE(s,tag) (((~FEATURE_SCRIPT_MASK) & (tag)) | \
- ((FEATURE_SCRIPT_MASK) & (s)))
-
-bool
-gfxFontEntry::SupportsOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
-{
- if (!mSupportedFeatures) {
- mSupportedFeatures = new nsDataHashtable<nsUint32HashKey,bool>();
- }
-
- // note: high-order three bytes *must* be unique for each feature
- // listed below (see SCRIPT_FEATURE macro def'n)
- NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
- aFeatureTag == HB_TAG('c','2','s','c') ||
- aFeatureTag == HB_TAG('p','c','a','p') ||
- aFeatureTag == HB_TAG('c','2','p','c') ||
- aFeatureTag == HB_TAG('s','u','p','s') ||
- aFeatureTag == HB_TAG('s','u','b','s'),
- "use of unknown feature tag");
-
- // note: graphite feature support uses the last script index
- NS_ASSERTION(aScript < FEATURE_SCRIPT_MASK - 1,
- "need to bump the size of the feature shift");
-
- uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
- bool result;
- if (mSupportedFeatures->Get(scriptFeature, &result)) {
- return result;
- }
-
- result = false;
-
- hb_face_t *face = GetHBFace();
-
- if (hb_ot_layout_has_substitution(face)) {
- hb_script_t hbScript =
- gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
-
- // Get the OpenType tag(s) that match this script code
- hb_tag_t scriptTags[4] = {
- HB_TAG_NONE,
- HB_TAG_NONE,
- HB_TAG_NONE,
- HB_TAG_NONE
- };
- hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
-
- // Replace the first remaining NONE with DEFAULT
- hb_tag_t* scriptTag = &scriptTags[0];
- while (*scriptTag != HB_TAG_NONE) {
- ++scriptTag;
- }
- *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
-
- // Now check for 'smcp' under the first of those scripts that is present
- const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
- scriptTag = &scriptTags[0];
- while (*scriptTag != HB_TAG_NONE) {
- unsigned int scriptIndex;
- if (hb_ot_layout_table_find_script(face, kGSUB, *scriptTag,
- &scriptIndex)) {
- if (hb_ot_layout_language_find_feature(face, kGSUB,
- scriptIndex,
- HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
- aFeatureTag, nullptr)) {
- result = true;
- }
- break;
- }
- ++scriptTag;
- }
- }
-
- hb_face_destroy(face);
-
- mSupportedFeatures->Put(scriptFeature, result);
-
- return result;
-}
-
-const hb_set_t*
-gfxFontEntry::InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
-{
- if (!mFeatureInputs) {
- mFeatureInputs = new nsDataHashtable<nsUint32HashKey,hb_set_t*>();
- }
-
- NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
- aFeatureTag == HB_TAG('s','u','b','s'),
- "use of unknown feature tag");
-
- uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
- hb_set_t *inputGlyphs;
- if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
- return inputGlyphs;
- }
-
- inputGlyphs = hb_set_create();
-
- hb_face_t *face = GetHBFace();
-
- if (hb_ot_layout_has_substitution(face)) {
- hb_script_t hbScript =
- gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
-
- // Get the OpenType tag(s) that match this script code
- hb_tag_t scriptTags[4] = {
- HB_TAG_NONE,
- HB_TAG_NONE,
- HB_TAG_NONE,
- HB_TAG_NONE
- };
- hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
-
- // Replace the first remaining NONE with DEFAULT
- hb_tag_t* scriptTag = &scriptTags[0];
- while (*scriptTag != HB_TAG_NONE) {
- ++scriptTag;
- }
- *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
-
- const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
- hb_tag_t features[2] = { aFeatureTag, HB_TAG_NONE };
- hb_set_t *featurelookups = hb_set_create();
- hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr,
- features, featurelookups);
- hb_codepoint_t index = -1;
- while (hb_set_next(featurelookups, &index)) {
- hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index,
- nullptr, inputGlyphs,
- nullptr, nullptr);
- }
- }
-
- hb_face_destroy(face);
-
- mFeatureInputs->Put(scriptFeature, inputGlyphs);
- return inputGlyphs;
-}
-
-bool
-gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag)
-{
- if (!mSupportedFeatures) {
- mSupportedFeatures = new nsDataHashtable<nsUint32HashKey,bool>();
- }
-
- // note: high-order three bytes *must* be unique for each feature
- // listed below (see SCRIPT_FEATURE macro def'n)
- NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
- aFeatureTag == HB_TAG('c','2','s','c') ||
- aFeatureTag == HB_TAG('p','c','a','p') ||
- aFeatureTag == HB_TAG('c','2','p','c') ||
- aFeatureTag == HB_TAG('s','u','p','s') ||
- aFeatureTag == HB_TAG('s','u','b','s'),
- "use of unknown feature tag");
-
- // graphite feature check uses the last script slot
- uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
- bool result;
- if (mSupportedFeatures->Get(scriptFeature, &result)) {
- return result;
- }
-
- gr_face* face = GetGrFace();
- result = gr_face_find_fref(face, aFeatureTag) != nullptr;
- ReleaseGrFace(face);
-
- mSupportedFeatures->Put(scriptFeature, result);
-
- return result;
-}
-
-bool
-gfxFontEntry::GetColorLayersInfo(uint32_t aGlyphId,
- nsTArray<uint16_t>& aLayerGlyphs,
- nsTArray<mozilla::gfx::Color>& aLayerColors)
-{
- return gfxFontUtils::GetColorGlyphLayers(mCOLR,
- mCPAL,
- aGlyphId,
- aLayerGlyphs,
- aLayerColors);
-}
-
-size_t
-gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
-{
- size_t n = 0;
- if (mBlob) {
- n += aMallocSizeOf(mBlob);
- }
- if (mSharedBlobData) {
- n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
- }
- return n;
-}
-
-void
-gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
-{
- aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-
- // cmaps are shared so only non-shared cmaps are included here
- if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
- aSizes->mCharMapsSize +=
- mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
- }
- if (mFontTableCache) {
- aSizes->mFontTableCacheSize +=
- mFontTableCache->SizeOfIncludingThis(aMallocSizeOf);
- }
-}
-
-void
-gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
-{
- aSizes->mFontListSize += aMallocSizeOf(this);
- AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// class gfxFontFamily
-//
-//////////////////////////////////////////////////////////////////////////////
-
-// we consider faces with mStandardFace == true to be "greater than" those with false,
-// because during style matching, later entries will replace earlier ones
-class FontEntryStandardFaceComparator {
- public:
- bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
- return a->mStandardFace == b->mStandardFace;
- }
- bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
- return (a->mStandardFace == false && b->mStandardFace == true);
- }
-};
-
-void
-gfxFontFamily::SortAvailableFonts()
-{
- mAvailableFonts.Sort(FontEntryStandardFaceComparator());
-}
-
-bool
-gfxFontFamily::HasOtherFamilyNames()
-{
- // need to read in other family names to determine this
- if (!mOtherFamilyNamesInitialized) {
- ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
- }
- return mHasOtherFamilyNames;
-}
-
-gfxFontEntry*
-gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
- bool& aNeedsSyntheticBold)
-{
- if (!mHasStyles)
- FindStyleVariations(); // collect faces for the family, if not already done
-
- NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
-
- aNeedsSyntheticBold = false;
-
- int8_t baseWeight = aFontStyle.ComputeWeight();
- bool wantBold = baseWeight >= 6;
-
- // If the family has only one face, we simply return it; no further checking needed
- if (mAvailableFonts.Length() == 1) {
- gfxFontEntry *fe = mAvailableFonts[0];
- aNeedsSyntheticBold =
- wantBold && !fe->IsBold() && aFontStyle.allowSyntheticWeight;
- return fe;
- }
-
- bool wantItalic = (aFontStyle.style &
- (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
-
- // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
- // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
- // stored in the above order; note that some of the entries may be nullptr.
- // We can then pick the required entry based on whether the request is for
- // bold or non-bold, italic or non-italic, without running the more complex
- // matching algorithm used for larger families with many weights and/or widths.
-
- if (mIsSimpleFamily) {
- // Family has no more than the "standard" 4 faces, at fixed indexes;
- // calculate which one we want.
- // Note that we cannot simply return it as not all 4 faces are necessarily present.
- uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
- (wantBold ? kBoldMask : 0);
-
- // if the desired style is available, return it directly
- gfxFontEntry *fe = mAvailableFonts[faceIndex];
- if (fe) {
- // no need to set aNeedsSyntheticBold here as we matched the boldness request
- return fe;
- }
-
- // order to check fallback faces in a simple family, depending on requested style
- static const uint8_t simpleFallbacks[4][3] = {
- { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular
- { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
- { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic
- { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic
- };
- const uint8_t *order = simpleFallbacks[faceIndex];
-
- for (uint8_t trial = 0; trial < 3; ++trial) {
- // check remaining faces in order of preference to find the first that actually exists
- fe = mAvailableFonts[order[trial]];
- if (fe) {
- aNeedsSyntheticBold =
- wantBold && !fe->IsBold() &&
- aFontStyle.allowSyntheticWeight;
- return fe;
- }
- }
-
- // this can't happen unless we have totally broken the font-list manager!
- NS_NOTREACHED("no face found in simple font family!");
- return nullptr;
- }
-
- // This is a large/rich font family, so we do full style- and weight-matching:
- // first collect a list of weights that are the best match for the requested
- // font-stretch and font-style, then pick the best weight match among those
- // available.
-
- gfxFontEntry *weightList[10] = { 0 };
- bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
- if (!foundWeights) {
- return nullptr;
- }
-
- // First find a match for the best weight
- int8_t matchBaseWeight = 0;
- int8_t i = baseWeight;
-
- // Need to special case when normal face doesn't exist but medium does.
- // In that case, use medium otherwise weights < 400
- if (baseWeight == 4 && !weightList[4]) {
- i = 5; // medium
- }
-
- // Loop through weights, since one exists loop will terminate
- int8_t direction = (baseWeight > 5) ? 1 : -1;
- for (; ; i += direction) {
- if (weightList[i]) {
- matchBaseWeight = i;
- break;
- }
-
- // If we've reached one side without finding a font,
- // start over and go the other direction until we find a match
- if (i == 1 || i == 9) {
- i = baseWeight;
- direction = -direction;
- }
- }
-
- NS_ASSERTION(matchBaseWeight != 0,
- "weight mapping should always find at least one font in a family");
-
- gfxFontEntry *matchFE = weightList[matchBaseWeight];
-
- NS_ASSERTION(matchFE,
- "weight mapping should always find at least one font in a family");
-
- if (!matchFE->IsBold() && baseWeight >= 6 &&
- aFontStyle.allowSyntheticWeight)
- {
- aNeedsSyntheticBold = true;
- }
-
- return matchFE;
-}
-
-void
-gfxFontFamily::CheckForSimpleFamily()
-{
- // already checked this family
- if (mIsSimpleFamily) {
- return;
- };
-
- uint32_t count = mAvailableFonts.Length();
- if (count > 4 || count == 0) {
- return; // can't be "simple" if there are >4 faces;
- // if none then the family is unusable anyway
- }
-
- if (count == 1) {
- mIsSimpleFamily = true;
- return;
- }
-
- int16_t firstStretch = mAvailableFonts[0]->Stretch();
-
- gfxFontEntry *faces[4] = { 0 };
- for (uint8_t i = 0; i < count; ++i) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (fe->Stretch() != firstStretch) {
- return; // font-stretch doesn't match, don't treat as simple family
- }
- uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
- (fe->Weight() >= 600 ? kBoldMask : 0);
- if (faces[faceIndex]) {
- return; // two faces resolve to the same slot; family isn't "simple"
- }
- faces[faceIndex] = fe;
- }
-
- // we have successfully slotted the available faces into the standard
- // 4-face framework
- mAvailableFonts.SetLength(4);
- for (uint8_t i = 0; i < 4; ++i) {
- if (mAvailableFonts[i].get() != faces[i]) {
- mAvailableFonts[i].swap(faces[i]);
- }
- }
-
- mIsSimpleFamily = true;
-}
-
-#ifdef DEBUG
-bool
-gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) {
- uint32_t i, numFonts = mAvailableFonts.Length();
- for (i = 0; i < numFonts; i++) {
- if (mAvailableFonts[i] == aFontEntry) {
- return true;
- }
- // userfonts contain the actual real font entry
- if (mAvailableFonts[i]->mIsUserFontContainer) {
- gfxUserFontEntry* ufe =
- static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get());
- if (ufe->GetPlatformFontEntry() == aFontEntry) {
- return true;
- }
- }
- }
- return false;
-}
-#endif
-
-static inline uint32_t
-StyleDistance(gfxFontEntry *aFontEntry,
- bool anItalic, int16_t aStretch)
-{
- // Compute a measure of the "distance" between the requested style
- // and the given fontEntry,
- // considering italicness and font-stretch but not weight.
-
- int32_t distance = 0;
- if (aStretch != aFontEntry->mStretch) {
- // stretch values are in the range -4 .. +4
- // if aStretch is positive, we prefer more-positive values;
- // if zero or negative, prefer more-negative
- if (aStretch > 0) {
- distance = (aFontEntry->mStretch - aStretch) * 2;
- } else {
- distance = (aStretch - aFontEntry->mStretch) * 2;
- }
- // if the computed "distance" here is negative, it means that
- // aFontEntry lies in the "non-preferred" direction from aStretch,
- // so we treat that as larger than any preferred-direction distance
- // (max possible is 8) by adding an extra 10 to the absolute value
- if (distance < 0) {
- distance = -distance + 10;
- }
- }
- if (aFontEntry->IsItalic() != anItalic) {
- distance += 1;
- }
- return uint32_t(distance);
-}
-
-bool
-gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
- bool anItalic, int16_t aStretch)
-{
- uint32_t foundWeights = 0;
- uint32_t bestMatchDistance = 0xffffffff;
-
- uint32_t count = mAvailableFonts.Length();
- for (uint32_t i = 0; i < count; i++) {
- // this is not called for "simple" families, and therefore it does not
- // need to check the mAvailableFonts entries for nullptr.
- gfxFontEntry *fe = mAvailableFonts[i];
- uint32_t distance = StyleDistance(fe, anItalic, aStretch);
- if (distance <= bestMatchDistance) {
- int8_t wt = fe->mWeight / 100;
- NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
- if (!aFontsForWeights[wt]) {
- // record this as a possible candidate for weight matching
- aFontsForWeights[wt] = fe;
- ++foundWeights;
- } else {
- uint32_t prevDistance =
- StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
- if (prevDistance >= distance) {
- // replacing a weight we already found,
- // so don't increment foundWeights
- aFontsForWeights[wt] = fe;
- }
- }
- bestMatchDistance = distance;
- }
- }
-
- NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
-
- if (foundWeights == 1) {
- // no need to cull entries if we only found one weight
- return true;
- }
-
- // we might have recorded some faces that were a partial style match, but later found
- // others that were closer; in this case, we need to cull the poorer matches from the
- // weight list we'll return
- for (uint32_t i = 0; i < 10; ++i) {
- if (aFontsForWeights[i] &&
- StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
- {
- aFontsForWeights[i] = 0;
- }
- }
-
- return (foundWeights > 0);
-}
-
-
-void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
-{
- // just return the primary name; subclasses should override
- aLocalizedName = mName;
-}
-
-// metric for how close a given font matches a style
-static int32_t
-CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
-{
- int32_t rank = 0;
- if (aStyle) {
- // italics
- bool wantItalic =
- (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
- if (aFontEntry->IsItalic() == wantItalic) {
- rank += 10;
- }
-
- // measure of closeness of weight to the desired value
- rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
- } else {
- // if no font to match, prefer non-bold, non-italic fonts
- if (!aFontEntry->IsItalic()) {
- rank += 3;
- }
- if (!aFontEntry->IsBold()) {
- rank += 2;
- }
- }
-
- return rank;
-}
-
-#define RANK_MATCHED_CMAP 20
-
-void
-gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
-{
- if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
- // none of the faces in the family support the required char,
- // so bail out immediately
- return;
- }
-
- bool needsBold;
- gfxFontStyle normal;
- gfxFontEntry *fe = FindFontForStyle(
- (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal,
- needsBold);
-
- if (fe && !fe->SkipDuringSystemFallback()) {
- int32_t rank = 0;
-
- if (fe->TestCharacterMap(aMatchData->mCh)) {
- rank += RANK_MATCHED_CMAP;
- aMatchData->mCount++;
-#ifdef PR_LOGGING
- PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
-
- if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) {
- uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
- uint32_t script = GetScriptCode(aMatchData->mCh);
- PR_LOG(log, PR_LOG_DEBUG,\
- ("(textrun-systemfallback-fonts) char: u+%6.6x "
- "unicode-range: %d script: %d match: [%s]\n",
- aMatchData->mCh,
- unicodeRange, script,
- NS_ConvertUTF16toUTF8(fe->Name()).get()));
- }
-#endif
- }
-
- aMatchData->mCmapsTested++;
- if (rank == 0) {
- return;
- }
-
- // omitting from original windows code -- family name, lang group, pitch
- // not available in current FontEntry implementation
- rank += CalcStyleMatch(fe, aMatchData->mStyle);
-
- // xxx - add whether AAT font with morphing info for specific lang groups
-
- if (rank > aMatchData->mMatchRank
- || (rank == aMatchData->mMatchRank &&
- Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
- {
- aMatchData->mBestMatch = fe;
- aMatchData->mMatchedFamily = this;
- aMatchData->mMatchRank = rank;
- }
- }
-}
-
-void
-gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
-{
- uint32_t i, numFonts = mAvailableFonts.Length();
- for (i = 0; i < numFonts; i++) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
- int32_t rank = RANK_MATCHED_CMAP;
- rank += CalcStyleMatch(fe, aMatchData->mStyle);
- if (rank > aMatchData->mMatchRank
- || (rank == aMatchData->mMatchRank &&
- Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
- {
- aMatchData->mBestMatch = fe;
- aMatchData->mMatchedFamily = this;
- aMatchData->mMatchRank = rank;
- }
- }
- }
-}
-
-/*static*/ void
-gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
- const char *aNameData,
- uint32_t aDataLength,
- nsTArray<nsString>& aOtherFamilyNames,
- bool useFullName)
-{
- const gfxFontUtils::NameHeader *nameHeader =
- reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
-
- uint32_t nameCount = nameHeader->count;
- if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
- NS_WARNING("invalid font (name records)");
- return;
- }
-
- const gfxFontUtils::NameRecord *nameRecord =
- reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
- uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
-
- for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
- uint32_t nameLen = nameRecord->length;
- uint32_t nameOff = nameRecord->offset; // offset from base of string storage
-
- if (stringsBase + nameOff + nameLen > aDataLength) {
- NS_WARNING("invalid font (name table strings)");
- return;
- }
-
- uint16_t nameID = nameRecord->nameID;
- if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
- (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
- nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
- nsAutoString otherFamilyName;
- bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
- nameLen,
- uint32_t(nameRecord->platformID),
- uint32_t(nameRecord->encodingID),
- uint32_t(nameRecord->languageID),
- otherFamilyName);
- // add if not same as canonical family name
- if (ok && otherFamilyName != aFamilyName) {
- aOtherFamilyNames.AppendElement(otherFamilyName);
- }
- }
- }
-}
-
-// returns true if other names were found, false otherwise
-bool
-gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
- hb_blob_t *aNameTable,
- bool useFullName)
-{
- uint32_t dataLength;
- const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
- nsAutoTArray<nsString,4> otherFamilyNames;
-
- ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
- otherFamilyNames, useFullName);
-
- uint32_t n = otherFamilyNames.Length();
- for (uint32_t i = 0; i < n; i++) {
- aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
- }
-
- return n != 0;
-}
-
-void
-gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
-{
- if (mOtherFamilyNamesInitialized)
- return;
- mOtherFamilyNamesInitialized = true;
-
- FindStyleVariations();
-
- // read in other family names for the first face in the list
- uint32_t i, numFonts = mAvailableFonts.Length();
- const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
-
- for (i = 0; i < numFonts; ++i) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (!fe) {
- continue;
- }
- gfxFontEntry::AutoTable nameTable(fe, kNAME);
- if (!nameTable) {
- continue;
- }
- mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
- nameTable);
- break;
- }
-
- // read in other names for the first face in the list with the assumption
- // that if extra names don't exist in that face then they don't exist in
- // other faces for the same font
- if (!mHasOtherFamilyNames)
- return;
-
- // read in names for all faces, needed to catch cases where fonts have
- // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
- for ( ; i < numFonts; i++) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (!fe) {
- continue;
- }
- gfxFontEntry::AutoTable nameTable(fe, kNAME);
- if (!nameTable) {
- continue;
- }
- ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
- }
-}
-
-void
-gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
- bool aNeedFullnamePostscriptNames,
- FontInfoData *aFontInfoData)
-{
- // if all needed names have already been read, skip
- if (mOtherFamilyNamesInitialized &&
- (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
- return;
-
- bool asyncFontLoaderDisabled = false;
-
-#if defined(XP_MACOSX)
- // bug 975460 - async font loader crashes sometimes under 10.6, disable
- if (!nsCocoaFeatures::OnLionOrLater()) {
- asyncFontLoaderDisabled = true;
- }
-#endif
-
- if (!mOtherFamilyNamesInitialized &&
- aFontInfoData &&
- aFontInfoData->mLoadOtherNames &&
- !asyncFontLoaderDisabled)
- {
- nsAutoTArray<nsString,4> otherFamilyNames;
- bool foundOtherNames =
- aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
- if (foundOtherNames) {
- uint32_t i, n = otherFamilyNames.Length();
- for (i = 0; i < n; i++) {
- aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
- }
- }
- mOtherFamilyNamesInitialized = true;
- }
-
- // if all needed data has been initialized, return
- if (mOtherFamilyNamesInitialized &&
- (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
- return;
- }
-
- FindStyleVariations(aFontInfoData);
-
- // check again, as style enumeration code may have loaded names
- if (mOtherFamilyNamesInitialized &&
- (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
- return;
- }
-
- uint32_t i, numFonts = mAvailableFonts.Length();
- const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
-
- bool firstTime = true, readAllFaces = false;
- for (i = 0; i < numFonts; ++i) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (!fe) {
- continue;
- }
-
- nsAutoString fullname, psname;
- bool foundFaceNames = false;
- if (!mFaceNamesInitialized &&
- aNeedFullnamePostscriptNames &&
- aFontInfoData &&
- aFontInfoData->mLoadFaceNames) {
- aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
- if (!fullname.IsEmpty()) {
- aPlatformFontList->AddFullname(fe, fullname);
- }
- if (!psname.IsEmpty()) {
- aPlatformFontList->AddPostscriptName(fe, psname);
- }
- foundFaceNames = true;
-
- // found everything needed? skip to next font
- if (mOtherFamilyNamesInitialized) {
- continue;
- }
- }
-
- // load directly from the name table
- gfxFontEntry::AutoTable nameTable(fe, kNAME);
- if (!nameTable) {
- continue;
- }
-
- if (aNeedFullnamePostscriptNames && !foundFaceNames) {
- if (gfxFontUtils::ReadCanonicalName(
- nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
- {
- aPlatformFontList->AddFullname(fe, fullname);
- }
-
- if (gfxFontUtils::ReadCanonicalName(
- nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
- {
- aPlatformFontList->AddPostscriptName(fe, psname);
- }
- }
-
- if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
- bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
- nameTable);
-
- // if the first face has a different name, scan all faces, otherwise
- // assume the family doesn't have other names
- if (firstTime && foundOtherName) {
- mHasOtherFamilyNames = true;
- readAllFaces = true;
- }
- firstTime = false;
- }
-
- // if not reading in any more names, skip other faces
- if (!readAllFaces && !aNeedFullnamePostscriptNames) {
- break;
- }
- }
-
- mFaceNamesInitialized = true;
- mOtherFamilyNamesInitialized = true;
-}
-
-
-gfxFontEntry*
-gfxFontFamily::FindFont(const nsAString& aPostscriptName)
-{
- // find the font using a simple linear search
- uint32_t numFonts = mAvailableFonts.Length();
- for (uint32_t i = 0; i < numFonts; i++) {
- gfxFontEntry *fe = mAvailableFonts[i].get();
- if (fe && fe->Name() == aPostscriptName)
- return fe;
- }
- return nullptr;
-}
-
-void
-gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
-{
- FindStyleVariations(aFontInfoData);
-
- uint32_t i, numFonts = mAvailableFonts.Length();
- for (i = 0; i < numFonts; i++) {
- gfxFontEntry *fe = mAvailableFonts[i];
- // don't try to load cmaps for downloadable fonts not yet loaded
- if (!fe || fe->mIsUserFontContainer) {
- continue;
- }
- fe->ReadCMAP(aFontInfoData);
- mFamilyCharacterMap.Union(*(fe->mCharacterMap));
- }
- mFamilyCharacterMap.Compact();
- mFamilyCharacterMapInitialized = true;
-}
-
-void
-gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
-{
- aSizes->mFontListSize +=
- mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
- aSizes->mCharMapsSize +=
- mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
-
- aSizes->mFontListSize +=
- mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
- for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
- gfxFontEntry *fe = mAvailableFonts[i];
- if (fe) {
- fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
- }
- }
-}
-
-void
-gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const
-{
- aSizes->mFontListSize += aMallocSizeOf(this);
- AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
-}
/*
* gfxFontCache - global cache of gfxFont instances.
* Expires unused fonts after a short interval;
* notifies fonts to age their cached shaped-word records;
* observes memory-pressure notification and tells fonts to clear their
* shaped-word caches to free up memory.
*/
@@ -2316,16 +539,194 @@ gfxFontShaper::MergeFontFeatures(
const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
aMergedFeatures.Put(feature.mTag, feature.mValue);
}
return aMergedFeatures.Count() != 0;
}
void
+gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
+ const char16_t *aString,
+ uint32_t aLength)
+{
+ CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+
+ gfxTextRun::CompressedGlyph extendCluster;
+ extendCluster.SetComplex(false, true, 0);
+
+ ClusterIterator iter(aString, aLength);
+
+ // the ClusterIterator won't be able to tell us if the string
+ // _begins_ with a cluster-extender, so we handle that here
+ if (aLength && IsClusterExtender(*aString)) {
+ *glyphs = extendCluster;
+ }
+
+ while (!iter.AtEnd()) {
+ if (*iter == char16_t(' ')) {
+ glyphs->SetIsSpace();
+ }
+ // advance iter to the next cluster-start (or end of text)
+ iter.Next();
+ // step past the first char of the cluster
+ aString++;
+ glyphs++;
+ // mark all the rest as cluster-continuations
+ while (aString < iter) {
+ *glyphs = extendCluster;
+ if (NS_IS_LOW_SURROGATE(*aString)) {
+ glyphs->SetIsLowSurrogate();
+ }
+ glyphs++;
+ aString++;
+ }
+ }
+}
+
+void
+gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
+ const uint8_t *aString,
+ uint32_t aLength)
+{
+ CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
+ const uint8_t *limit = aString + aLength;
+
+ while (aString < limit) {
+ if (*aString == uint8_t(' ')) {
+ glyphs->SetIsSpace();
+ }
+ aString++;
+ glyphs++;
+ }
+}
+
+gfxShapedText::DetailedGlyph *
+gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
+{
+ NS_ASSERTION(aIndex < GetLength(), "Index out of range");
+
+ if (!mDetailedGlyphs) {
+ mDetailedGlyphs = new DetailedGlyphStore();
+ }
+
+ return mDetailedGlyphs->Allocate(aIndex, aCount);
+}
+
+void
+gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
+ const DetailedGlyph *aGlyphs)
+{
+ NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
+ NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
+ "First character can't be a ligature continuation!");
+
+ uint32_t glyphCount = aGlyph.GetGlyphCount();
+ if (glyphCount > 0) {
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
+ memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
+ }
+ GetCharacterGlyphs()[aIndex] = aGlyph;
+}
+
+#define ZWNJ 0x200C
+#define ZWJ 0x200D
+// U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
+// in a future Unicode update. Add it manually for now
+#define ALM 0x061C
+static inline bool
+IsDefaultIgnorable(uint32_t aChar)
+{
+ return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
+ aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
+}
+
+void
+gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
+{
+ uint8_t category = GetGeneralCategory(aChar);
+ if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
+ category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+ {
+ GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
+ }
+
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+
+ details->mGlyphID = aChar;
+ if (IsDefaultIgnorable(aChar)) {
+ // Setting advance width to zero will prevent drawing the hexbox
+ details->mAdvance = 0;
+ } else {
+ gfxFloat width =
+ std::max(aFont->GetMetrics().aveCharWidth,
+ gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
+ mAppUnitsPerDevUnit));
+ details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
+ }
+ details->mXOffset = 0;
+ details->mYOffset = 0;
+ GetCharacterGlyphs()[aIndex].SetMissing(1);
+}
+
+bool
+gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
+{
+ if (IsDefaultIgnorable(aCh)) {
+ DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+ details->mGlyphID = aCh;
+ details->mAdvance = 0;
+ details->mXOffset = 0;
+ details->mYOffset = 0;
+ GetCharacterGlyphs()[aIndex].SetMissing(1);
+ return true;
+ }
+ return false;
+}
+
+void
+gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+ uint32_t aOffset,
+ uint32_t aLength)
+{
+ uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
+ CompressedGlyph *charGlyphs = GetCharacterGlyphs();
+ for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
+ CompressedGlyph *glyphData = charGlyphs + i;
+ if (glyphData->IsSimpleGlyph()) {
+ // simple glyphs ==> just add the advance
+ int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
+ if (CompressedGlyph::IsSimpleAdvance(advance)) {
+ glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
+ } else {
+ // rare case, tested by making this the default
+ uint32_t glyphIndex = glyphData->GetSimpleGlyph();
+ glyphData->SetComplex(true, true, 1);
+ DetailedGlyph detail = {glyphIndex, advance, 0, 0};
+ SetGlyphs(i, *glyphData, &detail);
+ }
+ } else {
+ // complex glyphs ==> add offset at cluster/ligature boundaries
+ uint32_t detailedLength = glyphData->GetGlyphCount();
+ if (detailedLength) {
+ DetailedGlyph *details = GetDetailedGlyphs(i);
+ if (!details) {
+ continue;
+ }
+ if (IsRightToLeft()) {
+ details[0].mAdvance += synAppUnitOffset;
+ } else {
+ details[detailedLength - 1].mAdvance += synAppUnitOffset;
+ }
+ }
+ }
+ }
+}
+
+void
gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
{
mAscent = std::max(mAscent, aOther.mAscent);
mDescent = std::max(mDescent, aOther.mDescent);
if (aOtherIsOnLeft) {
mBoundingBox =
(mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
} else {
@@ -3071,46 +1472,16 @@ static AntialiasMode Get2DAAMode(gfxFont
return AntialiasMode::GRAY;
case gfxFont::kAntialiasNone:
return AntialiasMode::NONE;
default:
return AntialiasMode::DEFAULT;
}
}
-// Parameters passed to gfxFont methods for drawing glyphs from a textrun.
-// The TextRunDrawParams are set up once per textrun; the FontDrawParams
-// are dependent on the specific font, so they are set per GlyphRun.
-
-struct TextRunDrawParams {
- RefPtr<DrawTarget> dt;
- gfxContext *context;
- gfxFont::Spacing *spacing;
- gfxTextRunDrawCallbacks *callbacks;
- gfxTextContextPaint *runContextPaint;
- gfxFloat direction;
- double devPerApp;
- DrawMode drawMode;
- bool isRTL;
- bool paintSVGGlyphs;
-};
-
-struct FontDrawParams {
- RefPtr<ScaledFont> scaledFont;
- RefPtr<GlyphRenderingOptions> renderingOptions;
- gfxTextContextPaint *contextPaint;
- Matrix *passedInvMatrix;
- Matrix matInv;
- double synBoldOnePixelOffset;
- int32_t extraStrikes;
- DrawOptions drawOptions;
- bool haveSVGGlyphs;
- bool haveColorGlyphs;
-};
-
class GlyphBufferAzure
{
public:
GlyphBufferAzure(const TextRunDrawParams& aRunParams,
const FontDrawParams& aFontParams)
: mRunParams(aRunParams)
, mFontParams(aFontParams)
, mNumGlyphs(0)
@@ -3653,30 +2024,16 @@ UnionRange(gfxFloat aX, gfxFloat* aDestM
// on overflowing glyphs.
static bool
NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
{
return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
aFont->GetFontEntry()->IsUserFont();
}
-static bool
-NeedsGlyphExtents(gfxTextRun *aTextRun)
-{
- if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
- return true;
- uint32_t numRuns;
- const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
- for (uint32_t i = 0; i < numRuns; ++i) {
- if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
- return true;
- }
- return false;
-}
-
gfxFont::RunMetrics
gfxFont::Measure(gfxTextRun *aTextRun,
uint32_t aStart, uint32_t aEnd,
BoundingBoxType aBoundingBoxType,
gfxContext *aRefContext,
Spacing *aSpacing)
{
// If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
@@ -3832,22 +2189,16 @@ gfxFont::NotifyGlyphsChanged()
}
static bool
IsBoundarySpace(char16_t aChar, char16_t aNextChar)
{
return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
}
-static inline uint32_t
-HashMix(uint32_t aHash, char16_t aCh)
-{
- return (aHash >> 28) ^ (aHash << 4) ^ aCh;
-}
-
#ifdef __GNUC__
#define GFX_MAYBE_UNUSED __attribute__((unused))
#else
#define GFX_MAYBE_UNUSED
#endif
template<typename T>
gfxShapedWord*
@@ -3925,17 +2276,17 @@ gfxFont::GetShapedWord(gfxContext *aCont
bool
gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
{
const gfxShapedWord *sw = mShapedWord;
if (!sw) {
return false;
}
if (sw->GetLength() != aKey->mLength ||
- sw->Flags() != aKey->mFlags ||
+ sw->GetFlags() != aKey->mFlags ||
sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
sw->Script() != aKey->mScript) {
return false;
}
if (sw->TextIs8Bit()) {
if (aKey->mTextIs8Bit) {
return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
aKey->mLength * sizeof(uint8_t)));
@@ -4243,17 +2594,17 @@ gfxFont::SplitAndInitTextRun(gfxContext
// break into separate ShapedWords when we hit an invalid char,
// or a boundary space (always handled individually),
// or the first non-space after a space
if (!boundary && !invalid) {
if (!IsChar8Bit(ch)) {
wordIs8Bit = false;
}
// include this character in the hash, and move on to next
- hash = HashMix(hash, ch);
+ hash = gfxShapedWord::HashMix(hash, ch);
continue;
}
// We've decided to break here (i.e. we're at the end of a "word");
// shape the word and add it to the textrun.
// For words longer than the limit, we don't use the
// font's word cache but just shape directly into the textrun.
if (length > wordCacheCharLimit) {
@@ -4293,17 +2644,17 @@ gfxFont::SplitAndInitTextRun(gfxContext
// word was terminated by a space: add that to the textrun
if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
aRunStart + i, ch))
{
static const uint8_t space = ' ';
gfxShapedWord *sw =
GetShapedWord(aContext,
&space, 1,
- HashMix(0, ' '), aRunScript,
+ gfxShapedWord::HashMix(0, ' '), aRunScript,
appUnitsPerDevUnit,
flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
if (sw) {
aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
} else {
return false;
}
}
@@ -4335,16 +2686,230 @@ gfxFont::SplitAndInitTextRun(gfxContext
hash = 0;
wordStart = i + 1;
wordIs8Bit = true;
}
return true;
}
+// Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
+template bool
+gfxFont::SplitAndInitTextRun(gfxContext *aContext,
+ gfxTextRun *aTextRun,
+ const uint8_t *aString,
+ uint32_t aRunStart,
+ uint32_t aRunLength,
+ int32_t aRunScript);
+template bool
+gfxFont::SplitAndInitTextRun(gfxContext *aContext,
+ gfxTextRun *aTextRun,
+ const char16_t *aString,
+ uint32_t aRunStart,
+ uint32_t aRunLength,
+ int32_t aRunScript);
+
+bool
+gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
+ gfxTextRun *aTextRun,
+ const uint8_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ uint8_t aMatchType,
+ int32_t aScript,
+ bool aSyntheticLower,
+ bool aSyntheticUpper)
+{
+ NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
+ aLength);
+ return InitFakeSmallCapsRun(aContext, aTextRun, unicodeString.get(),
+ aOffset, aLength, aMatchType, aScript,
+ aSyntheticLower, aSyntheticUpper);
+}
+
+bool
+gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
+ gfxTextRun *aTextRun,
+ const char16_t *aText,
+ uint32_t aOffset,
+ uint32_t aLength,
+ uint8_t aMatchType,
+ int32_t aScript,
+ bool aSyntheticLower,
+ bool aSyntheticUpper)
+{
+ bool ok = true;
+
+ nsRefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
+
+ enum RunCaseAction {
+ kNoChange,
+ kUppercaseReduce,
+ kUppercase
+ };
+
+ RunCaseAction runAction = kNoChange;
+ uint32_t runStart = 0;
+
+ for (uint32_t i = 0; i <= aLength; ++i) {
+ uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
+ // a trailing surrogate as well as the
+ // current code unit.
+ RunCaseAction chAction = kNoChange;
+ // Unless we're at the end, figure out what treatment the current
+ // character will need.
+ if (i < aLength) {
+ uint32_t ch = aText[i];
+ if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
+ NS_IS_LOW_SURROGATE(aText[i + 1])) {
+ ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
+ extraCodeUnits = 1;
+ }
+ // Characters that aren't the start of a cluster are ignored here.
+ // They get added to whatever lowercase/non-lowercase run we're in.
+ if (IsClusterExtender(ch)) {
+ chAction = runAction;
+ } else {
+ if (ch != ToUpperCase(ch) || mozilla::unicode::SpecialUpper(ch)) {
+ // ch is lower case
+ chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
+ } else if (ch != ToLowerCase(ch)) {
+ // ch is upper case
+ chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
+ if (mStyle.language == nsGkAtoms::el) {
+ // In Greek, check for characters that will be modified by
+ // the GreekUpperCase mapping - this catches accented
+ // capitals where the accent is to be removed (bug 307039).
+ // These are handled by using the full-size font with the
+ // uppercasing transform.
+ mozilla::GreekCasing::State state;
+ uint32_t ch2 = mozilla::GreekCasing::UpperCase(ch, state);
+ if (ch != ch2 && !aSyntheticUpper) {
+ chAction = kUppercase;
+ }
+ }
+ }
+ }
+ }
+
+ // At the end of the text or when the current character needs different
+ // casing treatment from the current run, finish the run-in-progress
+ // and prepare to accumulate a new run.
+ // Note that we do not look at any source data for offset [i] here,
+ // as that would be invalid in the case where i==length.
+ if ((i == aLength || runAction != chAction) && runStart < i) {
+ uint32_t runLength = i - runStart;
+ gfxFont* f = this;
+ switch (runAction) {
+ case kNoChange:
+ // just use the current font and the existing string
+ aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true);
+ if (!f->SplitAndInitTextRun(aContext, aTextRun,
+ aText + runStart,
+ aOffset + runStart, runLength,
+ aScript)) {
+ ok = false;
+ }
+ break;
+
+ case kUppercaseReduce:
+ // use reduced-size font, then fall through to uppercase the text
+ f = smallCapsFont;
+
+ case kUppercase:
+ // apply uppercase transform to the string
+ nsDependentSubstring origString(aText + runStart, runLength);
+ nsAutoString convertedString;
+ nsAutoTArray<bool,50> charsToMergeArray;
+ nsAutoTArray<bool,50> deletedCharsArray;
+
+ bool mergeNeeded = nsCaseTransformTextRunFactory::
+ TransformString(origString,
+ convertedString,
+ true,
+ mStyle.language,
+ charsToMergeArray,
+ deletedCharsArray);
+
+ if (mergeNeeded) {
+ // This is the hard case: the transformation caused chars
+ // to be inserted or deleted, so we can't shape directly
+ // into the destination textrun but have to handle the
+ // mismatch of character positions.
+ gfxTextRunFactory::Parameters params = {
+ aContext, nullptr, nullptr, nullptr, 0,
+ aTextRun->GetAppUnitsPerDevUnit()
+ };
+ nsAutoPtr<gfxTextRun> tempRun;
+ tempRun =
+ gfxTextRun::Create(¶ms, convertedString.Length(),
+ aTextRun->GetFontGroup(), 0);
+ tempRun->AddGlyphRun(f, aMatchType, 0, true);
+ if (!f->SplitAndInitTextRun(aContext, tempRun,
+ convertedString.BeginReading(),
+ 0, convertedString.Length(),
+ aScript)) {
+ ok = false;
+ } else {
+ nsAutoPtr<gfxTextRun> mergedRun;
+ mergedRun =
+ gfxTextRun::Create(¶ms, runLength,
+ aTextRun->GetFontGroup(), 0);
+ MergeCharactersInTextRun(mergedRun, tempRun,
+ charsToMergeArray.Elements(),
+ deletedCharsArray.Elements());
+ aTextRun->CopyGlyphDataFrom(mergedRun, 0, runLength,
+ aOffset + runStart);
+ }
+ } else {
+ aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
+ true);
+ if (!f->SplitAndInitTextRun(aContext, aTextRun,
+ convertedString.BeginReading(),
+ aOffset + runStart, runLength,
+ aScript)) {
+ ok = false;
+ }
+ }
+ break;
+ }
+
+ runStart = i;
+ }
+
+ i += extraCodeUnits;
+ if (i < aLength) {
+ runAction = chAction;
+ }
+ }
+
+ return ok;
+}
+
+already_AddRefed<gfxFont>
+gfxFont::GetSmallCapsFont()
+{
+ gfxFontStyle style(*GetStyle());
+ style.size *= SMALL_CAPS_SCALE_FACTOR;
+ style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+ gfxFontEntry* fe = GetFontEntry();
+ bool needsBold = style.weight >= 600 && !fe->IsBold();
+ return fe->FindOrMakeFont(&style, needsBold);
+}
+
+already_AddRefed<gfxFont>
+gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
+{
+ gfxFontStyle style(*GetStyle());
+ style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
+ gfxFontEntry* fe = GetFontEntry();
+ bool needsBold = style.weight >= 600 && !fe->IsBold();
+ return fe->FindOrMakeFont(&style, needsBold);
+}
+
gfxGlyphExtents *
gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
uint32_t i, count = mGlyphExtentsArray.Length();
for (i = 0; i < count; ++i) {
if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
return mGlyphExtentsArray[i];
}
gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
@@ -4682,1623 +3247,16 @@ gfxFont::AddGlyphChangeObserver(GlyphCha
void
gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
{
NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
mGlyphChangeObservers->RemoveEntry(aObserver);
}
-gfxGlyphExtents::~gfxGlyphExtents()
-{
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- gGlyphExtentsWidthsTotalSize +=
- mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
- gGlyphExtentsCount++;
-#endif
- MOZ_COUNT_DTOR(gfxGlyphExtents);
-}
-
-bool
-gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
- gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
-{
- HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
- if (!entry) {
- if (!aContext) {
- NS_WARNING("Could not get glyph extents (no aContext)");
- return false;
- }
-
- if (aFont->SetupCairoFont(aContext)) {
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- ++gGlyphExtentsSetupLazyTight;
-#endif
- aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
- entry = mTightGlyphExtents.GetEntry(aGlyphID);
- }
- if (!entry) {
- NS_WARNING("Could not get glyph extents");
- return false;
- }
- }
-
- *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
- return true;
-}
-
-gfxGlyphExtents::GlyphWidths::~GlyphWidths()
-{
- uint32_t i, count = mBlocks.Length();
- for (i = 0; i < count; ++i) {
- uintptr_t bits = mBlocks[i];
- if (bits && !(bits & 0x1)) {
- delete[] reinterpret_cast<uint16_t *>(bits);
- }
- }
-}
-
-uint32_t
-gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-{
- uint32_t i;
- uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
- for (i = 0; i < mBlocks.Length(); ++i) {
- uintptr_t bits = mBlocks[i];
- if (bits && !(bits & 0x1)) {
- size += aMallocSizeOf(reinterpret_cast<void*>(bits));
- }
- }
- return size;
-}
-
-void
-gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
-{
- uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
- uint32_t len = mBlocks.Length();
- if (block >= len) {
- uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
- if (!elems)
- return;
- memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
- }
-
- uintptr_t bits = mBlocks[block];
- uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
- if (!bits) {
- mBlocks[block] = MakeSingle(glyphOffset, aWidth);
- return;
- }
-
- uint16_t *newBlock;
- if (bits & 0x1) {
- // Expand the block to a real block. We could avoid this by checking
- // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
- newBlock = new uint16_t[BLOCK_SIZE];
- if (!newBlock)
- return;
- uint32_t i;
- for (i = 0; i < BLOCK_SIZE; ++i) {
- newBlock[i] = INVALID_WIDTH;
- }
- newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
- mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
- } else {
- newBlock = reinterpret_cast<uint16_t *>(bits);
- }
- newBlock[glyphOffset] = aWidth;
-}
-
-void
-gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
-{
- HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
- if (!entry)
- return;
- entry->x = aExtentsAppUnits.X();
- entry->y = aExtentsAppUnits.Y();
- entry->width = aExtentsAppUnits.Width();
- entry->height = aExtentsAppUnits.Height();
-}
-
-size_t
-gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-{
- return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
- mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf);
-}
-
-size_t
-gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
- return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
-}
-
-gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
- const gfxFontStyle *aStyle,
- gfxUserFontSet *aUserFontSet)
- : mFamilyList(aFontFamilyList)
- , mStyle(*aStyle)
- , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
- , mHyphenWidth(-1)
- , mUserFontSet(aUserFontSet)
- , mTextPerf(nullptr)
- , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
- , mSkipDrawing(false)
-{
- // We don't use SetUserFontSet() here, as we want to unconditionally call
- // BuildFontList() rather than only do UpdateFontList() if it changed.
- mCurrGeneration = GetGeneration();
- BuildFontList();
-}
-
-void
-gfxFontGroup::FindGenericFonts(FontFamilyType aGenericType,
- nsIAtom *aLanguage,
- void *aClosure)
-{
- nsAutoTArray<nsString, 5> resolvedGenerics;
- ResolveGenericFontNames(aGenericType, aLanguage, resolvedGenerics);
- uint32_t g = 0, numGenerics = resolvedGenerics.Length();
- for (g = 0; g < numGenerics; g++) {
- FindPlatformFont(resolvedGenerics[g], false, aClosure);
- }
-}
-
-/* static */ void
-gfxFontGroup::ResolveGenericFontNames(FontFamilyType aGenericType,
- nsIAtom *aLanguage,
- nsTArray<nsString>& aGenericFamilies)
-{
- static const char kGeneric_serif[] = "serif";
- static const char kGeneric_sans_serif[] = "sans-serif";
- static const char kGeneric_monospace[] = "monospace";
- static const char kGeneric_cursive[] = "cursive";
- static const char kGeneric_fantasy[] = "fantasy";
-
- // treat -moz-fixed as monospace
- if (aGenericType == eFamily_moz_fixed) {
- aGenericType = eFamily_monospace;
- }
-
- // type should be standard generic type at this point
- NS_ASSERTION(aGenericType >= eFamily_serif &&
- aGenericType <= eFamily_fantasy,
- "standard generic font family type required");
-
- // create the lang string
- nsIAtom *langGroupAtom = nullptr;
- nsAutoCString langGroupString;
- if (aLanguage) {
- if (!gLangService) {
- CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
- }
- if (gLangService) {
- nsresult rv;
- langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
- }
- }
- if (!langGroupAtom) {
- langGroupAtom = nsGkAtoms::Unicode;
- }
- langGroupAtom->ToUTF8String(langGroupString);
-
- // map generic type to string
- const char *generic = nullptr;
- switch (aGenericType) {
- case eFamily_serif:
- generic = kGeneric_serif;
- break;
- case eFamily_sans_serif:
- generic = kGeneric_sans_serif;
- break;
- case eFamily_monospace:
- generic = kGeneric_monospace;
- break;
- case eFamily_cursive:
- generic = kGeneric_cursive;
- break;
- case eFamily_fantasy:
- generic = kGeneric_fantasy;
- break;
- default:
- break;
- }
-
- if (!generic) {
- return;
- }
-
- aGenericFamilies.Clear();
-
- // load family for "font.name.generic.lang"
- nsAutoCString prefFontName("font.name.");
- prefFontName.Append(generic);
- prefFontName.Append('.');
- prefFontName.Append(langGroupString);
- gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
- aGenericFamilies);
-
- // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
- if (!aGenericFamilies.IsEmpty()) {
- nsAutoCString prefFontListName("font.name-list.");
- prefFontListName.Append(generic);
- prefFontListName.Append('.');
- prefFontListName.Append(langGroupString);
- gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
- aGenericFamilies);
- }
-
-#if 0 // dump out generic mappings
- printf("%s ===> ", prefFontName.get());
- for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
- if (k > 0) printf(", ");
- printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
- }
- printf("\n");
-#endif
-}
-
-void gfxFontGroup::EnumerateFontList(nsIAtom *aLanguage, void *aClosure)
-{
- // initialize fonts in the font family list
- const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
-
- // lookup fonts in the fontlist
- uint32_t i, numFonts = fontlist.Length();
- for (i = 0; i < numFonts; i++) {
- const FontFamilyName& name = fontlist[i];
- if (name.IsNamed()) {
- FindPlatformFont(name.mName, true, aClosure);
- } else {
- FindGenericFonts(name.mType, aLanguage, aClosure);
- }
- }
-
- // if necessary, append default generic onto the end
- if (mFamilyList.GetDefaultFontType() != eFamily_none &&
- !mFamilyList.HasDefaultGeneric()) {
- FindGenericFonts(mFamilyList.GetDefaultFontType(),
- aLanguage,
- aClosure);
- }
-}
-
-void
-gfxFontGroup::BuildFontList()
-{
-// gfxPangoFontGroup behaves differently, so this method is a no-op on that platform
-#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
-
- EnumerateFontList(mStyle.language);
-
- // at this point, fontlist should have been filled in
- // get a default font if none exists
- if (mFonts.Length() == 0) {
- bool needsBold;
- gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
- gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
- NS_ASSERTION(defaultFamily,
- "invalid default font returned by GetDefaultFont");
-
- if (defaultFamily) {
- gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
- needsBold);
- if (fe) {
- nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
- needsBold);
- if (font) {
- mFonts.AppendElement(FamilyFace(defaultFamily, font));
- }
- }
- }
-
- if (mFonts.Length() == 0) {
- // Try for a "font of last resort...."
- // Because an empty font list would be Really Bad for later code
- // that assumes it will be able to get valid metrics for layout,
- // just look for the first usable font and put in the list.
- // (see bug 554544)
- nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
- pfl->GetFontFamilyList(families);
- uint32_t count = families.Length();
- for (uint32_t i = 0; i < count; ++i) {
- gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
- needsBold);
- if (fe) {
- nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
- needsBold);
- if (font) {
- mFonts.AppendElement(FamilyFace(families[i], font));
- break;
- }
- }
- }
- }
-
- if (mFonts.Length() == 0) {
- // an empty font list at this point is fatal; we're not going to
- // be able to do even the most basic layout operations
- char msg[256]; // CHECK buffer length if revising message below
- nsAutoString families;
- mFamilyList.ToString(families);
- sprintf(msg, "unable to find a usable font (%.220s)",
- NS_ConvertUTF16toUTF8(families).get());
- NS_RUNTIMEABORT(msg);
- }
- }
-
- if (!mStyle.systemFont) {
- uint32_t count = mFonts.Length();
- for (uint32_t i = 0; i < count; ++i) {
- gfxFont* font = mFonts[i].Font();
- if (font->GetFontEntry()->mIsBadUnderlineFont) {
- gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
- gfxFloat bad = font->GetMetrics().underlineOffset;
- mUnderlineOffset = std::min(first, bad);
- break;
- }
- }
- }
-#endif
-}
-
-void
-gfxFontGroup::FindPlatformFont(const nsAString& aName,
- bool aUseFontSet,
- void *aClosure)
-{
- bool needsBold;
- gfxFontFamily *family = nullptr;
- gfxFontEntry *fe = nullptr;
-
- if (aUseFontSet) {
- // First, look up in the user font set...
- // If the fontSet matches the family, we must not look for a platform
- // font of the same name, even if we fail to actually get a fontEntry
- // here; we'll fall back to the next name in the CSS font-family list.
- if (mUserFontSet) {
- // If the fontSet matches the family, but the font has not yet finished
- // loading (nor has its load timeout fired), the fontGroup should wait
- // for the download, and not actually draw its text yet.
- family = mUserFontSet->LookupFamily(aName);
- if (family) {
- bool waitForUserFont = false;
- gfxUserFontEntry* userFontEntry = nullptr;
- userFontEntry = mUserFontSet->FindUserFontEntry(family, mStyle,
- needsBold,
- waitForUserFont);
- if (userFontEntry) {
- fe = userFontEntry->GetPlatformFontEntry();
- }
- if (!fe && waitForUserFont) {
- mSkipDrawing = true;
- }
- }
- }
- }
-
- // Not known in the user font set ==> check system fonts
- if (!family) {
- gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList();
- family = fontList->FindFamily(aName, mStyle.systemFont);
- if (family) {
- fe = family->FindFontForStyle(mStyle, needsBold);
- }
- }
-
- // add to the font group, unless it's already there
- if (fe && !HasFont(fe)) {
- nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
- if (font) {
- mFonts.AppendElement(FamilyFace(family, font));
- }
- }
-}
-
-bool
-gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
-{
- uint32_t count = mFonts.Length();
- for (uint32_t i = 0; i < count; ++i) {
- if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
- return true;
- }
- return false;
-}
-
-gfxFontGroup::~gfxFontGroup()
-{
- mFonts.Clear();
-}
-
-gfxFont *
-gfxFontGroup::GetFirstMathFont()
-{
- uint32_t count = mFonts.Length();
- for (uint32_t i = 0; i < count; ++i) {
- gfxFont* font = GetFontAt(i);
- if (font->GetFontEntry()->TryGetMathTable()) {
- return font;
- }
- }
- return nullptr;
-}
-
-gfxFontGroup *
-gfxFontGroup::Copy(const gfxFontStyle *aStyle)
-{
- gfxFontGroup *fg = new gfxFontGroup(mFamilyList, aStyle, mUserFontSet);
- fg->SetTextPerfMetrics(mTextPerf);
- return fg;
-}
-
-bool
-gfxFontGroup::IsInvalidChar(uint8_t ch)
-{
- return ((ch & 0x7f) < 0x20 || ch == 0x7f);
-}
-
-bool
-gfxFontGroup::IsInvalidChar(char16_t ch)
-{
- // All printable 7-bit ASCII values are OK
- if (ch >= ' ' && ch < 0x7f) {
- return false;
- }
- // No point in sending non-printing control chars through font shaping
- if (ch <= 0x9f) {
- return true;
- }
- return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
- (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
- IsBidiControl(ch));
-}
-
-gfxTextRun *
-gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
-{
- aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
- return gfxTextRun::Create(aParams, 0, this, aFlags);
-}
-
-gfxTextRun *
-gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
-{
- aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
-
- gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
- if (!textRun) {
- return nullptr;
- }
-
- gfxFont *font = GetFontAt(0);
- if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
- // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
- // them, and always create at least size 1 fonts, i.e. they still
- // render something for size 0 fonts.
- textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
- }
- else {
- if (font->GetSpaceGlyph()) {
- // Normally, the font has a cached space glyph, so we can avoid
- // the cost of calling FindFontForChar.
- textRun->SetSpaceGlyph(font, aParams->mContext, 0);
- } else {
- // In case the primary font doesn't have <space> (bug 970891),
- // find one that does.
- uint8_t matchType;
- nsRefPtr<gfxFont> spaceFont =
- FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType);
- if (spaceFont) {
- textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0);
- }
- }
- }
-
- // Note that the gfxGlyphExtents glyph bounds storage for the font will
- // always contain an entry for the font's space glyph, so we don't have
- // to call FetchGlyphExtents here.
- return textRun;
-}
-
-gfxTextRun *
-gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags)
-{
- gfxTextRun *textRun =
- gfxTextRun::Create(aParams, aLength, this, aFlags);
- if (!textRun) {
- return nullptr;
- }
-
- textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
- return textRun;
-}
-
-gfxTextRun *
-gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
-{
- // only use U+2010 if it is supported by the first font in the group;
- // it's better to use ASCII '-' from the primary font than to fall back to
- // U+2010 from some other, possibly poorly-matching face
- static const char16_t hyphen = 0x2010;
- gfxFont *font = GetFontAt(0);
- if (font && font->HasCharacter(hyphen)) {
- return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
- gfxFontGroup::TEXT_IS_PERSISTENT);
- }
-
- static const uint8_t dash = '-';
- return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
- gfxFontGroup::TEXT_IS_PERSISTENT);
-}
-
-gfxFloat
-gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
-{
- if (mHyphenWidth < 0) {
- nsRefPtr<gfxContext> ctx(aProvider->GetContext());
- if (ctx) {
- nsAutoPtr<gfxTextRun>
- hyphRun(MakeHyphenTextRun(ctx,
- aProvider->GetAppUnitsPerDevUnit()));
- mHyphenWidth = hyphRun.get() ?
- hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
- }
- }
- return mHyphenWidth;
-}
-
-gfxTextRun *
-gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags)
-{
- if (aLength == 0) {
- return MakeEmptyTextRun(aParams, aFlags);
- }
- if (aLength == 1 && aString[0] == ' ') {
- return MakeSpaceTextRun(aParams, aFlags);
- }
-
- aFlags |= TEXT_IS_8BIT;
-
- if (GetStyle()->size == 0) {
- // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
- // them, and always create at least size 1 fonts, i.e. they still
- // render something for size 0 fonts.
- return MakeBlankTextRun(aLength, aParams, aFlags);
- }
-
- gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
- this, aFlags);
- if (!textRun) {
- return nullptr;
- }
-
- InitTextRun(aParams->mContext, textRun, aString, aLength);
-
- textRun->FetchGlyphExtents(aParams->mContext);
-
- return textRun;
-}
-
-gfxTextRun *
-gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags)
-{
- if (aLength == 0) {
- return MakeEmptyTextRun(aParams, aFlags);
- }
- if (aLength == 1 && aString[0] == ' ') {
- return MakeSpaceTextRun(aParams, aFlags);
- }
- if (GetStyle()->size == 0) {
- return MakeBlankTextRun(aLength, aParams, aFlags);
- }
-
- gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
- this, aFlags);
- if (!textRun) {
- return nullptr;
- }
-
- InitTextRun(aParams->mContext, textRun, aString, aLength);
-
- textRun->FetchGlyphExtents(aParams->mContext);
-
- return textRun;
-}
-
-template<typename T>
-void
-gfxFontGroup::InitTextRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const T *aString,
- uint32_t aLength)
-{
- NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
-
- // we need to do numeral processing even on 8-bit text,
- // in case we're converting Western to Hindi/Arabic digits
- int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
- nsAutoArrayPtr<char16_t> transformedString;
- if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
- // scan the string for numerals that may need to be transformed;
- // if we find any, we'll make a local copy here and use that for
- // font matching and glyph generation/shaping
- bool prevIsArabic =
- (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
- for (uint32_t i = 0; i < aLength; ++i) {
- char16_t origCh = aString[i];
- char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
- if (newCh != origCh) {
- if (!transformedString) {
- transformedString = new char16_t[aLength];
- if (sizeof(T) == sizeof(char16_t)) {
- memcpy(transformedString.get(), aString, i * sizeof(char16_t));
- } else {
- for (uint32_t j = 0; j < i; ++j) {
- transformedString[j] = aString[j];
- }
- }
- }
- }
- if (transformedString) {
- transformedString[i] = newCh;
- }
- prevIsArabic = IS_ARABIC_CHAR(newCh);
- }
- }
-
-#ifdef PR_LOGGING
- PRLogModuleInfo *log = (mStyle.systemFont ?
- gfxPlatform::GetLog(eGfxLog_textrunui) :
- gfxPlatform::GetLog(eGfxLog_textrun));
-#endif
-
- // variant fallback handling may end up passing through this twice
- bool redo;
- do {
- redo = false;
-
- if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
-
-#ifdef PR_LOGGING
- if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
- nsAutoCString lang;
- mStyle.language->ToUTF8String(lang);
- nsAutoString families;
- mFamilyList.ToString(families);
- nsAutoCString str((const char*)aString, aLength);
- PR_LOG(log, PR_LOG_WARNING,\
- ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
- "len %d weight: %d width: %d style: %s size: %6.2f %d-byte "
- "TEXTRUN [%s] ENDTEXTRUN\n",
- (mStyle.systemFont ? "textrunui" : "textrun"),
- NS_ConvertUTF16toUTF8(families).get(),
- (mFamilyList.GetDefaultFontType() == eFamily_serif ?
- "serif" :
- (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
- "sans-serif" : "none")),
- lang.get(), MOZ_SCRIPT_LATIN, aLength,
- uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
- (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
- (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
- "normal")),
- mStyle.size,
- sizeof(T),
- str.get()));
- }
-#endif
-
- // the text is still purely 8-bit; bypass the script-run itemizer
- // and treat it as a single Latin run
- InitScriptRun(aContext, aTextRun, aString,
- 0, aLength, MOZ_SCRIPT_LATIN);
- } else {
- const char16_t *textPtr;
- if (transformedString) {
- textPtr = transformedString.get();
- } else {
- // typecast to avoid compilation error for the 8-bit version,
- // even though this is dead code in that case
- textPtr = reinterpret_cast<const char16_t*>(aString);
- }
-
- // split into script runs so that script can potentially influence
- // the font matching process below
- gfxScriptItemizer scriptRuns(textPtr, aLength);
-
- uint32_t runStart = 0, runLimit = aLength;
- int32_t runScript = MOZ_SCRIPT_LATIN;
- while (scriptRuns.Next(runStart, runLimit, runScript)) {
-
- #ifdef PR_LOGGING
- if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
- nsAutoCString lang;
- mStyle.language->ToUTF8String(lang);
- nsAutoString families;
- mFamilyList.ToString(families);
- uint32_t runLen = runLimit - runStart;
- PR_LOG(log, PR_LOG_WARNING,\
- ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
- "len %d weight: %d width: %d style: %s size: %6.2f "
- "%d-byte TEXTRUN [%s] ENDTEXTRUN\n",
- (mStyle.systemFont ? "textrunui" : "textrun"),
- NS_ConvertUTF16toUTF8(families).get(),
- (mFamilyList.GetDefaultFontType() == eFamily_serif ?
- "serif" :
- (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
- "sans-serif" : "none")),
- lang.get(), runScript, runLen,
- uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
- (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
- (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
- "normal")),
- mStyle.size,
- sizeof(T),
- NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
- }
- #endif
-
- InitScriptRun(aContext, aTextRun, textPtr + runStart,
- runStart, runLimit - runStart, runScript);
- }
- }
-
- // if shaping was aborted due to lack of feature support, clear out
- // glyph runs and redo shaping with fallback forced on
- if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
- redo = true;
- aTextRun->SetShapingState(
- gfxTextRun::eShapingState_ForceFallbackFeature);
- aTextRun->ClearGlyphsAndCharacters();
- }
-
- } while (redo);
-
- if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
- gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
- if (!glyph->IsSimpleGlyph()) {
- glyph->SetClusterStart(true);
- }
- }
-
- // It's possible for CoreText to omit glyph runs if it decides they contain
- // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
- // need to eliminate them from the glyph run array to avoid drawing "partial
- // ligatures" with the wrong font.
- // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
- // it will iterate back over all glyphruns in the textrun, which leads to
- // pathologically-bad perf in the case where a textrun contains many script
- // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
- // every time a new script subrun is processed.
- aTextRun->SanitizeGlyphRuns();
-
- aTextRun->SortGlyphRuns();
-}
-
-template<typename T>
-void
-gfxFontGroup::InitScriptRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const T *aString, // text for this script run,
- // not the entire textrun
- uint32_t aOffset, // position of the script run
- // within the textrun
- uint32_t aLength, // length of the script run
- int32_t aRunScript)
-{
- NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
- NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
- "don't call InitScriptRun with aborted shaping state");
-
- gfxFont *mainFont = GetFontAt(0);
-
- uint32_t runStart = 0;
- nsAutoTArray<gfxTextRange,3> fontRanges;
- ComputeRanges(fontRanges, aString, aLength, aRunScript);
- uint32_t numRanges = fontRanges.Length();
-
- for (uint32_t r = 0; r < numRanges; r++) {
- const gfxTextRange& range = fontRanges[r];
- uint32_t matchedLength = range.Length();
- gfxFont *matchedFont = range.font;
-
- // create the glyph run for this range
- if (matchedFont && mStyle.noFallbackVariantFeatures) {
- // common case - just do glyph layout and record the
- // resulting positioned glyphs
- aTextRun->AddGlyphRun(matchedFont, range.matchType,
- aOffset + runStart, (matchedLength > 0));
- if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
- aString + runStart,
- aOffset + runStart,
- matchedLength,
- aRunScript)) {
- // glyph layout failed! treat as missing glyphs
- matchedFont = nullptr;
- }
- } else if (matchedFont) {
- // shape with some variant feature that requires fallback handling
- bool petiteToSmallCaps = false;
- bool syntheticLower = false;
- bool syntheticUpper = false;
-
- if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
- (aTextRun->GetShapingState() ==
- gfxTextRun::eShapingState_ForceFallbackFeature ||
- !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
- aString, aLength,
- aRunScript)))
- {
- // fallback for subscript/superscript variant glyphs
-
- // if the feature was already used, abort and force
- // fallback across the entire textrun
- gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
-
- if (ss == gfxTextRun::eShapingState_Normal) {
- aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFallback);
- } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
- aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
- return;
- }
-
- nsRefPtr<gfxFont> subSuperFont =
- matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
- aTextRun->AddGlyphRun(subSuperFont, range.matchType,
- aOffset + runStart, (matchedLength > 0));
- if (!subSuperFont->SplitAndInitTextRun(aContext, aTextRun,
- aString + runStart,
- aOffset + runStart,
- matchedLength,
- aRunScript)) {
- // glyph layout failed! treat as missing glyphs
- matchedFont = nullptr;
- }
- } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
- !matchedFont->SupportsVariantCaps(aRunScript,
- mStyle.variantCaps,
- petiteToSmallCaps,
- syntheticLower,
- syntheticUpper))
- {
- // fallback for small-caps variant glyphs
- if (!matchedFont->InitFakeSmallCapsRun(aContext, aTextRun,
- aString + runStart,
- aOffset + runStart,
- matchedLength,
- range.matchType,
- aRunScript,
- syntheticLower,
- syntheticUpper)) {
- matchedFont = nullptr;
- }
- } else {
- // shape normally with variant feature enabled
- gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
-
- // adjust the shaping state if necessary
- if (ss == gfxTextRun::eShapingState_Normal) {
- aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFeature);
- } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
- // already have shaping results using fallback, need to redo
- aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
- return;
- }
-
- // do glyph layout and record the resulting positioned glyphs
- aTextRun->AddGlyphRun(matchedFont, range.matchType,
- aOffset + runStart, (matchedLength > 0));
- if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
- aString + runStart,
- aOffset + runStart,
- matchedLength,
- aRunScript)) {
- // glyph layout failed! treat as missing glyphs
- matchedFont = nullptr;
- }
- }
- } else {
- aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
- aOffset + runStart, (matchedLength > 0));
- }
-
- if (!matchedFont) {
- // We need to set cluster boundaries (and mark spaces) so that
- // surrogate pairs, combining characters, etc behave properly,
- // even if we don't have glyphs for them
- aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
- matchedLength);
-
- // various "missing" characters may need special handling,
- // so we check for them here
- uint32_t runLimit = runStart + matchedLength;
- for (uint32_t index = runStart; index < runLimit; index++) {
- T ch = aString[index];
-
- // tab and newline are not to be displayed as hexboxes,
- // but do need to be recorded in the textrun
- if (ch == '\n') {
- aTextRun->SetIsNewline(aOffset + index);
- continue;
- }
- if (ch == '\t') {
- aTextRun->SetIsTab(aOffset + index);
- continue;
- }
-
- // for 16-bit textruns only, check for surrogate pairs and
- // special Unicode spaces; omit these checks in 8-bit runs
- if (sizeof(T) == sizeof(char16_t)) {
- if (NS_IS_HIGH_SURROGATE(ch) &&
- index + 1 < aLength &&
- NS_IS_LOW_SURROGATE(aString[index + 1]))
- {
- aTextRun->SetMissingGlyph(aOffset + index,
- SURROGATE_TO_UCS4(ch,
- aString[index + 1]),
- mainFont);
- index++;
- continue;
- }
-
- // check if this is a known Unicode whitespace character that
- // we can render using the space glyph with a custom width
- gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
- if (wid >= 0.0) {
- nscoord advance =
- aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
- if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
- aTextRun->GetCharacterGlyphs()[aOffset + index].
- SetSimpleGlyph(advance,
- mainFont->GetSpaceGlyph());
- } else {
- gfxTextRun::DetailedGlyph detailedGlyph;
- detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
- detailedGlyph.mAdvance = advance;
- detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
- gfxShapedText::CompressedGlyph g;
- g.SetComplex(true, true, 1);
- aTextRun->SetGlyphs(aOffset + index,
- g, &detailedGlyph);
- }
- continue;
- }
- }
-
- if (IsInvalidChar(ch)) {
- // invalid chars are left as zero-width/invisible
- continue;
- }
-
- // record char code so we can draw a box with the Unicode value
- aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
- }
- }
-
- runStart += matchedLength;
- }
-}
-
-bool
-gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const uint8_t *aText,
- uint32_t aOffset,
- uint32_t aLength,
- uint8_t aMatchType,
- int32_t aScript,
- bool aSyntheticLower,
- bool aSyntheticUpper)
-{
- NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
- aLength);
- return InitFakeSmallCapsRun(aContext, aTextRun, unicodeString.get(),
- aOffset, aLength, aMatchType, aScript,
- aSyntheticLower, aSyntheticUpper);
-}
-
-bool
-gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const char16_t *aText,
- uint32_t aOffset,
- uint32_t aLength,
- uint8_t aMatchType,
- int32_t aScript,
- bool aSyntheticLower,
- bool aSyntheticUpper)
-{
- bool ok = true;
-
- nsRefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
-
- enum RunCaseAction {
- kNoChange,
- kUppercaseReduce,
- kUppercase
- };
-
- RunCaseAction runAction = kNoChange;
- uint32_t runStart = 0;
-
- for (uint32_t i = 0; i <= aLength; ++i) {
- uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
- // a trailing surrogate as well as the
- // current code unit.
- RunCaseAction chAction = kNoChange;
- // Unless we're at the end, figure out what treatment the current
- // character will need.
- if (i < aLength) {
- uint32_t ch = aText[i];
- if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
- NS_IS_LOW_SURROGATE(aText[i + 1])) {
- ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
- extraCodeUnits = 1;
- }
- // Characters that aren't the start of a cluster are ignored here.
- // They get added to whatever lowercase/non-lowercase run we're in.
- if (IsClusterExtender(ch)) {
- chAction = runAction;
- } else {
- if (ch != ToUpperCase(ch) || mozilla::unicode::SpecialUpper(ch)) {
- // ch is lower case
- chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
- } else if (ch != ToLowerCase(ch)) {
- // ch is upper case
- chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
- if (mStyle.language == nsGkAtoms::el) {
- // In Greek, check for characters that will be modified by
- // the GreekUpperCase mapping - this catches accented
- // capitals where the accent is to be removed (bug 307039).
- // These are handled by using the full-size font with the
- // uppercasing transform.
- mozilla::GreekCasing::State state;
- uint32_t ch2 = mozilla::GreekCasing::UpperCase(ch, state);
- if (ch != ch2 && !aSyntheticUpper) {
- chAction = kUppercase;
- }
- }
- }
- }
- }
-
- // At the end of the text or when the current character needs different
- // casing treatment from the current run, finish the run-in-progress
- // and prepare to accumulate a new run.
- // Note that we do not look at any source data for offset [i] here,
- // as that would be invalid in the case where i==length.
- if ((i == aLength || runAction != chAction) && runStart < i) {
- uint32_t runLength = i - runStart;
- gfxFont* f = this;
- switch (runAction) {
- case kNoChange:
- // just use the current font and the existing string
- aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true);
- if (!f->SplitAndInitTextRun(aContext, aTextRun,
- aText + runStart,
- aOffset + runStart, runLength,
- aScript)) {
- ok = false;
- }
- break;
-
- case kUppercaseReduce:
- // use reduced-size font, then fall through to uppercase the text
- f = smallCapsFont;
-
- case kUppercase:
- // apply uppercase transform to the string
- nsDependentSubstring origString(aText + runStart, runLength);
- nsAutoString convertedString;
- nsAutoTArray<bool,50> charsToMergeArray;
- nsAutoTArray<bool,50> deletedCharsArray;
-
- bool mergeNeeded = nsCaseTransformTextRunFactory::
- TransformString(origString,
- convertedString,
- true,
- mStyle.language,
- charsToMergeArray,
- deletedCharsArray);
-
- if (mergeNeeded) {
- // This is the hard case: the transformation caused chars
- // to be inserted or deleted, so we can't shape directly
- // into the destination textrun but have to handle the
- // mismatch of character positions.
- gfxTextRunFactory::Parameters params = {
- aContext, nullptr, nullptr, nullptr, 0,
- aTextRun->GetAppUnitsPerDevUnit()
- };
- nsAutoPtr<gfxTextRun> tempRun;
- tempRun =
- gfxTextRun::Create(¶ms, convertedString.Length(),
- aTextRun->GetFontGroup(), 0);
- tempRun->AddGlyphRun(f, aMatchType, 0, true);
- if (!f->SplitAndInitTextRun(aContext, tempRun,
- convertedString.BeginReading(),
- 0, convertedString.Length(),
- aScript)) {
- ok = false;
- } else {
- nsAutoPtr<gfxTextRun> mergedRun;
- mergedRun =
- gfxTextRun::Create(¶ms, runLength,
- aTextRun->GetFontGroup(), 0);
- MergeCharactersInTextRun(mergedRun, tempRun,
- charsToMergeArray.Elements(),
- deletedCharsArray.Elements());
- aTextRun->CopyGlyphDataFrom(mergedRun, 0, runLength,
- aOffset + runStart);
- }
- } else {
- aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
- true);
- if (!f->SplitAndInitTextRun(aContext, aTextRun,
- convertedString.BeginReading(),
- aOffset + runStart, runLength,
- aScript)) {
- ok = false;
- }
- }
- break;
- }
-
- runStart = i;
- }
-
- i += extraCodeUnits;
- if (i < aLength) {
- runAction = chAction;
- }
- }
-
- return ok;
-}
-
-already_AddRefed<gfxFont>
-gfxFont::GetSmallCapsFont()
-{
- gfxFontStyle style(*GetStyle());
- style.size *= SMALL_CAPS_SCALE_FACTOR;
- style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
- gfxFontEntry* fe = GetFontEntry();
- bool needsBold = style.weight >= 600 && !fe->IsBold();
- return fe->FindOrMakeFont(&style, needsBold);
-}
-
-already_AddRefed<gfxFont>
-gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
-{
- gfxFontStyle style(*GetStyle());
- style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
- gfxFontEntry* fe = GetFontEntry();
- bool needsBold = style.weight >= 600 && !fe->IsBold();
- return fe->FindOrMakeFont(&style, needsBold);
-}
-
-gfxTextRun *
-gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
- LazyReferenceContextGetter& aRefContextGetter)
-{
- if (mCachedEllipsisTextRun &&
- mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
- return mCachedEllipsisTextRun;
- }
-
- // Use a Unicode ellipsis if the font supports it,
- // otherwise use three ASCII periods as fallback.
- gfxFont* firstFont = GetFontAt(0);
- nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
- ? nsDependentString(kEllipsisChar,
- ArrayLength(kEllipsisChar) - 1)
- : nsDependentString(kASCIIPeriodsChar,
- ArrayLength(kASCIIPeriodsChar) - 1);
-
- nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
- Parameters params = {
- refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
- };
- gfxTextRun* textRun =
- MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms, TEXT_IS_PERSISTENT);
- if (!textRun) {
- return nullptr;
- }
- mCachedEllipsisTextRun = textRun;
- textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
- // textrun prolong the fontgroup's life
- return textRun;
-}
-
-already_AddRefed<gfxFont>
-gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
-{
- if (!aFamily->TestCharacterMap(aCh)) {
- return nullptr;
- }
-
- // Note that we don't need the actual runScript in matchData for
- // gfxFontFamily::SearchAllFontsForChar, it's only used for the
- // system-fallback case. So we can just set it to 0 here.
- GlobalFontMatch matchData(aCh, 0, &mStyle);
- aFamily->SearchAllFontsForChar(&matchData);
- gfxFontEntry *fe = matchData.mBestMatch;
- if (!fe) {
- return nullptr;
- }
-
- bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
- nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
- return font.forget();
-}
-
-already_AddRefed<gfxFont>
-gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
- int32_t aRunScript, gfxFont *aPrevMatchedFont,
- uint8_t *aMatchType)
-{
- // To optimize common cases, try the first font in the font-group
- // before going into the more detailed checks below
- uint32_t nextIndex = 0;
- bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
- bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
- bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
-
- if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
- nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
- if (firstFont->HasCharacter(aCh)) {
- *aMatchType = gfxTextRange::kFontGroup;
- return firstFont.forget();
- }
- // It's possible that another font in the family (e.g. regular face,
- // where the requested style was italic) will support the character
- nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
- if (font) {
- *aMatchType = gfxTextRange::kFontGroup;
- return font.forget();
- }
- // we don't need to check the first font again below
- ++nextIndex;
- }
-
- if (aPrevMatchedFont) {
- // Don't switch fonts for control characters, regardless of
- // whether they are present in the current font, as they won't
- // actually be rendered (see bug 716229)
- if (isJoinControl ||
- GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
- nsRefPtr<gfxFont> ret = aPrevMatchedFont;
- return ret.forget();
- }
-
- // if previous character was a join-causer (ZWJ),
- // use the same font as the previous range if we can
- if (wasJoinCauser) {
- if (aPrevMatchedFont->HasCharacter(aCh)) {
- nsRefPtr<gfxFont> ret = aPrevMatchedFont;
- return ret.forget();
- }
- }
- }
-
- // if this character is a variation selector,
- // use the previous font regardless of whether it supports VS or not.
- // otherwise the text run will be divided.
- if (isVarSelector) {
- if (aPrevMatchedFont) {
- nsRefPtr<gfxFont> ret = aPrevMatchedFont;
- return ret.forget();
- }
- // VS alone. it's meaningless to search different fonts
- return nullptr;
- }
-
- // 1. check remaining fonts in the font group
- uint32_t fontListLength = FontListLength();
- for (uint32_t i = nextIndex; i < fontListLength; i++) {
- nsRefPtr<gfxFont> font = mFonts[i].Font();
- if (font->HasCharacter(aCh)) {
- *aMatchType = gfxTextRange::kFontGroup;
- return font.forget();
- }
-
- font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
- if (font) {
- *aMatchType = gfxTextRange::kFontGroup;
- return font.forget();
- }
- }
-
- // if character is in Private Use Area, don't do matching against pref or system fonts
- if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
- return nullptr;
-
- // 2. search pref fonts
- nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh);
- if (font) {
- *aMatchType = gfxTextRange::kPrefsFallback;
- return font.forget();
- }
-
- // 3. use fallback fonts
- // -- before searching for something else check the font used for the previous character
- if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
- *aMatchType = gfxTextRange::kSystemFallback;
- nsRefPtr<gfxFont> ret = aPrevMatchedFont;
- return ret.forget();
- }
-
- // never fall back for characters from unknown scripts
- if (aRunScript == HB_SCRIPT_UNKNOWN) {
- return nullptr;
- }
-
- // for known "space" characters, don't do a full system-fallback search;
- // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
- if (GetGeneralCategory(aCh) ==
- HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
- GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
- {
- return nullptr;
- }
-
- // -- otherwise look for other stuff
- *aMatchType = gfxTextRange::kSystemFallback;
- font = WhichSystemFontSupportsChar(aCh, aRunScript);
- return font.forget();
-}
-
-template<typename T>
-void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
- const T *aString, uint32_t aLength,
- int32_t aRunScript)
-{
- NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
- NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
-
- uint32_t prevCh = 0;
- int32_t lastRangeIndex = -1;
-
- // initialize prevFont to the group's primary font, so that this will be
- // used for string-initial control chars, etc rather than risk hitting font
- // fallback for these (bug 716229)
- gfxFont *prevFont = GetFontAt(0);
-
- // if we use the initial value of prevFont, we treat this as a match from
- // the font group; fixes bug 978313
- uint8_t matchType = gfxTextRange::kFontGroup;
-
- for (uint32_t i = 0; i < aLength; i++) {
-
- const uint32_t origI = i; // save off in case we increase for surrogate
-
- // set up current ch
- uint32_t ch = aString[i];
-
- // in 16-bit case only, check for surrogate pair
- if (sizeof(T) == sizeof(char16_t)) {
- if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
- NS_IS_LOW_SURROGATE(aString[i + 1])) {
- i++;
- ch = SURROGATE_TO_UCS4(ch, aString[i]);
- }
- }
-
- if (ch == 0xa0) {
- ch = ' ';
- }
-
- // find the font for this char
- nsRefPtr<gfxFont> font =
- FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
-
-#ifndef RELEASE_BUILD
- if (MOZ_UNLIKELY(mTextPerf)) {
- if (matchType == gfxTextRange::kPrefsFallback) {
- mTextPerf->current.fallbackPrefs++;
- } else if (matchType == gfxTextRange::kSystemFallback) {
- mTextPerf->current.fallbackSystem++;
- }
- }
-#endif
-
- prevCh = ch;
-
- if (lastRangeIndex == -1) {
- // first char ==> make a new range
- aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
- lastRangeIndex++;
- prevFont = font;
- } else {
- // if font has changed, make a new range
- gfxTextRange& prevRange = aRanges[lastRangeIndex];
- if (prevRange.font != font || prevRange.matchType != matchType) {
- // close out the previous range
- prevRange.end = origI;
- aRanges.AppendElement(gfxTextRange(origI, i + 1,
- font, matchType));
- lastRangeIndex++;
-
- // update prevFont for the next match, *unless* we switched
- // fonts on a ZWJ, in which case propagating the changed font
- // is probably not a good idea (see bug 619511)
- if (sizeof(T) == sizeof(uint8_t) ||
- !gfxFontUtils::IsJoinCauser(ch))
- {
- prevFont = font;
- }
- }
- }
- }
-
- aRanges[lastRangeIndex].end = aLength;
-}
-
-gfxUserFontSet*
-gfxFontGroup::GetUserFontSet()
-{
- return mUserFontSet;
-}
-
-void
-gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
-{
- if (aUserFontSet == mUserFontSet) {
- return;
- }
- mUserFontSet = aUserFontSet;
- mCurrGeneration = GetGeneration() - 1;
- UpdateFontList();
-}
-
-uint64_t
-gfxFontGroup::GetGeneration()
-{
- if (!mUserFontSet)
- return 0;
- return mUserFontSet->GetGeneration();
-}
-
-// note: gfxPangoFontGroup overrides UpdateFontList, such that
-// BuildFontList is never used
-void
-gfxFontGroup::UpdateFontList()
-{
- if (mCurrGeneration != GetGeneration()) {
- // xxx - can probably improve this to detect when all fonts were found, so no need to update list
- mFonts.Clear();
- mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
- mSkipDrawing = false;
- BuildFontList();
- mCurrGeneration = GetGeneration();
- mCachedEllipsisTextRun = nullptr;
- }
-}
-
-struct PrefFontCallbackData {
- explicit PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
- : mPrefFamilies(aFamiliesArray)
- {}
-
- nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
-
- static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
- {
- PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
-
- gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
- if (family) {
- prefFontData->mPrefFamilies.AppendElement(family);
- }
- return true;
- }
-};
-
-already_AddRefed<gfxFont>
-gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
-{
- nsRefPtr<gfxFont> font;
-
- // get the pref font list if it hasn't been set up already
- uint32_t unicodeRange = FindCharUnicodeRange(aCh);
- eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
-
- // if the last pref font was the first family in the pref list, no need to recheck through a list of families
- if (mLastPrefFont && charLang == mLastPrefLang &&
- mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
- font = mLastPrefFont;
- return font.forget();
- }
-
- // based on char lang and page lang, set up list of pref lang fonts to check
- eFontPrefLang prefLangs[kMaxLenPrefLangList];
- uint32_t i, numLangs = 0;
-
- gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
-
- for (i = 0; i < numLangs; i++) {
- nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
- eFontPrefLang currentLang = prefLangs[i];
-
- gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
-
- // get the pref families for a single pref lang
- if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
- eFontPrefLang prefLangsToSearch[1] = { currentLang };
- PrefFontCallbackData prefFontData(families);
- gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
- &prefFontData);
- fontList->SetPrefFontFamilyEntries(currentLang, families);
- }
-
- // find the first pref font that includes the character
- uint32_t j, numPrefs;
- numPrefs = families.Length();
- for (j = 0; j < numPrefs; j++) {
- // look up the appropriate face
- gfxFontFamily *family = families[j];
- if (!family) continue;
-
- // if a pref font is used, it's likely to be used again in the same text run.
- // the style doesn't change so the face lookup can be cached rather than calling
- // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent
- // pref font lookups
- if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
- font = mLastPrefFont;
- return font.forget();
- }
-
- bool needsBold;
- gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
- // if ch in cmap, create and return a gfxFont
- if (fe && fe->TestCharacterMap(aCh)) {
- nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
- if (!prefFont) continue;
- mLastPrefFamily = family;
- mLastPrefFont = prefFont;
- mLastPrefLang = charLang;
- mLastPrefFirstFont = (i == 0 && j == 0);
- return prefFont.forget();
- }
-
- }
- }
-
- return nullptr;
-}
-
-already_AddRefed<gfxFont>
-gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
-{
- gfxFontEntry *fe =
- gfxPlatformFontList::PlatformFontList()->
- SystemFindFontForChar(aCh, aRunScript, &mStyle);
- if (fe) {
- bool wantBold = mStyle.ComputeWeight() >= 6;
- nsRefPtr<gfxFont> font =
- fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
- return font.forget();
- }
-
- return nullptr;
-}
-
-/*static*/ void
-gfxFontGroup::Shutdown()
-{
- NS_IF_RELEASE(gLangService);
-}
-
-nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
-
#define DEFAULT_PIXEL_FONT_SIZE 16.0f
/*static*/ uint32_t
gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
{
if (!aLangTag.Length() || aLangTag.Length() > 4) {
return NO_FONT_LANGUAGE_OVERRIDE;
@@ -6432,1568 +3390,8 @@ gfxFontStyle::AdjustForSubSuperscript(in
NS_FONT_SUB_SUPER_SMALL_SIZE);
size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
}
// clear the variant field
variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
}
-
-void
-gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
- const char16_t *aString,
- uint32_t aLength)
-{
- CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
-
- gfxTextRun::CompressedGlyph extendCluster;
- extendCluster.SetComplex(false, true, 0);
-
- ClusterIterator iter(aString, aLength);
-
- // the ClusterIterator won't be able to tell us if the string
- // _begins_ with a cluster-extender, so we handle that here
- if (aLength && IsClusterExtender(*aString)) {
- *glyphs = extendCluster;
- }
-
- while (!iter.AtEnd()) {
- if (*iter == char16_t(' ')) {
- glyphs->SetIsSpace();
- }
- // advance iter to the next cluster-start (or end of text)
- iter.Next();
- // step past the first char of the cluster
- aString++;
- glyphs++;
- // mark all the rest as cluster-continuations
- while (aString < iter) {
- *glyphs = extendCluster;
- if (NS_IS_LOW_SURROGATE(*aString)) {
- glyphs->SetIsLowSurrogate();
- }
- glyphs++;
- aString++;
- }
- }
-}
-
-void
-gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
- const uint8_t *aString,
- uint32_t aLength)
-{
- CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
- const uint8_t *limit = aString + aLength;
-
- while (aString < limit) {
- if (*aString == uint8_t(' ')) {
- glyphs->SetIsSpace();
- }
- aString++;
- glyphs++;
- }
-}
-
-gfxShapedText::DetailedGlyph *
-gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
-{
- NS_ASSERTION(aIndex < GetLength(), "Index out of range");
-
- if (!mDetailedGlyphs) {
- mDetailedGlyphs = new DetailedGlyphStore();
- }
-
- return mDetailedGlyphs->Allocate(aIndex, aCount);
-}
-
-void
-gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
- const DetailedGlyph *aGlyphs)
-{
- NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
- NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
- "First character can't be a ligature continuation!");
-
- uint32_t glyphCount = aGlyph.GetGlyphCount();
- if (glyphCount > 0) {
- DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
- memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
- }
- GetCharacterGlyphs()[aIndex] = aGlyph;
-}
-
-#define ZWNJ 0x200C
-#define ZWJ 0x200D
-// U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
-// in a future Unicode update. Add it manually for now
-#define ALM 0x061C
-static inline bool
-IsDefaultIgnorable(uint32_t aChar)
-{
- return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
- aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
-}
-
-void
-gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
-{
- uint8_t category = GetGeneralCategory(aChar);
- if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
- category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
- {
- GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
- }
-
- DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
-
- details->mGlyphID = aChar;
- if (IsDefaultIgnorable(aChar)) {
- // Setting advance width to zero will prevent drawing the hexbox
- details->mAdvance = 0;
- } else {
- gfxFloat width =
- std::max(aFont->GetMetrics().aveCharWidth,
- gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
- mAppUnitsPerDevUnit));
- details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
- }
- details->mXOffset = 0;
- details->mYOffset = 0;
- GetCharacterGlyphs()[aIndex].SetMissing(1);
-}
-
-bool
-gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
-{
- if (IsDefaultIgnorable(aCh)) {
- DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
- details->mGlyphID = aCh;
- details->mAdvance = 0;
- details->mXOffset = 0;
- details->mYOffset = 0;
- GetCharacterGlyphs()[aIndex].SetMissing(1);
- return true;
- }
- return false;
-}
-
-void
-gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
- uint32_t aOffset,
- uint32_t aLength)
-{
- uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
- CompressedGlyph *charGlyphs = GetCharacterGlyphs();
- for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
- CompressedGlyph *glyphData = charGlyphs + i;
- if (glyphData->IsSimpleGlyph()) {
- // simple glyphs ==> just add the advance
- int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
- if (CompressedGlyph::IsSimpleAdvance(advance)) {
- glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
- } else {
- // rare case, tested by making this the default
- uint32_t glyphIndex = glyphData->GetSimpleGlyph();
- glyphData->SetComplex(true, true, 1);
- DetailedGlyph detail = {glyphIndex, advance, 0, 0};
- SetGlyphs(i, *glyphData, &detail);
- }
- } else {
- // complex glyphs ==> add offset at cluster/ligature boundaries
- uint32_t detailedLength = glyphData->GetGlyphCount();
- if (detailedLength) {
- DetailedGlyph *details = GetDetailedGlyphs(i);
- if (!details) {
- continue;
- }
- if (IsRightToLeft()) {
- details[0].mAdvance += synAppUnitOffset;
- } else {
- details[detailedLength - 1].mAdvance += synAppUnitOffset;
- }
- }
- }
- }
-}
-
-bool
-gfxTextRun::GlyphRunIterator::NextRun() {
- if (mNextIndex >= mTextRun->mGlyphRuns.Length())
- return false;
- mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
- if (mGlyphRun->mCharacterOffset >= mEndOffset)
- return false;
-
- mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
- uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
- ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
- mStringEnd = std::min(mEndOffset, last);
-
- ++mNextIndex;
- return true;
-}
-
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
-static void
-AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
-{
- // Ignores detailed glyphs... we don't know when those have been constructed
- // Also ignores gfxSkipChars dynamic storage (which won't be anything
- // for preformatted text)
- // Also ignores GlyphRun array, again because it hasn't been constructed
- // by the time this gets called. If there's only one glyphrun that's stored
- // directly in the textrun anyway so no additional overhead.
- uint32_t length = aTextRun->GetLength();
- int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
- bytes += sizeof(gfxTextRun);
- gTextRunStorage += bytes*aSign;
- gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
-}
-#endif
-
-// Helper for textRun creation to preallocate storage for glyph records;
-// this function returns a pointer to the newly-allocated glyph storage.
-// Returns nullptr if allocation fails.
-void *
-gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
-{
- // Allocate the storage we need, returning nullptr on failure rather than
- // throwing an exception (because web content can create huge runs).
- void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
- if (!storage) {
- NS_WARNING("failed to allocate storage for text run!");
- return nullptr;
- }
-
- // Initialize the glyph storage (beyond aSize) to zero
- memset(reinterpret_cast<char*>(storage) + aSize, 0,
- aLength * sizeof(CompressedGlyph));
-
- return storage;
-}
-
-gfxTextRun *
-gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
- uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
-{
- void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
- if (!storage) {
- return nullptr;
- }
-
- return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
-}
-
-gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
- uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
- : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
- , mUserData(aParams->mUserData)
- , mFontGroup(aFontGroup)
- , mReleasedFontGroup(false)
- , mShapingState(eShapingState_Normal)
-{
- NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
- MOZ_COUNT_CTOR(gfxTextRun);
- NS_ADDREF(mFontGroup);
-
-#ifndef RELEASE_BUILD
- gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
- if (tp) {
- tp->current.textrunConst++;
- }
-#endif
-
- mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
-
- if (aParams->mSkipChars) {
- mSkipChars.TakeFrom(aParams->mSkipChars);
- }
-
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- AccountStorageForTextRun(this, 1);
-#endif
-
- mSkipDrawing = mFontGroup->ShouldSkipDrawing();
-}
-
-gfxTextRun::~gfxTextRun()
-{
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- AccountStorageForTextRun(this, -1);
-#endif
-#ifdef DEBUG
- // Make it easy to detect a dead text run
- mFlags = 0xFFFFFFFF;
-#endif
-
- // The cached ellipsis textrun (if any) in a fontgroup will have already
- // been told to release its reference to the group, so we mustn't do that
- // again here.
- if (!mReleasedFontGroup) {
-#ifndef RELEASE_BUILD
- gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
- if (tp) {
- tp->current.textrunDestr++;
- }
-#endif
- NS_RELEASE(mFontGroup);
- }
-
- MOZ_COUNT_DTOR(gfxTextRun);
-}
-
-void
-gfxTextRun::ReleaseFontGroup()
-{
- NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
- NS_RELEASE(mFontGroup);
- mReleasedFontGroup = true;
-}
-
-bool
-gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
- uint8_t *aBreakBefore,
- gfxContext *aRefContext)
-{
- NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
-
- uint32_t changed = 0;
- uint32_t i;
- CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
- for (i = 0; i < aLength; ++i) {
- uint8_t canBreak = aBreakBefore[i];
- if (canBreak && !charGlyphs[i].IsClusterStart()) {
- // This can happen ... there is no guarantee that our linebreaking rules
- // align with the platform's idea of what constitutes a cluster.
- NS_WARNING("Break suggested inside cluster!");
- canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
- }
- changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
- }
- return changed != 0;
-}
-
-gfxTextRun::LigatureData
-gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
- PropertyProvider *aProvider)
-{
- NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
- NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
-
- LigatureData result;
- CompressedGlyph *charGlyphs = mCharacterGlyphs;
-
- uint32_t i;
- for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
- NS_ASSERTION(i > 0, "Ligature at the start of the run??");
- }
- result.mLigatureStart = i;
- for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
- }
- result.mLigatureEnd = i;
-
- int32_t ligatureWidth =
- GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
- // Count the number of started clusters we have seen
- uint32_t totalClusterCount = 0;
- uint32_t partClusterIndex = 0;
- uint32_t partClusterCount = 0;
- for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
- // Treat the first character of the ligature as the start of a
- // cluster for our purposes of allocating ligature width to its
- // characters.
- if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
- ++totalClusterCount;
- if (i < aPartStart) {
- ++partClusterIndex;
- } else if (i < aPartEnd) {
- ++partClusterCount;
- }
- }
- }
- NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
- result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
- result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
-
- // Any rounding errors are apportioned to the final part of the ligature,
- // so that measuring all parts of a ligature and summing them is equal to
- // the ligature width.
- if (aPartEnd == result.mLigatureEnd) {
- gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
- result.mPartWidth += ligatureWidth - allParts;
- }
-
- if (partClusterCount == 0) {
- // nothing to draw
- result.mClipBeforePart = result.mClipAfterPart = true;
- } else {
- // Determine whether we should clip before or after this part when
- // drawing its slice of the ligature.
- // We need to clip before the part if any cluster is drawn before
- // this part.
- result.mClipBeforePart = partClusterIndex > 0;
- // We need to clip after the part if any cluster is drawn after
- // this part.
- result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
- }
-
- if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
- gfxFont::Spacing spacing;
- if (aPartStart == result.mLigatureStart) {
- aProvider->GetSpacing(aPartStart, 1, &spacing);
- result.mPartWidth += spacing.mBefore;
- }
- if (aPartEnd == result.mLigatureEnd) {
- aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
- result.mPartWidth += spacing.mAfter;
- }
- }
-
- return result;
-}
-
-gfxFloat
-gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
- PropertyProvider *aProvider)
-{
- if (aPartStart >= aPartEnd)
- return 0;
- LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
- return data.mPartWidth;
-}
-
-int32_t
-gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
-{
- const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
- int32_t advance = 0;
- uint32_t i;
- for (i = aStart; i < aEnd; ++i, ++glyphData) {
- if (glyphData->IsSimpleGlyph()) {
- advance += glyphData->GetSimpleAdvance();
- } else {
- uint32_t glyphCount = glyphData->GetGlyphCount();
- if (glyphCount == 0) {
- continue;
- }
- const DetailedGlyph *details = GetDetailedGlyphs(i);
- if (details) {
- uint32_t j;
- for (j = 0; j < glyphCount; ++j, ++details) {
- advance += details->mAdvance;
- }
- }
- }
- }
- return advance;
-}
-
-static void
-GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
- gfxTextRun::PropertyProvider *aProvider,
- gfxTextRun::PropertyProvider::Spacing *aSpacing)
-{
- if (aStart >= aEnd)
- return;
-
- aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
-
-#ifdef DEBUG
- // Check to see if we have spacing inside ligatures
-
- const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
- uint32_t i;
-
- for (i = aStart; i < aEnd; ++i) {
- if (!charGlyphs[i].IsLigatureGroupStart()) {
- NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
- "Before-spacing inside a ligature!");
- NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
- "After-spacing inside a ligature!");
- }
- }
-#endif
-}
-
-bool
-gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
- PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- nsTArray<PropertyProvider::Spacing> *aSpacing)
-{
- if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
- return false;
- if (!aSpacing->AppendElements(aEnd - aStart))
- return false;
- memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
- GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
- aSpacing->Elements() + aSpacingStart - aStart);
- memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
- return true;
-}
-
-void
-gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
-{
- if (*aStart >= *aEnd)
- return;
-
- CompressedGlyph *charGlyphs = mCharacterGlyphs;
-
- while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
- ++(*aStart);
- }
- if (*aEnd < GetLength()) {
- while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
- --(*aEnd);
- }
- }
-}
-
-void
-gfxTextRun::DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
- gfxPoint *aPt, PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- TextRunDrawParams& aParams)
-{
- nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
- bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
- aSpacingStart, aSpacingEnd, &spacingBuffer);
- aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
- aFont->Draw(this, aStart, aEnd, aPt, aParams);
-}
-
-static void
-ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
- gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
-{
- if (aLigature->mClipBeforePart) {
- if (aTextRun->IsRightToLeft()) {
- *aRight = std::min(*aRight, aXOrigin);
- } else {
- *aLeft = std::max(*aLeft, aXOrigin);
- }
- }
- if (aLigature->mClipAfterPart) {
- gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
- if (aTextRun->IsRightToLeft()) {
- *aLeft = std::max(*aLeft, endEdge);
- } else {
- *aRight = std::min(*aRight, endEdge);
- }
- }
-}
-
-void
-gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
- gfxPoint *aPt, PropertyProvider *aProvider,
- TextRunDrawParams& aParams)
-{
- if (aStart >= aEnd)
- return;
-
- // Draw partial ligature. We hack this by clipping the ligature.
- LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
- gfxRect clipExtents = aParams.context->GetClipExtents();
- gfxFloat left = clipExtents.X() * mAppUnitsPerDevUnit;
- gfxFloat right = clipExtents.XMost() * mAppUnitsPerDevUnit;
- ClipPartialLigature(this, &left, &right, aPt->x, &data);
-
- {
- // Need to preserve the path, otherwise this can break canvas text-on-path;
- // in general it seems like a good thing, as naive callers probably won't
- // expect gfxTextRun::Draw to implicitly destroy the current path.
- gfxContextPathAutoSaveRestore savePath(aParams.context);
-
- // use division here to ensure that when the rect is aligned on multiples
- // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
- // Also, make sure we snap the rectangle to device pixels.
- aParams.context->Save();
- aParams.context->NewPath();
- aParams.context->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
- clipExtents.Y(),
- (right - left) / mAppUnitsPerDevUnit,
- clipExtents.Height()), true);
- aParams.context->Clip();
- }
-
- gfxPoint pt(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
- DrawGlyphs(aFont, data.mLigatureStart, data.mLigatureEnd, &pt,
- aProvider, aStart, aEnd, aParams);
- aParams.context->Restore();
-
- aPt->x += aParams.direction * data.mPartWidth;
-}
-
-// returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
-static bool
-HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
-{
- gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
- while (iter.NextRun()) {
- gfxFont *font = iter.GetGlyphRun()->mFont;
- if (font && font->IsSyntheticBold()) {
- return true;
- }
- }
-
- return false;
-}
-
-// returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
-// if true, color is set on output
-static bool
-HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
-{
- if (aContext->GetDeviceColor(aCurrentColor)) {
- if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
- return true;
- }
- }
-
- return false;
-}
-
-// helper class for double-buffering drawing with non-opaque color
-struct BufferAlphaColor {
- explicit BufferAlphaColor(gfxContext *aContext)
- : mContext(aContext)
- {
-
- }
-
- ~BufferAlphaColor() {}
-
- void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
- {
- mContext->Save();
- mContext->NewPath();
- mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
- aBounds.Y() / appsPerDevUnit,
- aBounds.Width() / appsPerDevUnit,
- aBounds.Height() / appsPerDevUnit), true);
- mContext->Clip();
- mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
- mContext->PushGroup(gfxContentType::COLOR_ALPHA);
- mAlpha = aAlphaColor.a;
- }
-
- void PopAlpha()
- {
- // pop the text, using the color alpha as the opacity
- mContext->PopGroupToSource();
- mContext->SetOperator(gfxContext::OPERATOR_OVER);
- mContext->Paint(mAlpha);
- mContext->Restore();
- }
-
- gfxContext *mContext;
- gfxFloat mAlpha;
-};
-
-void
-gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
- uint32_t aStart, uint32_t aLength,
- PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
- gfxTextContextPaint *aContextPaint,
- gfxTextRunDrawCallbacks *aCallbacks)
-{
- NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
- NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH ||
- !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
- "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
- NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks,
- "callback must not be specified unless using GLYPH_PATH");
-
- bool skipDrawing = mSkipDrawing;
- if (aDrawMode == DrawMode::GLYPH_FILL) {
- gfxRGBA currentColor;
- if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
- skipDrawing = true;
- }
- }
-
- gfxFloat direction = GetDirection();
-
- if (skipDrawing) {
- // We don't need to draw anything;
- // but if the caller wants advance width, we need to compute it here
- if (aAdvanceWidth) {
- gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
- gfxFont::LOOSE_INK_EXTENTS,
- aContext, aProvider);
- *aAdvanceWidth = metrics.mAdvanceWidth * direction;
- }
-
- // return without drawing
- return;
- }
-
- // Set up parameters that will be constant across all glyph runs we need
- // to draw, regardless of the font used.
- TextRunDrawParams params;
- params.context = aContext;
- params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
- params.isRTL = IsRightToLeft();
- params.direction = direction;
- params.drawMode = aDrawMode;
- params.callbacks = aCallbacks;
- params.runContextPaint = aContextPaint;
- params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
- params.dt = aContext->GetDrawTarget();
-
- gfxPoint pt = aPt;
-
- // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
- // correctly unless first drawn without alpha
- BufferAlphaColor syntheticBoldBuffer(aContext);
- gfxRGBA currentColor;
- bool needToRestore = false;
-
- if (aDrawMode == DrawMode::GLYPH_FILL &&
- HasNonOpaqueColor(aContext, currentColor) &&
- HasSyntheticBold(this, aStart, aLength)) {
- needToRestore = true;
- // measure text, use the bounding box
- gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
- gfxFont::LOOSE_INK_EXTENTS,
- aContext, aProvider);
- metrics.mBoundingBox.MoveBy(aPt);
- syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
- GetAppUnitsPerDevUnit());
- }
-
- GlyphRunIterator iter(this, aStart, aLength);
- while (iter.NextRun()) {
- gfxFont *font = iter.GetGlyphRun()->mFont;
- uint32_t start = iter.GetStringStart();
- uint32_t end = iter.GetStringEnd();
- uint32_t ligatureRunStart = start;
- uint32_t ligatureRunEnd = end;
- ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
-
- bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
- (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
-
- if (drawPartial) {
- DrawPartialLigature(font, start, ligatureRunStart, &pt,
- aProvider, params);
- }
-
- DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &pt,
- aProvider, ligatureRunStart, ligatureRunEnd, params);
-
- if (drawPartial) {
- DrawPartialLigature(font, ligatureRunEnd, end, &pt,
- aProvider, params);
- }
- }
-
- // composite result when synthetic bolding used
- if (needToRestore) {
- syntheticBoldBuffer.PopAlpha();
- }
-
- if (aAdvanceWidth) {
- *aAdvanceWidth = (pt.x - aPt.x)*direction;
- }
-}
-
-void
-gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
- uint32_t aStart, uint32_t aEnd,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContext,
- PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- Metrics *aMetrics)
-{
- nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
- bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
- aSpacingStart, aSpacingEnd, &spacingBuffer);
- Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
- haveSpacing ? spacingBuffer.Elements() : nullptr);
- aMetrics->CombineWith(metrics, IsRightToLeft());
-}
-
-void
-gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
- uint32_t aStart, uint32_t aEnd,
- gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
- PropertyProvider *aProvider, Metrics *aMetrics)
-{
- if (aStart >= aEnd)
- return;
-
- // Measure partial ligature. We hack this by clipping the metrics in the
- // same way we clip the drawing.
- LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
-
- // First measure the complete ligature
- Metrics metrics;
- AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
- aBoundingBoxType, aRefContext,
- aProvider, aStart, aEnd, &metrics);
-
- // Clip the bounding box to the ligature part
- gfxFloat bboxLeft = metrics.mBoundingBox.X();
- gfxFloat bboxRight = metrics.mBoundingBox.XMost();
- // Where we are going to start "drawing" relative to our left baseline origin
- gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
- ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
- metrics.mBoundingBox.x = bboxLeft;
- metrics.mBoundingBox.width = bboxRight - bboxLeft;
-
- // mBoundingBox is now relative to the left baseline origin for the entire
- // ligature. Shift it left.
- metrics.mBoundingBox.x -=
- IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
- : data.mPartAdvance;
- metrics.mAdvanceWidth = data.mPartWidth;
-
- aMetrics->CombineWith(metrics, IsRightToLeft());
-}
-
-gfxTextRun::Metrics
-gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContext,
- PropertyProvider *aProvider)
-{
- NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
-
- Metrics accumulatedMetrics;
- GlyphRunIterator iter(this, aStart, aLength);
- while (iter.NextRun()) {
- gfxFont *font = iter.GetGlyphRun()->mFont;
- uint32_t start = iter.GetStringStart();
- uint32_t end = iter.GetStringEnd();
- uint32_t ligatureRunStart = start;
- uint32_t ligatureRunEnd = end;
- ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
-
- AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
- aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
-
- // XXX This sucks. We have to get glyph extents just so we can detect
- // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
- // even though in almost all cases we could get correct results just
- // by getting some ascent/descent from the font and using our stored
- // advance widths.
- AccumulateMetricsForRun(font,
- ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
- aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
- &accumulatedMetrics);
-
- AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
- aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
- }
-
- return accumulatedMetrics;
-}
-
-#define MEASUREMENT_BUFFER_SIZE 100
-
-uint32_t
-gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
- bool aLineBreakBefore, gfxFloat aWidth,
- PropertyProvider *aProvider,
- bool aSuppressInitialBreak,
- gfxFloat *aTrimWhitespace,
- Metrics *aMetrics,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContext,
- bool *aUsedHyphenation,
- uint32_t *aLastBreak,
- bool aCanWordWrap,
- gfxBreakPriority *aBreakPriority)
-{
- aMaxLength = std::min(aMaxLength, GetLength() - aStart);
-
- NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
-
- uint32_t bufferStart = aStart;
- uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
- PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
- bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
- if (haveSpacing) {
- GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
- spacingBuffer);
- }
- bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
- bool haveHyphenation = aProvider &&
- (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
- (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
- (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
- if (haveHyphenation) {
- aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
- hyphenBuffer);
- }
-
- gfxFloat width = 0;
- gfxFloat advance = 0;
- // The number of space characters that can be trimmed
- uint32_t trimmableChars = 0;
- // The amount of space removed by ignoring trimmableChars
- gfxFloat trimmableAdvance = 0;
- int32_t lastBreak = -1;
- int32_t lastBreakTrimmableChars = -1;
- gfxFloat lastBreakTrimmableAdvance = -1;
- bool aborted = false;
- uint32_t end = aStart + aMaxLength;
- bool lastBreakUsedHyphenation = false;
-
- uint32_t ligatureRunStart = aStart;
- uint32_t ligatureRunEnd = end;
- ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
-
- uint32_t i;
- for (i = aStart; i < end; ++i) {
- if (i >= bufferStart + bufferLength) {
- // Fetch more spacing and hyphenation data
- bufferStart = i;
- bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
- if (haveSpacing) {
- GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
- spacingBuffer);
- }
- if (haveHyphenation) {
- aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
- hyphenBuffer);
- }
- }
-
- // There can't be a word-wrap break opportunity at the beginning of the
- // line: if the width is too small for even one character to fit, it
- // could be the first and last break opportunity on the line, and that
- // would trigger an infinite loop.
- if (!aSuppressInitialBreak || i > aStart) {
- bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
- bool atHyphenationBreak =
- !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart];
- bool atBreak = atNaturalBreak || atHyphenationBreak;
- bool wordWrapping =
- aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
- *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
-
- if (atBreak || wordWrapping) {
- gfxFloat hyphenatedAdvance = advance;
- if (atHyphenationBreak) {
- hyphenatedAdvance += aProvider->GetHyphenWidth();
- }
-
- if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
- // We can break here.
- lastBreak = i;
- lastBreakTrimmableChars = trimmableChars;
- lastBreakTrimmableAdvance = trimmableAdvance;
- lastBreakUsedHyphenation = atHyphenationBreak;
- *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
- : gfxBreakPriority::eWordWrapBreak;
- }
-
- width += advance;
- advance = 0;
- if (width - trimmableAdvance > aWidth) {
- // No more text fits. Abort
- aborted = true;
- break;
- }
- }
- }
-
- gfxFloat charAdvance;
- if (i >= ligatureRunStart && i < ligatureRunEnd) {
- charAdvance = GetAdvanceForGlyphs(i, i + 1);
- if (haveSpacing) {
- PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
- charAdvance += space->mBefore + space->mAfter;
- }
- } else {
- charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
- }
-
- advance += charAdvance;
- if (aTrimWhitespace) {
- if (mCharacterGlyphs[i].CharIsSpace()) {
- ++trimmableChars;
- trimmableAdvance += charAdvance;
- } else {
- trimmableAdvance = 0;
- trimmableChars = 0;
- }
- }
- }
-
- if (!aborted) {
- width += advance;
- }
-
- // There are three possibilities:
- // 1) all the text fit (width <= aWidth)
- // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
- // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
- uint32_t charsFit;
- bool usedHyphenation = false;
- if (width - trimmableAdvance <= aWidth) {
- charsFit = aMaxLength;
- } else if (lastBreak >= 0) {
- charsFit = lastBreak - aStart;
- trimmableChars = lastBreakTrimmableChars;
- trimmableAdvance = lastBreakTrimmableAdvance;
- usedHyphenation = lastBreakUsedHyphenation;
- } else {
- charsFit = aMaxLength;
- }
-
- if (aMetrics) {
- *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
- aBoundingBoxType, aRefContext, aProvider);
- }
- if (aTrimWhitespace) {
- *aTrimWhitespace = trimmableAdvance;
- }
- if (aUsedHyphenation) {
- *aUsedHyphenation = usedHyphenation;
- }
- if (aLastBreak && charsFit == aMaxLength) {
- if (lastBreak < 0) {
- *aLastBreak = UINT32_MAX;
- } else {
- *aLastBreak = lastBreak - aStart;
- }
- }
-
- return charsFit;
-}
-
-gfxFloat
-gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
- PropertyProvider *aProvider)
-{
- NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
-
- uint32_t ligatureRunStart = aStart;
- uint32_t ligatureRunEnd = aStart + aLength;
- ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
-
- gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
- ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
-
- // Account for all remaining spacing here. This is more efficient than
- // processing it along with the glyphs.
- if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
- uint32_t i;
- nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
- if (spacingBuffer.AppendElements(aLength)) {
- GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
- spacingBuffer.Elements());
- for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
- PropertyProvider::Spacing *space = &spacingBuffer[i];
- result += space->mBefore + space->mAfter;
- }
- }
- }
-
- return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
-}
-
-bool
-gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
- bool aLineBreakBefore, bool aLineBreakAfter,
- gfxFloat *aAdvanceWidthDelta,
- gfxContext *aRefContext)
-{
- // Do nothing because our shaping does not currently take linebreaks into
- // account. There is no change in advance width.
- if (aAdvanceWidthDelta) {
- *aAdvanceWidthDelta = 0;
- }
- return false;
-}
-
-uint32_t
-gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
-{
- NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
- NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
- "non-empty text but no glyph runs present!");
- if (aOffset == GetLength())
- return mGlyphRuns.Length();
- uint32_t start = 0;
- uint32_t end = mGlyphRuns.Length();
- while (end - start > 1) {
- uint32_t mid = (start + end)/2;
- if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
- start = mid;
- } else {
- end = mid;
- }
- }
- NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
- "Hmm, something went wrong, aOffset should have been found");
- return start;
-}
-
-nsresult
-gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
- uint32_t aUTF16Offset, bool aForceNewRun)
-{
- NS_ASSERTION(aFont, "adding glyph run for null font!");
- if (!aFont) {
- return NS_OK;
- }
- uint32_t numGlyphRuns = mGlyphRuns.Length();
- if (!aForceNewRun && numGlyphRuns > 0) {
- GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
-
- NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
- "Glyph runs out of order (and run not forced)");
-
- // Don't append a run if the font is already the one we want
- if (lastGlyphRun->mFont == aFont &&
- lastGlyphRun->mMatchType == aMatchType)
- {
- return NS_OK;
- }
-
- // If the offset has not changed, avoid leaving a zero-length run
- // by overwriting the last entry instead of appending...
- if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
-
- // ...except that if the run before the last entry had the same
- // font as the new one wants, merge with it instead of creating
- // adjacent runs with the same font
- if (numGlyphRuns > 1 &&
- mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
- mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
- {
- mGlyphRuns.TruncateLength(numGlyphRuns - 1);
- return NS_OK;
- }
-
- lastGlyphRun->mFont = aFont;
- lastGlyphRun->mMatchType = aMatchType;
- return NS_OK;
- }
- }
-
- NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
- "First run doesn't cover the first character (and run not forced)?");
-
- GlyphRun *glyphRun = mGlyphRuns.AppendElement();
- if (!glyphRun)
- return NS_ERROR_OUT_OF_MEMORY;
- glyphRun->mFont = aFont;
- glyphRun->mCharacterOffset = aUTF16Offset;
- glyphRun->mMatchType = aMatchType;
- return NS_OK;
-}
-
-void
-gfxTextRun::SortGlyphRuns()
-{
- if (mGlyphRuns.Length() <= 1)
- return;
-
- nsTArray<GlyphRun> runs(mGlyphRuns);
- GlyphRunOffsetComparator comp;
- runs.Sort(comp);
-
- // Now copy back, coalescing adjacent glyph runs that have the same font
- mGlyphRuns.Clear();
- uint32_t i, count = runs.Length();
- for (i = 0; i < count; ++i) {
- // a GlyphRun with the same font as the previous GlyphRun can just
- // be skipped; the last GlyphRun will cover its character range.
- if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
- mGlyphRuns.AppendElement(runs[i]);
- // If two fonts have the same character offset, Sort() will have
- // randomized the order.
- NS_ASSERTION(i == 0 ||
- runs[i].mCharacterOffset !=
- runs[i - 1].mCharacterOffset,
- "Two fonts for the same run, glyph indices may not match the font");
- }
- }
-}
-
-// Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
-// therefore we only call it once, at the end of textrun construction,
-// NOT incrementally as each glyph run is added (bug 680402).
-void
-gfxTextRun::SanitizeGlyphRuns()
-{
- if (mGlyphRuns.Length() <= 1)
- return;
-
- // If any glyph run starts with ligature-continuation characters, we need to advance it
- // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
- // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
- // it appear as if a ligature has been formed)
- int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
- const CompressedGlyph *charGlyphs = mCharacterGlyphs;
- for (i = lastRunIndex; i >= 0; --i) {
- GlyphRun& run = mGlyphRuns[i];
- while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
- run.mCharacterOffset < GetLength()) {
- run.mCharacterOffset++;
- }
- // if the run has become empty, eliminate it
- if ((i < lastRunIndex &&
- run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
- (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
- mGlyphRuns.RemoveElementAt(i);
- --lastRunIndex;
- }
- }
-}
-
-uint32_t
-gfxTextRun::CountMissingGlyphs()
-{
- uint32_t i;
- uint32_t count = 0;
- for (i = 0; i < GetLength(); ++i) {
- if (mCharacterGlyphs[i].IsMissing()) {
- ++count;
- }
- }
- return count;
-}
-
-void
-gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
-{
- uint32_t wordLen = aShapedWord->GetLength();
- NS_ASSERTION(aOffset + wordLen <= GetLength(),
- "word overruns end of textrun!");
-
- CompressedGlyph *charGlyphs = GetCharacterGlyphs();
- const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
- if (aShapedWord->HasDetailedGlyphs()) {
- for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
- const CompressedGlyph& g = wordGlyphs[i];
- if (g.IsSimpleGlyph()) {
- charGlyphs[aOffset] = g;
- } else {
- const DetailedGlyph *details =
- g.GetGlyphCount() > 0 ?
- aShapedWord->GetDetailedGlyphs(i) : nullptr;
- SetGlyphs(aOffset, g, details);
- }
- }
- } else {
- memcpy(charGlyphs + aOffset, wordGlyphs,
- wordLen * sizeof(CompressedGlyph));
- }
-}
-
-void
-gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
- uint32_t aLength, uint32_t aDest)
-{
- NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
- "Source substring out of range");
- NS_ASSERTION(aDest + aLength <= GetLength(),
- "Destination substring out of range");
-
- if (aSource->mSkipDrawing) {
- mSkipDrawing = true;
- }
-
- // Copy base glyph data, and DetailedGlyph data where present
- const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
- CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
- for (uint32_t i = 0; i < aLength; ++i) {
- CompressedGlyph g = srcGlyphs[i];
- g.SetCanBreakBefore(!g.IsClusterStart() ?
- CompressedGlyph::FLAG_BREAK_TYPE_NONE :
- dstGlyphs[i].CanBreakBefore());
- if (!g.IsSimpleGlyph()) {
- uint32_t count = g.GetGlyphCount();
- if (count > 0) {
- DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
- if (dst) {
- DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
- if (src) {
- ::memcpy(dst, src, count * sizeof(DetailedGlyph));
- } else {
- g.SetMissing(0);
- }
- } else {
- g.SetMissing(0);
- }
- }
- }
- dstGlyphs[i] = g;
- }
-
- // Copy glyph runs
- GlyphRunIterator iter(aSource, aStart, aLength);
-#ifdef DEBUG
- gfxFont *lastFont = nullptr;
-#endif
- while (iter.NextRun()) {
- gfxFont *font = iter.GetGlyphRun()->mFont;
- NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
-#ifdef DEBUG
- lastFont = font;
- uint32_t end = iter.GetStringEnd();
-#endif
- uint32_t start = iter.GetStringStart();
-
- // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
- // Although it's unusual (and not desirable), it's possible for us to assign
- // different fonts to a base character and a following diacritic.
- // Example on OSX 10.5/10.6 with default fonts installed:
- // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
- // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
- // This means the rendering of the cluster will probably not be very good,
- // but it's the best we can do for now if the specified font only covered the
- // initial base character and not its applied marks.
- NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
- "Started font run in the middle of a cluster");
- NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
- "Ended font run in the middle of a cluster");
-
- nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
- start - aStart + aDest, false);
- if (NS_FAILED(rv))
- return;
- }
-}
-
-void
-gfxTextRun::ClearGlyphsAndCharacters()
-{
- ResetGlyphRuns();
- memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
- mLength * sizeof(CompressedGlyph));
- mDetailedGlyphs = nullptr;
-}
-
-void
-gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
- uint32_t aCharIndex)
-{
- if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
- return;
- }
-
- aFont->InitWordCache();
- static const uint8_t space = ' ';
- gfxShapedWord *sw = aFont->GetShapedWord(aContext,
- &space, 1,
- HashMix(0, ' '),
- MOZ_SCRIPT_LATIN,
- mAppUnitsPerDevUnit,
- gfxTextRunFactory::TEXT_IS_8BIT |
- gfxTextRunFactory::TEXT_IS_ASCII |
- gfxTextRunFactory::TEXT_IS_PERSISTENT,
- nullptr);
- if (sw) {
- AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
- CopyGlyphDataFrom(sw, aCharIndex);
- }
-}
-
-bool
-gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
- uint32_t aCharIndex, char16_t aSpaceChar)
-{
- uint32_t spaceGlyph = aFont->GetSpaceGlyph();
- if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
- return false;
- }
-
- uint32_t spaceWidthAppUnits =
- NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
- if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
- return false;
- }
-
- AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
- CompressedGlyph g;
- g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
- if (aSpaceChar == ' ') {
- g.SetIsSpace();
- }
- GetCharacterGlyphs()[aCharIndex] = g;
- return true;
-}
-
-void
-gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
-{
- bool needsGlyphExtents = NeedsGlyphExtents(this);
- if (!needsGlyphExtents && !mDetailedGlyphs)
- return;
-
- uint32_t i, runCount = mGlyphRuns.Length();
- CompressedGlyph *charGlyphs = mCharacterGlyphs;
- for (i = 0; i < runCount; ++i) {
- const GlyphRun& run = mGlyphRuns[i];
- gfxFont *font = run.mFont;
- if (MOZ_UNLIKELY(font->GetStyle()->size == 0)) {
- continue;
- }
-
- uint32_t start = run.mCharacterOffset;
- uint32_t end = i + 1 < runCount ?
- mGlyphRuns[i + 1].mCharacterOffset : GetLength();
- bool fontIsSetup = false;
- uint32_t j;
- gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
-
- for (j = start; j < end; ++j) {
- const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
- if (glyphData->IsSimpleGlyph()) {
- // If we're in speed mode, don't set up glyph extents here; we'll
- // just return "optimistic" glyph bounds later
- if (needsGlyphExtents) {
- uint32_t glyphIndex = glyphData->GetSimpleGlyph();
- if (!extents->IsGlyphKnown(glyphIndex)) {
- if (!fontIsSetup) {
- if (!font->SetupCairoFont(aRefContext)) {
- NS_WARNING("failed to set up font for glyph extents");
- break;
- }
- fontIsSetup = true;
- }
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- ++gGlyphExtentsSetupEagerSimple;
-#endif
- font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
- }
- }
- } else if (!glyphData->IsMissing()) {
- uint32_t glyphCount = glyphData->GetGlyphCount();
- if (glyphCount == 0) {
- continue;
- }
- const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
- if (!details) {
- continue;
- }
- for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
- uint32_t glyphIndex = details->mGlyphID;
- if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
- if (!fontIsSetup) {
- if (!font->SetupCairoFont(aRefContext)) {
- NS_WARNING("failed to set up font for glyph extents");
- break;
- }
- fontIsSetup = true;
- }
-#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
- ++gGlyphExtentsSetupEagerTight;
-#endif
- font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
- }
- }
- }
- }
- }
-}
-
-
-gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
- : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
-{
-}
-
-void
-gfxTextRun::ClusterIterator::Reset()
-{
- mCurrentChar = uint32_t(-1);
-}
-
-bool
-gfxTextRun::ClusterIterator::NextCluster()
-{
- uint32_t len = mTextRun->GetLength();
- while (++mCurrentChar < len) {
- if (mTextRun->IsClusterStart(mCurrentChar)) {
- return true;
- }
- }
-
- mCurrentChar = uint32_t(-1);
- return false;
-}
-
-uint32_t
-gfxTextRun::ClusterIterator::ClusterLength() const
-{
- if (mCurrentChar == uint32_t(-1)) {
- return 0;
- }
-
- uint32_t i = mCurrentChar,
- len = mTextRun->GetLength();
- while (++i < len) {
- if (mTextRun->IsClusterStart(i)) {
- break;
- }
- }
-
- return i - mCurrentChar;
-}
-
-gfxFloat
-gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
-{
- if (mCurrentChar == uint32_t(-1)) {
- return 0;
- }
-
- return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
-}
-
-size_t
-gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
-{
- // The second arg is how much gfxTextRun::AllocateStorage would have
- // allocated.
- size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
-
- if (mDetailedGlyphs) {
- total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
- }
-
- return total;
-}
-
-size_t
-gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
-{
- return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
-}
-
-
-#ifdef DEBUG
-void
-gfxTextRun::Dump(FILE* aOutput) {
- if (!aOutput) {
- aOutput = stdout;
- }
-
- uint32_t i;
- fputc('[', aOutput);
- for (i = 0; i < mGlyphRuns.Length(); ++i) {
- if (i > 0) {
- fputc(',', aOutput);
- }
- gfxFont* font = mGlyphRuns[i].mFont;
- const gfxFontStyle* style = font->GetStyle();
- NS_ConvertUTF16toUTF8 fontName(font->GetName());
- nsAutoCString lang;
- style->language->ToUTF8String(lang);
- fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
- fontName.get(), style->size,
- style->weight, style->style, lang.get());
- }
- fputc(']', aOutput);
-}
-#endif
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -2,73 +2,59 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_FONT_H
#define GFX_FONT_H
#include "gfxTypes.h"
+#include "gfxFontEntry.h"
#include "nsString.h"
#include "gfxPoint.h"
-#include "gfxFontFamilyList.h"
-#include "gfxFontUtils.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
-#include "gfxSkipChars.h"
#include "gfxRect.h"
#include "nsExpirationTracker.h"
#include "gfxPlatform.h"
#include "nsIAtom.h"
#include "mozilla/HashFunctions.h"
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
-#include "gfxFontFeatures.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Attributes.h"
#include <algorithm>
#include "DrawMode.h"
-#include "nsUnicodeScriptCodes.h"
#include "nsDataHashtable.h"
#include "harfbuzz/hb.h"
#include "mozilla/gfx/2D.h"
typedef struct _cairo_scaled_font cairo_scaled_font_t;
-typedef struct gr_face gr_face;
+//typedef struct gr_face gr_face;
#ifdef DEBUG
#include <stdio.h>
#endif
class gfxContext;
class gfxTextRun;
class gfxFont;
-class gfxFontFamily;
-class gfxFontGroup;
-class gfxGraphiteShaper;
-class gfxHarfBuzzShaper;
-class gfxUserFontSet;
-class gfxUserFontData;
+class gfxGlyphExtents;
class gfxShapedText;
class gfxShapedWord;
-class gfxSVGGlyphs;
-class gfxMathTable;
+class gfxSkipChars;
class gfxTextContextPaint;
-class FontInfoData;
-
-class nsILanguageAtomService;
#define FONT_MAX_SIZE 2000.0
#define NO_FONT_LANGUAGE_OVERRIDE 0
#define SMALL_CAPS_SCALE_FACTOR 0.8
-struct FontListSizes;
struct gfxTextRunDrawCallbacks;
namespace mozilla {
namespace gfx {
class GlyphRenderingOptions;
}
}
@@ -208,794 +194,16 @@ struct gfxFontStyle {
}
static void ParseFontFeatureSettings(const nsString& aFeatureString,
nsTArray<gfxFontFeature>& aFeatures);
static uint32_t ParseFontLanguageOverride(const nsString& aLangTag);
};
-class gfxCharacterMap : public gfxSparseBitSet {
-public:
- nsrefcnt AddRef() {
- NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
- ++mRefCnt;
- NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this));
- return mRefCnt;
- }
-
- nsrefcnt Release() {
- NS_PRECONDITION(0 != mRefCnt, "dup release");
- --mRefCnt;
- NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap");
- if (mRefCnt == 0) {
- NotifyReleased();
- // |this| has been deleted.
- return 0;
- }
- return mRefCnt;
- }
-
- gfxCharacterMap() :
- mHash(0), mBuildOnTheFly(false), mShared(false)
- { }
-
- void CalcHash() { mHash = GetChecksum(); }
-
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
- return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf);
- }
-
- // hash of the cmap bitvector
- uint32_t mHash;
-
- // if cmap is built on the fly it's never shared
- bool mBuildOnTheFly;
-
- // cmap is shared globally
- bool mShared;
-
-protected:
- void NotifyReleased();
-
- nsAutoRefCnt mRefCnt;
-
-private:
- gfxCharacterMap(const gfxCharacterMap&);
- gfxCharacterMap& operator=(const gfxCharacterMap&);
-};
-
-class gfxFontEntry {
-public:
- NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
-
- explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
-
- // unique name for the face, *not* the family; not necessarily the
- // "real" or user-friendly name, may be an internal identifier
- const nsString& Name() const { return mName; }
-
- // family name
- const nsString& FamilyName() const { return mFamilyName; }
-
- // The following two methods may be relatively expensive, as they
- // will (usually, except on Linux) load and parse the 'name' table;
- // they are intended only for the font-inspection API, not for
- // perf-critical layout/drawing work.
-
- // The "real" name of the face, if available from the font resource;
- // returns Name() if nothing better is available.
- virtual nsString RealFaceName();
-
- uint16_t Weight() const { return mWeight; }
- int16_t Stretch() const { return mStretch; }
-
- bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
- bool IsLocalUserFont() const { return mIsLocalUserFont; }
- bool IsFixedPitch() const { return mFixedPitch; }
- bool IsItalic() const { return mItalic; }
- bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above
- bool IgnoreGDEF() const { return mIgnoreGDEF; }
- bool IgnoreGSUB() const { return mIgnoreGSUB; }
-
- // whether a feature is supported by the font (limited to a small set
- // of features for which some form of fallback needs to be implemented)
- bool SupportsOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag);
- bool SupportsGraphiteFeature(uint32_t aFeatureTag);
-
- // returns a set containing all input glyph ids for a given feature
- const hb_set_t*
- InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag);
-
- virtual bool IsSymbolFont();
-
- virtual bool HasFontTable(uint32_t aTableTag);
-
- inline bool HasGraphiteTables() {
- if (!mCheckedForGraphiteTables) {
- CheckForGraphiteTables();
- mCheckedForGraphiteTables = true;
- }
- return mHasGraphiteTables;
- }
-
- inline bool HasCmapTable() {
- if (!mCharacterMap) {
- ReadCMAP();
- NS_ASSERTION(mCharacterMap, "failed to initialize character map");
- }
- return mHasCmapTable;
- }
-
- inline bool HasCharacter(uint32_t ch) {
- if (mCharacterMap && mCharacterMap->test(ch)) {
- return true;
- }
- return TestCharacterMap(ch);
- }
-
- virtual bool SkipDuringSystemFallback() { return false; }
- virtual bool TestCharacterMap(uint32_t aCh);
- nsresult InitializeUVSMap();
- uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
-
- // All concrete gfxFontEntry subclasses (except gfxUserFontEntry) need
- // to override this, otherwise the font will never be used as it will
- // be considered to support no characters.
- // ReadCMAP() must *always* set the mCharacterMap pointer to a valid
- // gfxCharacterMap, even if empty, as other code assumes this pointer
- // can be safely dereferenced.
- virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
-
- bool TryGetSVGData(gfxFont* aFont);
- bool HasSVGGlyph(uint32_t aGlyphId);
- bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
- gfxRect *aResult);
- bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
- gfxTextContextPaint *aContextPaint);
- // Call this when glyph geometry or rendering has changed
- // (e.g. animated SVG glyphs)
- void NotifyGlyphsChanged();
-
- enum MathConstant {
- // The order of the constants must match the order of the fields
- // defined in the MATH table.
- ScriptPercentScaleDown,
- ScriptScriptPercentScaleDown,
- DelimitedSubFormulaMinHeight,
- DisplayOperatorMinHeight,
- MathLeading,
- AxisHeight,
- AccentBaseHeight,
- FlattenedAccentBaseHeight,
- SubscriptShiftDown,
- SubscriptTopMax,
- SubscriptBaselineDropMin,
- SuperscriptShiftUp,
- SuperscriptShiftUpCramped,
- SuperscriptBottomMin,
- SuperscriptBaselineDropMax,
- SubSuperscriptGapMin,
- SuperscriptBottomMaxWithSubscript,
- SpaceAfterScript,
- UpperLimitGapMin,
- UpperLimitBaselineRiseMin,
- LowerLimitGapMin,
- LowerLimitBaselineDropMin,
- StackTopShiftUp,
- StackTopDisplayStyleShiftUp,
- StackBottomShiftDown,
- StackBottomDisplayStyleShiftDown,
- StackGapMin,
- StackDisplayStyleGapMin,
- StretchStackTopShiftUp,
- StretchStackBottomShiftDown,
- StretchStackGapAboveMin,
- StretchStackGapBelowMin,
- FractionNumeratorShiftUp,
- FractionNumeratorDisplayStyleShiftUp,
- FractionDenominatorShiftDown,
- FractionDenominatorDisplayStyleShiftDown,
- FractionNumeratorGapMin,
- FractionNumDisplayStyleGapMin,
- FractionRuleThickness,
- FractionDenominatorGapMin,
- FractionDenomDisplayStyleGapMin,
- SkewedFractionHorizontalGap,
- SkewedFractionVerticalGap,
- OverbarVerticalGap,
- OverbarRuleThickness,
- OverbarExtraAscender,
- UnderbarVerticalGap,
- UnderbarRuleThickness,
- UnderbarExtraDescender,
- RadicalVerticalGap,
- RadicalDisplayStyleVerticalGap,
- RadicalRuleThickness,
- RadicalExtraAscender,
- RadicalKernBeforeDegree,
- RadicalKernAfterDegree,
- RadicalDegreeBottomRaisePercent
- };
-
- // Call TryGetMathTable to try to load the Open Type MATH table. The other
- // functions forward the call to the gfxMathTable class. The GetMath...()
- // functions MUST NOT be called unless TryGetMathTable() has returned true.
- bool TryGetMathTable();
- gfxFloat GetMathConstant(MathConstant aConstant);
- bool GetMathItalicsCorrection(uint32_t aGlyphID,
- gfxFloat* aItalicCorrection);
- uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
- uint16_t aSize);
- bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
- uint32_t aGlyphs[4]);
-
- bool TryGetColorGlyphs();
- bool GetColorLayersInfo(uint32_t aGlyphId,
- nsTArray<uint16_t>& layerGlyphs,
- nsTArray<mozilla::gfx::Color>& layerColors);
-
- virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
- return true;
- }
- virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
- return true;
- }
-
- // Access to raw font table data (needed for Harfbuzz):
- // returns a pointer to data owned by the fontEntry or the OS,
- // which will remain valid until the blob is destroyed.
- // The data MUST be treated as read-only; we may be getting a
- // reference to a shared system font cache.
- //
- // The default implementation uses CopyFontTable to get the data
- // into a byte array, and maintains a cache of loaded tables.
- //
- // Subclasses should override this if they can provide more efficient
- // access than copying table data into our own buffers.
- //
- // Get blob that encapsulates a specific font table, or nullptr if
- // the table doesn't exist in the font.
- //
- // Caller is responsible to call hb_blob_destroy() on the returned blob
- // (if non-nullptr) when no longer required. For transient access to a
- // table, use of AutoTable (below) is generally preferred.
- virtual hb_blob_t *GetFontTable(uint32_t aTag);
-
- // Stack-based utility to return a specified table, automatically releasing
- // the blob when the AutoTable goes out of scope.
- class AutoTable {
- public:
- AutoTable(gfxFontEntry* aFontEntry, uint32_t aTag)
- {
- mBlob = aFontEntry->GetFontTable(aTag);
- }
- ~AutoTable() {
- if (mBlob) {
- hb_blob_destroy(mBlob);
- }
- }
- operator hb_blob_t*() const { return mBlob; }
- private:
- hb_blob_t* mBlob;
- // not implemented:
- AutoTable(const AutoTable&) MOZ_DELETE;
- AutoTable& operator=(const AutoTable&) MOZ_DELETE;
- };
-
- already_AddRefed<gfxFont> FindOrMakeFont(const gfxFontStyle *aStyle,
- bool aNeedsBold);
-
- // Get an existing font table cache entry in aBlob if it has been
- // registered, or return false if not. Callers must call
- // hb_blob_destroy on aBlob if true is returned.
- //
- // Note that some gfxFont implementations may not call this at all,
- // if it is more efficient to get the table from the OS at that level.
- bool GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob);
-
- // Elements of aTable are transferred (not copied) to and returned in a
- // new hb_blob_t which is registered on the gfxFontEntry, but the initial
- // reference is owned by the caller. Removing the last reference
- // unregisters the table from the font entry.
- //
- // Pass nullptr for aBuffer to indicate that the table is not present and
- // nullptr will be returned. Also returns nullptr on OOM.
- hb_blob_t *ShareFontTableAndGetBlob(uint32_t aTag,
- FallibleTArray<uint8_t>* aTable);
-
- // Get the font's unitsPerEm from the 'head' table, in the case of an
- // sfnt resource. Will return kInvalidUPEM for non-sfnt fonts,
- // if present on the platform.
- uint16_t UnitsPerEm();
- enum {
- kMinUPEM = 16, // Limits on valid unitsPerEm range, from the
- kMaxUPEM = 16384, // OpenType spec
- kInvalidUPEM = uint16_t(-1)
- };
-
- // Shaper face accessors:
- // NOTE that harfbuzz and graphite handle ownership/lifetime of the face
- // object in completely different ways.
-
- // Get HarfBuzz face corresponding to this font file.
- // Caller must release with hb_face_destroy() when finished with it,
- // and the font entry will be notified via ForgetHBFace.
- hb_face_t* GetHBFace();
- virtual void ForgetHBFace();
-
- // Get Graphite face corresponding to this font file.
- // Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
- gr_face* GetGrFace();
- virtual void ReleaseGrFace(gr_face* aFace);
-
- // Release any SVG-glyphs document this font may have loaded.
- void DisconnectSVG();
-
- // Called to notify that aFont is being destroyed. Needed when we're tracking
- // the fonts belonging to this font entry.
- void NotifyFontDestroyed(gfxFont* aFont);
-
- // For memory reporting
- virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const;
- virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const;
-
- // Used when checking for complex script support, to mask off cmap ranges
- struct ScriptRange {
- uint32_t rangeStart;
- uint32_t rangeEnd;
- hb_tag_t tags[3]; // one or two OpenType script tags to check,
- // plus a NULL terminator
- };
-
- bool SupportsScriptInGSUB(const hb_tag_t* aScriptTags);
-
- nsString mName;
- nsString mFamilyName;
-
- bool mItalic : 1;
- bool mFixedPitch : 1;
- bool mIsValid : 1;
- bool mIsBadUnderlineFont : 1;
- bool mIsUserFontContainer : 1; // userfont entry
- bool mIsDataUserFont : 1; // platform font entry (data)
- bool mIsLocalUserFont : 1; // platform font entry (local)
- bool mStandardFace : 1;
- bool mSymbolFont : 1;
- bool mIgnoreGDEF : 1;
- bool mIgnoreGSUB : 1;
- bool mSVGInitialized : 1;
- bool mMathInitialized : 1;
- bool mHasSpaceFeaturesInitialized : 1;
- bool mHasSpaceFeatures : 1;
- bool mHasSpaceFeaturesKerning : 1;
- bool mHasSpaceFeaturesNonKerning : 1;
- bool mSkipDefaultFeatureSpaceCheck : 1;
- bool mHasGraphiteTables : 1;
- bool mCheckedForGraphiteTables : 1;
- bool mHasCmapTable : 1;
- bool mGrFaceInitialized : 1;
- bool mCheckedForColorGlyph : 1;
-
- // bitvector of substitution space features per script, one each
- // for default and non-default features
- uint32_t mDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32];
- uint32_t mNonDefaultSubSpaceFeatures[(MOZ_NUM_SCRIPT_CODES + 31) / 32];
-
- uint16_t mWeight;
- int16_t mStretch;
-
- nsRefPtr<gfxCharacterMap> mCharacterMap;
- uint32_t mUVSOffset;
- nsAutoArrayPtr<uint8_t> mUVSData;
- nsAutoPtr<gfxUserFontData> mUserFontData;
- nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
- // list of gfxFonts that are using SVG glyphs
- nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
- nsAutoPtr<gfxMathTable> mMathTable;
- nsTArray<gfxFontFeature> mFeatureSettings;
- nsAutoPtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
- nsAutoPtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
- uint32_t mLanguageOverride;
-
- // Color Layer font support
- hb_blob_t* mCOLR;
- hb_blob_t* mCPAL;
-
-protected:
- friend class gfxPlatformFontList;
- friend class gfxMacPlatformFontList;
- friend class gfxUserFcFontEntry;
- friend class gfxFontFamily;
- friend class gfxSingleFaceMacFontFamily;
-
- gfxFontEntry();
-
- // Protected destructor, to discourage deletion outside of Release():
- virtual ~gfxFontEntry();
-
- virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
- NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
- return nullptr;
- }
-
- virtual void CheckForGraphiteTables();
-
- // Copy a font table into aBuffer.
- // The caller will be responsible for ownership of the data.
- virtual nsresult CopyFontTable(uint32_t aTableTag,
- FallibleTArray<uint8_t>& aBuffer) {
- NS_NOTREACHED("forgot to override either GetFontTable or CopyFontTable?");
- return NS_ERROR_FAILURE;
- }
-
- // Return a blob that wraps a table found within a buffer of font data.
- // The blob does NOT own its data; caller guarantees that the buffer
- // will remain valid at least as long as the blob.
- // Returns null if the specified table is not found.
- // This method assumes aFontData is valid 'sfnt' data; before using this,
- // caller is responsible to do any sanitization/validation necessary.
- hb_blob_t* GetTableFromFontData(const void* aFontData, uint32_t aTableTag);
-
- // lookup the cmap in cached font data
- virtual already_AddRefed<gfxCharacterMap>
- GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
- uint32_t& aUVSOffset,
- bool& aSymbolFont);
-
- // Font's unitsPerEm from the 'head' table, if available (will be set to
- // kInvalidUPEM for non-sfnt font formats)
- uint16_t mUnitsPerEm;
-
- // Shaper-specific face objects, shared by all instantiations of the same
- // physical font, regardless of size.
- // Usually, only one of these will actually be created for any given font
- // entry, depending on the font tables that are present.
-
- // hb_face_t is refcounted internally, so each shaper that's using it will
- // bump the ref count when it acquires the face, and "destroy" (release) it
- // in its destructor. The font entry has only this non-owning reference to
- // the face; when the face is deleted, it will tell the font entry to forget
- // it, so that a new face will be created next time it is needed.
- hb_face_t* mHBFace;
-
- static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData);
-
- // Callback that the hb_face will use to tell us when it is being deleted.
- static void HBFaceDeletedCallback(void *aUserData);
-
- // gr_face is -not- refcounted, so it will be owned directly by the font
- // entry, and we'll keep a count of how many references we've handed out;
- // each shaper is responsible to call ReleaseGrFace on its entry when
- // finished with it, so that we know when it can be deleted.
- gr_face* mGrFace;
-
- // hashtable to map raw table data ptr back to its owning blob, for use by
- // graphite table-release callback
- nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap;
-
- // number of current users of this entry's mGrFace
- nsrefcnt mGrFaceRefCnt;
-
- static const void* GrGetTable(const void *aAppFaceHandle,
- unsigned int aName,
- size_t *aLen);
- static void GrReleaseTable(const void *aAppFaceHandle,
- const void *aTableBuffer);
-
-private:
- /**
- * Font table hashtable, to support GetFontTable for harfbuzz.
- *
- * The harfbuzz shaper (and potentially other clients) needs access to raw
- * font table data. This needs to be cached so that it can be used
- * repeatedly (each time we construct a text run; in some cases, for
- * each character/glyph within the run) without re-fetching large tables
- * every time.
- *
- * Because we may instantiate many gfxFonts for the same physical font
- * file (at different sizes), we should ensure that they can share a
- * single cached copy of the font tables. To do this, we implement table
- * access and sharing on the fontEntry rather than the font itself.
- *
- * The default implementation uses GetFontTable() to read font table
- * data into byte arrays, and wraps them in blobs which are registered in
- * a hashtable. The hashtable can then return pre-existing blobs to
- * harfbuzz.
- *
- * Harfbuzz will "destroy" the blobs when it is finished with them. When
- * the last blob reference is removed, the FontTableBlobData user data
- * will remove the blob from the hashtable if still registered.
- */
-
- class FontTableBlobData;
-
- /**
- * FontTableHashEntry manages the entries of hb_blob_t's containing font
- * table data.
- *
- * This is used to share font tables across fonts with the same
- * font entry (but different sizes) for use by HarfBuzz. The hashtable
- * does not own a strong reference to the blob, but keeps a weak pointer,
- * managed by FontTableBlobData. Similarly FontTableBlobData keeps only a
- * weak pointer to the hashtable, managed by FontTableHashEntry.
- */
-
- class FontTableHashEntry : public nsUint32HashKey
- {
- public:
- // Declarations for nsTHashtable
-
- typedef nsUint32HashKey KeyClass;
- typedef KeyClass::KeyType KeyType;
- typedef KeyClass::KeyTypePointer KeyTypePointer;
-
- explicit FontTableHashEntry(KeyTypePointer aTag)
- : KeyClass(aTag)
- , mSharedBlobData(nullptr)
- , mBlob(nullptr)
- { }
-
- // NOTE: This assumes the new entry belongs to the same hashtable as
- // the old, because the mHashtable pointer in mSharedBlobData (if
- // present) will not be updated.
- FontTableHashEntry(FontTableHashEntry&& toMove)
- : KeyClass(mozilla::Move(toMove))
- , mSharedBlobData(mozilla::Move(toMove.mSharedBlobData))
- , mBlob(mozilla::Move(toMove.mBlob))
- {
- toMove.mSharedBlobData = nullptr;
- toMove.mBlob = nullptr;
- }
-
- ~FontTableHashEntry() { Clear(); }
-
- // FontTable/Blob API
-
- // Transfer (not copy) elements of aTable to a new hb_blob_t and
- // return ownership to the caller. A weak reference to the blob is
- // recorded in the hashtable entry so that others may use the same
- // table.
- hb_blob_t *
- ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
- nsTHashtable<FontTableHashEntry> *aHashtable);
-
- // Return a strong reference to the blob.
- // Callers must hb_blob_destroy the returned blob.
- hb_blob_t *GetBlob() const;
-
- void Clear();
-
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
- private:
- static void DeleteFontTableBlobData(void *aBlobData);
- // not implemented
- FontTableHashEntry& operator=(FontTableHashEntry& toCopy);
-
- FontTableBlobData *mSharedBlobData;
- hb_blob_t *mBlob;
- };
-
- nsAutoPtr<nsTHashtable<FontTableHashEntry> > mFontTableCache;
-
- gfxFontEntry(const gfxFontEntry&);
- gfxFontEntry& operator=(const gfxFontEntry&);
-};
-
-
-// used when iterating over all fonts looking for a match for a given character
-struct GlobalFontMatch {
- GlobalFontMatch(const uint32_t aCharacter,
- int32_t aRunScript,
- const gfxFontStyle *aStyle) :
- mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle),
- mMatchRank(0), mCount(0), mCmapsTested(0)
- {
-
- }
-
- const uint32_t mCh; // codepoint to be matched
- int32_t mRunScript; // Unicode script for the codepoint
- const gfxFontStyle* mStyle; // style to match
- int32_t mMatchRank; // metric indicating closest match
- nsRefPtr<gfxFontEntry> mBestMatch; // current best match
- nsRefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
- uint32_t mCount; // number of fonts matched
- uint32_t mCmapsTested; // number of cmaps tested
-};
-
-class gfxFontFamily {
-public:
- NS_INLINE_DECL_REFCOUNTING(gfxFontFamily)
-
- explicit gfxFontFamily(const nsAString& aName) :
- mName(aName),
- mOtherFamilyNamesInitialized(false),
- mHasOtherFamilyNames(false),
- mFaceNamesInitialized(false),
- mHasStyles(false),
- mIsSimpleFamily(false),
- mIsBadUnderlineFamily(false),
- mFamilyCharacterMapInitialized(false),
- mSkipDefaultFeatureSpaceCheck(false)
- { }
-
- const nsString& Name() { return mName; }
-
- virtual void LocalizedName(nsAString& aLocalizedName);
- virtual bool HasOtherFamilyNames();
-
- nsTArray<nsRefPtr<gfxFontEntry> >& GetFontList() { return mAvailableFonts; }
-
- void AddFontEntry(nsRefPtr<gfxFontEntry> aFontEntry) {
- // bug 589682 - set the IgnoreGDEF flag on entries for Italic faces
- // of Times New Roman, because of buggy table in those fonts
- if (aFontEntry->IsItalic() && !aFontEntry->IsUserFont() &&
- Name().EqualsLiteral("Times New Roman"))
- {
- aFontEntry->mIgnoreGDEF = true;
- }
- if (aFontEntry->mFamilyName.IsEmpty()) {
- aFontEntry->mFamilyName = Name();
- } else {
- MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name()));
- }
- aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck;
- mAvailableFonts.AppendElement(aFontEntry);
- }
-
- // note that the styles for this family have been added
- bool HasStyles() { return mHasStyles; }
- void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
-
- // choose a specific face to match a style using CSS font matching
- // rules (weight matching occurs here). may return a face that doesn't
- // precisely match (e.g. normal face when no italic face exists).
- // aNeedsSyntheticBold is set to true when synthetic bolding is
- // needed, false otherwise
- gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle,
- bool& aNeedsSyntheticBold);
-
- // checks for a matching font within the family
- // used as part of the font fallback process
- void FindFontForChar(GlobalFontMatch *aMatchData);
-
- // checks all fonts for a matching font within the family
- void SearchAllFontsForChar(GlobalFontMatch *aMatchData);
-
- // read in other family names, if any, and use functor to add each into cache
- virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
-
- // helper method for reading localized family names from the name table
- // of a single face
- static void ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
- const char *aNameData,
- uint32_t aDataLength,
- nsTArray<nsString>& aOtherFamilyNames,
- bool useFullName);
-
- // set when other family names have been read in
- void SetOtherFamilyNamesInitialized() {
- mOtherFamilyNamesInitialized = true;
- }
-
- // read in other localized family names, fullnames and Postscript names
- // for all faces and append to lookup tables
- virtual void ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
- bool aNeedFullnamePostscriptNames,
- FontInfoData *aFontInfoData = nullptr);
-
- // find faces belonging to this family (platform implementations override this;
- // should be made pure virtual once all subclasses have been updated)
- virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr) { }
-
- // search for a specific face using the Postscript name
- gfxFontEntry* FindFont(const nsAString& aPostscriptName);
-
- // read in cmaps for all the faces
- void ReadAllCMAPs(FontInfoData *aFontInfoData = nullptr);
-
- bool TestCharacterMap(uint32_t aCh) {
- if (!mFamilyCharacterMapInitialized) {
- ReadAllCMAPs();
- }
- return mFamilyCharacterMap.test(aCh);
- }
-
- void ResetCharacterMap() {
- mFamilyCharacterMap.reset();
- mFamilyCharacterMapInitialized = false;
- }
-
- // mark this family as being in the "bad" underline offset blacklist
- void SetBadUnderlineFamily() {
- mIsBadUnderlineFamily = true;
- if (mHasStyles) {
- SetBadUnderlineFonts();
- }
- }
-
- bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
-
- // sort available fonts to put preferred (standard) faces towards the end
- void SortAvailableFonts();
-
- // check whether the family fits into the simple 4-face model,
- // so we can use simplified style-matching;
- // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
- void CheckForSimpleFamily();
-
- // For memory reporter
- virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const;
- virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
- FontListSizes* aSizes) const;
-
-#ifdef DEBUG
- // Only used for debugging checks - does a linear search
- bool ContainsFace(gfxFontEntry* aFontEntry);
-#endif
-
- void SetSkipSpaceFeatureCheck(bool aSkipCheck) {
- mSkipDefaultFeatureSpaceCheck = aSkipCheck;
- }
-
-protected:
- // Protected destructor, to discourage deletion outside of Release():
- virtual ~gfxFontFamily()
- {
- }
-
- // fills in an array with weights of faces that match style,
- // returns whether any matching entries found
- virtual bool FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
- bool anItalic, int16_t aStretch);
-
- bool ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
- hb_blob_t *aNameTable,
- bool useFullName = false);
-
- // set whether this font family is in "bad" underline offset blacklist.
- void SetBadUnderlineFonts() {
- uint32_t i, numFonts = mAvailableFonts.Length();
- for (i = 0; i < numFonts; i++) {
- if (mAvailableFonts[i]) {
- mAvailableFonts[i]->mIsBadUnderlineFont = true;
- }
- }
- }
-
- nsString mName;
- nsTArray<nsRefPtr<gfxFontEntry> > mAvailableFonts;
- gfxSparseBitSet mFamilyCharacterMap;
- bool mOtherFamilyNamesInitialized : 1;
- bool mHasOtherFamilyNames : 1;
- bool mFaceNamesInitialized : 1;
- bool mHasStyles : 1;
- bool mIsSimpleFamily : 1;
- bool mIsBadUnderlineFamily : 1;
- bool mFamilyCharacterMapInitialized : 1;
- bool mSkipDefaultFeatureSpaceCheck : 1;
-
- enum {
- // for "simple" families, the faces are stored in mAvailableFonts
- // with fixed positions:
- kRegularFaceIndex = 0,
- kBoldFaceIndex = 1,
- kItalicFaceIndex = 2,
- kBoldItalicFaceIndex = 3,
- // mask values for selecting face with bold and/or italic attributes
- kBoldMask = 0x01,
- kItalicMask = 0x02
- };
-};
-
struct gfxTextRange {
enum {
// flags for recording the kind of font-matching that was used
kFontGroup = 0x0001,
kPrefsFallback = 0x0002,
kSystemFallback = 0x0004
};
gfxTextRange(uint32_t aStart, uint32_t aEnd,
@@ -1335,141 +543,16 @@ public:
};
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~gfxTextRunFactory() {}
};
/**
- * This stores glyph bounds information for a particular gfxFont, at
- * a particular appunits-per-dev-pixel ratio (because the compressed glyph
- * width array is stored in appunits).
- *
- * We store a hashtable from glyph IDs to float bounding rects. For the
- * common case where the glyph has no horizontal left bearing, and no
- * y overflow above the font ascent or below the font descent, and tight
- * bounding boxes are not required, we avoid storing the glyph ID in the hashtable
- * and instead consult an array of 16-bit glyph XMost values (in appunits).
- * This array always has an entry for the font's space glyph --- the width is
- * assumed to be zero.
- */
-class gfxGlyphExtents {
-public:
- explicit gfxGlyphExtents(int32_t aAppUnitsPerDevUnit) :
- mAppUnitsPerDevUnit(aAppUnitsPerDevUnit) {
- MOZ_COUNT_CTOR(gfxGlyphExtents);
- }
- ~gfxGlyphExtents();
-
- enum { INVALID_WIDTH = 0xFFFF };
-
- void NotifyGlyphsChanged() {
- mTightGlyphExtents.Clear();
- }
-
- // returns INVALID_WIDTH => not a contained glyph
- // Otherwise the glyph has no before-bearing or vertical bearings,
- // and the result is its width measured from the baseline origin, in
- // appunits.
- uint16_t GetContainedGlyphWidthAppUnits(uint32_t aGlyphID) const {
- return mContainedGlyphWidths.Get(aGlyphID);
- }
-
- bool IsGlyphKnown(uint32_t aGlyphID) const {
- return mContainedGlyphWidths.Get(aGlyphID) != INVALID_WIDTH ||
- mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
- }
-
- bool IsGlyphKnownWithTightExtents(uint32_t aGlyphID) const {
- return mTightGlyphExtents.GetEntry(aGlyphID) != nullptr;
- }
-
- // Get glyph extents; a rectangle relative to the left baseline origin
- // Returns true on success. Can fail on OOM or when aContext is null
- // and extents were not (successfully) prefetched.
- bool GetTightGlyphExtentsAppUnits(gfxFont *aFont, gfxContext *aContext,
- uint32_t aGlyphID, gfxRect *aExtents);
-
- void SetContainedGlyphWidthAppUnits(uint32_t aGlyphID, uint16_t aWidth) {
- mContainedGlyphWidths.Set(aGlyphID, aWidth);
- }
- void SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits);
-
- int32_t GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
-
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
- size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-private:
- class HashEntry : public nsUint32HashKey {
- public:
- // When constructing a new entry in the hashtable, we'll leave this
- // blank. The caller of Put() will fill this in.
- explicit HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {}
- HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) {
- x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height;
- }
-
- float x, y, width, height;
- };
-
- enum { BLOCK_SIZE_BITS = 7, BLOCK_SIZE = 1 << BLOCK_SIZE_BITS }; // 128-glyph blocks
-
- class GlyphWidths {
- public:
- void Set(uint32_t aIndex, uint16_t aValue);
- uint16_t Get(uint32_t aIndex) const {
- uint32_t block = aIndex >> BLOCK_SIZE_BITS;
- if (block >= mBlocks.Length())
- return INVALID_WIDTH;
- uintptr_t bits = mBlocks[block];
- if (!bits)
- return INVALID_WIDTH;
- uint32_t indexInBlock = aIndex & (BLOCK_SIZE - 1);
- if (bits & 0x1) {
- if (GetGlyphOffset(bits) != indexInBlock)
- return INVALID_WIDTH;
- return GetWidth(bits);
- }
- uint16_t *widths = reinterpret_cast<uint16_t *>(bits);
- return widths[indexInBlock];
- }
-
- uint32_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
- ~GlyphWidths();
-
- private:
- static uint32_t GetGlyphOffset(uintptr_t aBits) {
- NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
- return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1);
- }
- static uint32_t GetWidth(uintptr_t aBits) {
- NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
- return aBits >> (1 + BLOCK_SIZE_BITS);
- }
- static uintptr_t MakeSingle(uint32_t aGlyphOffset, uint16_t aWidth) {
- return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1;
- }
-
- nsTArray<uintptr_t> mBlocks;
- };
-
- GlyphWidths mContainedGlyphWidths;
- nsTHashtable<HashEntry> mTightGlyphExtents;
- int32_t mAppUnitsPerDevUnit;
-
-private:
- // not implemented:
- gfxGlyphExtents(const gfxGlyphExtents& aOther) MOZ_DELETE;
- gfxGlyphExtents& operator=(const gfxGlyphExtents& aOther) MOZ_DELETE;
-};
-
-/**
* gfxFontShaper
*
* This class implements text shaping (character to glyph mapping and
* glyph layout). There is a gfxFontShaper subclass for each text layout
* technology (uniscribe, core text, harfbuzz,....) we support.
*
* The shaper is responsible for setting up glyph data in gfxTextRuns.
*
@@ -1514,16 +597,631 @@ public:
nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures);
protected:
// the font this shaper is working with
gfxFont * mFont;
};
+/*
+ * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun.
+ * These are objects that store a list of zero or more glyphs for each character.
+ * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets.
+ * The idea is that a string is rendered by a loop that draws each glyph
+ * at its designated offset from the current point, then advances the current
+ * point by the glyph's advance in the direction of the textrun (LTR or RTL).
+ * Each glyph advance is always rounded to the nearest appunit; this ensures
+ * consistent results when dividing the text in a textrun into multiple text
+ * frames (frame boundaries are always aligned to appunits). We optimize
+ * for the case where a character has a single glyph and zero xoffset and yoffset,
+ * and the glyph ID and advance are in a reasonable range so we can pack all
+ * necessary data into 32 bits.
+ *
+ * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont)
+ * or directly into a gfxTextRun (for cases where we want to shape textruns in
+ * their entirety rather than using cached words, because there may be layout
+ * features that depend on the inter-word spaces).
+ */
+class gfxShapedText
+{
+public:
+ gfxShapedText(uint32_t aLength, uint32_t aFlags,
+ int32_t aAppUnitsPerDevUnit)
+ : mLength(aLength)
+ , mFlags(aFlags)
+ , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
+ { }
+
+ virtual ~gfxShapedText() { }
+
+ /**
+ * This class records the information associated with a character in the
+ * input string. It's optimized for the case where there is one glyph
+ * representing that character alone.
+ *
+ * A character can have zero or more associated glyphs. Each glyph
+ * has an advance width and an x and y offset.
+ * A character may be the start of a cluster.
+ * A character may be the start of a ligature group.
+ * A character can be "missing", indicating that the system is unable
+ * to render the character.
+ *
+ * All characters in a ligature group conceptually share all the glyphs
+ * associated with the characters in a group.
+ */
+ class CompressedGlyph {
+ public:
+ CompressedGlyph() { mValue = 0; }
+
+ enum {
+ // Indicates that a cluster and ligature group starts at this
+ // character; this character has a single glyph with a reasonable
+ // advance and zero offsets. A "reasonable" advance
+ // is one that fits in the available bits (currently 12) (specified
+ // in appunits).
+ FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
+
+ // Indicates whether a linebreak is allowed before this character;
+ // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value
+ // indicating the kind of linebreak (if any) allowed here.
+ FLAGS_CAN_BREAK_BEFORE = 0x60000000U,
+
+ FLAGS_CAN_BREAK_SHIFT = 29,
+ FLAG_BREAK_TYPE_NONE = 0,
+ FLAG_BREAK_TYPE_NORMAL = 1,
+ FLAG_BREAK_TYPE_HYPHEN = 2,
+
+ FLAG_CHAR_IS_SPACE = 0x10000000U,
+
+ // The advance is stored in appunits
+ ADVANCE_MASK = 0x0FFF0000U,
+ ADVANCE_SHIFT = 16,
+
+ GLYPH_MASK = 0x0000FFFFU,
+
+ // Non-simple glyphs may or may not have glyph data in the
+ // corresponding mDetailedGlyphs entry. They have the following
+ // flag bits:
+
+ // When NOT set, indicates that this character corresponds to a
+ // missing glyph and should be skipped (or possibly, render the character
+ // Unicode value in some special way). If there are glyphs,
+ // the mGlyphID is actually the UTF16 character code. The bit is
+ // inverted so we can memset the array to zero to indicate all missing.
+ FLAG_NOT_MISSING = 0x01,
+ FLAG_NOT_CLUSTER_START = 0x02,
+ FLAG_NOT_LIGATURE_GROUP_START = 0x04,
+
+ FLAG_CHAR_IS_TAB = 0x08,
+ FLAG_CHAR_IS_NEWLINE = 0x10,
+ FLAG_CHAR_IS_LOW_SURROGATE = 0x20,
+ CHAR_IDENTITY_FLAGS_MASK = 0x38,
+
+ GLYPH_COUNT_MASK = 0x00FFFF00U,
+ GLYPH_COUNT_SHIFT = 8
+ };
+
+ // "Simple glyphs" have a simple glyph ID, simple advance and their
+ // x and y offsets are zero. Also the glyph extents do not overflow
+ // the font-box defined by the font ascent, descent and glyph advance width.
+ // These case is optimized to avoid storing DetailedGlyphs.
+
+ // Returns true if the glyph ID aGlyph fits into the compressed representation
+ static bool IsSimpleGlyphID(uint32_t aGlyph) {
+ return (aGlyph & GLYPH_MASK) == aGlyph;
+ }
+ // Returns true if the advance aAdvance fits into the compressed representation.
+ // aAdvance is in appunits.
+ static bool IsSimpleAdvance(uint32_t aAdvance) {
+ return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
+ }
+
+ bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
+ uint32_t GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
+ uint32_t GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
+
+ bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; }
+ bool IsClusterStart() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START);
+ }
+ bool IsLigatureGroupStart() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
+ }
+ bool IsLigatureContinuation() const {
+ return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 &&
+ (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) ==
+ (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING);
+ }
+
+ // Return true if the original character was a normal (breakable,
+ // trimmable) space (U+0020). Not true for other characters that
+ // may happen to map to the space glyph (U+00A0).
+ bool CharIsSpace() const {
+ return (mValue & FLAG_CHAR_IS_SPACE) != 0;
+ }
+
+ bool CharIsTab() const {
+ return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0;
+ }
+ bool CharIsNewline() const {
+ return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0;
+ }
+ bool CharIsLowSurrogate() const {
+ return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_LOW_SURROGATE) != 0;
+ }
+
+ uint32_t CharIdentityFlags() const {
+ return IsSimpleGlyph() ? 0 : (mValue & CHAR_IDENTITY_FLAGS_MASK);
+ }
+
+ void SetClusterStart(bool aIsClusterStart) {
+ NS_ASSERTION(!IsSimpleGlyph(),
+ "can't call SetClusterStart on simple glyphs");
+ if (aIsClusterStart) {
+ mValue &= ~FLAG_NOT_CLUSTER_START;
+ } else {
+ mValue |= FLAG_NOT_CLUSTER_START;
+ }
+ }
+
+ uint8_t CanBreakBefore() const {
+ return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT;
+ }
+ // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
+ uint32_t SetCanBreakBefore(uint8_t aCanBreakBefore) {
+ NS_ASSERTION(aCanBreakBefore <= 2,
+ "Bogus break-before value!");
+ uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
+ uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
+ mValue ^= toggle;
+ return toggle;
+ }
+
+ CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
+ NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
+ NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
+ NS_ASSERTION(!CharIdentityFlags(), "Char identity flags lost");
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
+ FLAG_IS_SIMPLE_GLYPH |
+ (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
+ return *this;
+ }
+ CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
+ uint32_t aGlyphCount) {
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
+ FLAG_NOT_MISSING |
+ CharIdentityFlags() |
+ (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
+ (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
+ (aGlyphCount << GLYPH_COUNT_SHIFT);
+ return *this;
+ }
+ /**
+ * Missing glyphs are treated as ligature group starts; don't mess with
+ * the cluster-start flag (see bugs 618870 and 619286).
+ */
+ CompressedGlyph& SetMissing(uint32_t aGlyphCount) {
+ mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START |
+ FLAG_CHAR_IS_SPACE)) |
+ CharIdentityFlags() |
+ (aGlyphCount << GLYPH_COUNT_SHIFT);
+ return *this;
+ }
+ uint32_t GetGlyphCount() const {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
+ }
+
+ void SetIsSpace() {
+ mValue |= FLAG_CHAR_IS_SPACE;
+ }
+ void SetIsTab() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_IS_TAB;
+ }
+ void SetIsNewline() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_IS_NEWLINE;
+ }
+ void SetIsLowSurrogate() {
+ NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+ mValue |= FLAG_CHAR_IS_LOW_SURROGATE;
+ }
+
+ private:
+ uint32_t mValue;
+ };
+
+ // Accessor for the array of CompressedGlyph records, which will be in
+ // a different place in gfxShapedWord vs gfxTextRun
+ virtual CompressedGlyph *GetCharacterGlyphs() = 0;
+
+ /**
+ * When the glyphs for a character don't fit into a CompressedGlyph record
+ * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
+ */
+ struct DetailedGlyph {
+ /** The glyphID, or the Unicode character
+ * if this is a missing glyph */
+ uint32_t mGlyphID;
+ /** The advance, x-offset and y-offset of the glyph, in appunits
+ * mAdvance is in the text direction (RTL or LTR)
+ * mXOffset is always from left to right
+ * mYOffset is always from top to bottom */
+ int32_t mAdvance;
+ float mXOffset, mYOffset;
+ };
+
+ void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
+ const DetailedGlyph *aGlyphs);
+
+ void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
+
+ void SetIsSpace(uint32_t aIndex) {
+ GetCharacterGlyphs()[aIndex].SetIsSpace();
+ }
+
+ void SetIsLowSurrogate(uint32_t aIndex) {
+ SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
+ GetCharacterGlyphs()[aIndex].SetIsLowSurrogate();
+ }
+
+ bool HasDetailedGlyphs() const {
+ return mDetailedGlyphs != nullptr;
+ }
+
+ bool IsLigatureGroupStart(uint32_t aPos) {
+ NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+ return GetCharacterGlyphs()[aPos].IsLigatureGroupStart();
+ }
+
+ // NOTE that this must not be called for a character offset that does
+ // not have any DetailedGlyph records; callers must have verified that
+ // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero.
+ DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
+ NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() &&
+ !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() &&
+ GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0,
+ "invalid use of GetDetailedGlyphs; check the caller!");
+ return mDetailedGlyphs->Get(aCharIndex);
+ }
+
+ void AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
+ uint32_t aOffset, uint32_t aLength);
+
+ // Mark clusters in the CompressedGlyph records, starting at aOffset,
+ // based on the Unicode properties of the text in aString.
+ // This is also responsible to set the IsSpace flag for space characters.
+ void SetupClusterBoundaries(uint32_t aOffset,
+ const char16_t *aString,
+ uint32_t aLength);
+ // In 8-bit text, there won't actually be any clusters, but we still need
+ // the space-marking functionality.
+ void SetupClusterBoundaries(uint32_t aOffset,
+ const uint8_t *aString,
+ uint32_t aLength);
+
+ uint32_t GetFlags() const {
+ return mFlags;
+ }
+
+ bool IsRightToLeft() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
+ }
+
+ gfxFloat GetDirection() const {
+ return IsRightToLeft() ? -1.0f : 1.0f;
+ }
+
+ bool DisableLigatures() const {
+ return (GetFlags() &
+ gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
+ }
+
+ bool TextIs8Bit() const {
+ return (GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
+ }
+
+ int32_t GetAppUnitsPerDevUnit() const {
+ return mAppUnitsPerDevUnit;
+ }
+
+ uint32_t GetLength() const {
+ return mLength;
+ }
+
+ bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
+
+protected:
+ // Allocate aCount DetailedGlyphs for the given index
+ DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
+ uint32_t aCount);
+
+ // For characters whose glyph data does not fit the "simple" glyph criteria
+ // in CompressedGlyph, we use a sorted array to store the association
+ // between the source character offset and an index into an array
+ // DetailedGlyphs. The CompressedGlyph record includes a count of
+ // the number of DetailedGlyph records that belong to the character,
+ // starting at the given index.
+ class DetailedGlyphStore {
+ public:
+ DetailedGlyphStore()
+ : mLastUsed(0)
+ { }
+
+ // This is optimized for the most common calling patterns:
+ // we rarely need random access to the records, access is most commonly
+ // sequential through the textRun, so we record the last-used index
+ // and check whether the caller wants the same record again, or the
+ // next; if not, it's most likely we're starting over from the start
+ // of the run, so we check the first entry before resorting to binary
+ // search as a last resort.
+ // NOTE that this must not be called for a character offset that does
+ // not have any DetailedGlyph records; callers must have verified that
+ // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero
+ // before calling this, otherwise the assertions here will fire (in a
+ // debug build), and we'll probably crash.
+ DetailedGlyph* Get(uint32_t aOffset) {
+ NS_ASSERTION(mOffsetToIndex.Length() > 0,
+ "no detailed glyph records!");
+ DetailedGlyph* details = mDetails.Elements();
+ // check common cases (fwd iteration, initial entry, etc) first
+ if (mLastUsed < mOffsetToIndex.Length() - 1 &&
+ aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) {
+ ++mLastUsed;
+ } else if (aOffset == mOffsetToIndex[0].mOffset) {
+ mLastUsed = 0;
+ } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) {
+ // do nothing
+ } else if (mLastUsed > 0 &&
+ aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) {
+ --mLastUsed;
+ } else {
+ mLastUsed =
+ mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset());
+ }
+ NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex,
+ "detailed glyph record missing!");
+ return details + mOffsetToIndex[mLastUsed].mIndex;
+ }
+
+ DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) {
+ uint32_t detailIndex = mDetails.Length();
+ DetailedGlyph *details = mDetails.AppendElements(aCount);
+ // We normally set up glyph records sequentially, so the common case
+ // here is to append new records to the mOffsetToIndex array;
+ // test for that before falling back to the InsertElementSorted
+ // method.
+ if (mOffsetToIndex.Length() == 0 ||
+ aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
+ mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex));
+ } else {
+ mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
+ CompareRecordOffsets());
+ }
+ return details;
+ }
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
+ return aMallocSizeOf(this) +
+ mDetails.SizeOfExcludingThis(aMallocSizeOf) +
+ mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ private:
+ struct DGRec {
+ DGRec(const uint32_t& aOffset, const uint32_t& aIndex)
+ : mOffset(aOffset), mIndex(aIndex) { }
+ uint32_t mOffset; // source character offset in the textrun
+ uint32_t mIndex; // index where this char's DetailedGlyphs begin
+ };
+
+ struct CompareToOffset {
+ bool Equals(const DGRec& a, const uint32_t& b) const {
+ return a.mOffset == b;
+ }
+ bool LessThan(const DGRec& a, const uint32_t& b) const {
+ return a.mOffset < b;
+ }
+ };
+
+ struct CompareRecordOffsets {
+ bool Equals(const DGRec& a, const DGRec& b) const {
+ return a.mOffset == b.mOffset;
+ }
+ bool LessThan(const DGRec& a, const DGRec& b) const {
+ return a.mOffset < b.mOffset;
+ }
+ };
+
+ // Concatenated array of all the DetailedGlyph records needed for the
+ // textRun; individual character offsets are associated with indexes
+ // into this array via the mOffsetToIndex table.
+ nsTArray<DetailedGlyph> mDetails;
+
+ // For each character offset that needs DetailedGlyphs, we record the
+ // index in mDetails where the list of glyphs begins. This array is
+ // sorted by mOffset.
+ nsTArray<DGRec> mOffsetToIndex;
+
+ // Records the most recently used index into mOffsetToIndex, so that
+ // we can support sequential access more quickly than just doing
+ // a binary search each time.
+ nsTArray<DGRec>::index_type mLastUsed;
+ };
+
+ nsAutoPtr<DetailedGlyphStore> mDetailedGlyphs;
+
+ // Number of char16_t characters and CompressedGlyph glyph records
+ uint32_t mLength;
+
+ // Shaping flags (direction, ligature-suppression)
+ uint32_t mFlags;
+
+ int32_t mAppUnitsPerDevUnit;
+};
+
+/*
+ * gfxShapedWord: an individual (space-delimited) run of text shaped with a
+ * particular font, without regard to external context.
+ *
+ * The glyph data is copied into gfxTextRuns as needed from the cache of
+ * ShapedWords associated with each gfxFont instance.
+ */
+class gfxShapedWord : public gfxShapedText
+{
+public:
+ // Create a ShapedWord that can hold glyphs for aLength characters,
+ // with mCharacterGlyphs sized appropriately.
+ //
+ // Returns null on allocation failure (does NOT use infallible alloc)
+ // so caller must check for success.
+ //
+ // This does NOT perform shaping, so the returned word contains no
+ // glyph data; the caller must call gfxFont::ShapeText() with appropriate
+ // parameters to set up the glyphs.
+ static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
+ int32_t aRunScript,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags) {
+ NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
+ "excessive length for gfxShapedWord!");
+
+ // Compute size needed including the mCharacterGlyphs array
+ // and a copy of the original text
+ uint32_t size =
+ offsetof(gfxShapedWord, mCharGlyphsStorage) +
+ aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
+ void *storage = moz_malloc(size);
+ if (!storage) {
+ return nullptr;
+ }
+
+ // Construct in the pre-allocated storage, using placement new
+ return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+ aAppUnitsPerDevUnit, aFlags);
+ }
+
+ static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength,
+ int32_t aRunScript,
+ int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags) {
+ NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
+ "excessive length for gfxShapedWord!");
+
+ // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
+ // then we convert the text to an 8-bit version and call the 8-bit
+ // Create function instead.
+ if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+ nsAutoCString narrowText;
+ LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
+ narrowText);
+ return Create((const uint8_t*)(narrowText.BeginReading()),
+ aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
+ }
+
+ uint32_t size =
+ offsetof(gfxShapedWord, mCharGlyphsStorage) +
+ aLength * (sizeof(CompressedGlyph) + sizeof(char16_t));
+ void *storage = moz_malloc(size);
+ if (!storage) {
+ return nullptr;
+ }
+
+ return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+ aAppUnitsPerDevUnit, aFlags);
+ }
+
+ // Override operator delete to properly free the object that was
+ // allocated via moz_malloc.
+ void operator delete(void* p) {
+ moz_free(p);
+ }
+
+ CompressedGlyph *GetCharacterGlyphs() {
+ return &mCharGlyphsStorage[0];
+ }
+
+ const uint8_t* Text8Bit() const {
+ NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
+ return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength());
+ }
+
+ const char16_t* TextUnicode() const {
+ NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
+ return reinterpret_cast<const char16_t*>(mCharGlyphsStorage + GetLength());
+ }
+
+ char16_t GetCharAt(uint32_t aOffset) const {
+ NS_ASSERTION(aOffset < GetLength(), "aOffset out of range");
+ return TextIs8Bit() ?
+ char16_t(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
+ }
+
+ int32_t Script() const {
+ return mScript;
+ }
+
+ void ResetAge() {
+ mAgeCounter = 0;
+ }
+ uint32_t IncrementAge() {
+ return ++mAgeCounter;
+ }
+
+ // Helper used when hashing a word for the shaped-word caches
+ static uint32_t HashMix(uint32_t aHash, char16_t aCh)
+ {
+ return (aHash >> 28) ^ (aHash << 4) ^ aCh;
+ }
+
+private:
+ // so that gfxTextRun can share our DetailedGlyphStore class
+ friend class gfxTextRun;
+
+ // Construct storage for a ShapedWord, ready to receive glyph data
+ gfxShapedWord(const uint8_t *aText, uint32_t aLength,
+ int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags)
+ : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
+ aAppUnitsPerDevUnit)
+ , mScript(aRunScript)
+ , mAgeCounter(0)
+ {
+ memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+ uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
+ memcpy(text, aText, aLength * sizeof(uint8_t));
+ }
+
+ gfxShapedWord(const char16_t *aText, uint32_t aLength,
+ int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
+ uint32_t aFlags)
+ : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
+ , mScript(aRunScript)
+ , mAgeCounter(0)
+ {
+ memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
+ char16_t *text = reinterpret_cast<char16_t*>(&mCharGlyphsStorage[aLength]);
+ memcpy(text, aText, aLength * sizeof(char16_t));
+ SetupClusterBoundaries(0, aText, aLength);
+ }
+
+ int32_t mScript;
+
+ uint32_t mAgeCounter;
+
+ // The mCharGlyphsStorage array is actually a variable-size member;
+ // when the ShapedWord is created, its size will be increased as necessary
+ // to allow the proper number of glyphs to be stored.
+ // The original text, in either 8-bit or 16-bit form, will be stored
+ // immediately following the CompressedGlyphs.
+ CompressedGlyph mCharGlyphsStorage[1];
+};
+
class GlyphBufferAzure;
struct TextRunDrawParams;
struct FontDrawParams;
class gfxFont {
friend class gfxHarfBuzzShaper;
friend class gfxGraphiteShaper;
@@ -2290,1590 +1988,39 @@ protected:
// This helper calculates the scale factor we need to apply to the
// synthetic-bold offset.
static double CalcXScale(gfxContext *aContext);
};
// proportion of ascent used for x-height, if unable to read value from font
#define DEFAULT_XHEIGHT_FACTOR 0.56f
-/*
- * gfxShapedText is an abstract superclass for gfxShapedWord and gfxTextRun.
- * These are objects that store a list of zero or more glyphs for each character.
- * For each glyph we store the glyph ID, the advance, and possibly x/y-offsets.
- * The idea is that a string is rendered by a loop that draws each glyph
- * at its designated offset from the current point, then advances the current
- * point by the glyph's advance in the direction of the textrun (LTR or RTL).
- * Each glyph advance is always rounded to the nearest appunit; this ensures
- * consistent results when dividing the text in a textrun into multiple text
- * frames (frame boundaries are always aligned to appunits). We optimize
- * for the case where a character has a single glyph and zero xoffset and yoffset,
- * and the glyph ID and advance are in a reasonable range so we can pack all
- * necessary data into 32 bits.
- *
- * gfxFontShaper can shape text into either a gfxShapedWord (cached by a gfxFont)
- * or directly into a gfxTextRun (for cases where we want to shape textruns in
- * their entirety rather than using cached words, because there may be layout
- * features that depend on the inter-word spaces).
- */
-class gfxShapedText
-{
-public:
- gfxShapedText(uint32_t aLength, uint32_t aFlags,
- int32_t aAppUnitsPerDevUnit)
- : mLength(aLength)
- , mFlags(aFlags)
- , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
- { }
-
- virtual ~gfxShapedText() { }
-
- /**
- * This class records the information associated with a character in the
- * input string. It's optimized for the case where there is one glyph
- * representing that character alone.
- *
- * A character can have zero or more associated glyphs. Each glyph
- * has an advance width and an x and y offset.
- * A character may be the start of a cluster.
- * A character may be the start of a ligature group.
- * A character can be "missing", indicating that the system is unable
- * to render the character.
- *
- * All characters in a ligature group conceptually share all the glyphs
- * associated with the characters in a group.
- */
- class CompressedGlyph {
- public:
- CompressedGlyph() { mValue = 0; }
-
- enum {
- // Indicates that a cluster and ligature group starts at this
- // character; this character has a single glyph with a reasonable
- // advance and zero offsets. A "reasonable" advance
- // is one that fits in the available bits (currently 12) (specified
- // in appunits).
- FLAG_IS_SIMPLE_GLYPH = 0x80000000U,
-
- // Indicates whether a linebreak is allowed before this character;
- // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value
- // indicating the kind of linebreak (if any) allowed here.
- FLAGS_CAN_BREAK_BEFORE = 0x60000000U,
-
- FLAGS_CAN_BREAK_SHIFT = 29,
- FLAG_BREAK_TYPE_NONE = 0,
- FLAG_BREAK_TYPE_NORMAL = 1,
- FLAG_BREAK_TYPE_HYPHEN = 2,
-
- FLAG_CHAR_IS_SPACE = 0x10000000U,
-
- // The advance is stored in appunits
- ADVANCE_MASK = 0x0FFF0000U,
- ADVANCE_SHIFT = 16,
-
- GLYPH_MASK = 0x0000FFFFU,
-
- // Non-simple glyphs may or may not have glyph data in the
- // corresponding mDetailedGlyphs entry. They have the following
- // flag bits:
-
- // When NOT set, indicates that this character corresponds to a
- // missing glyph and should be skipped (or possibly, render the character
- // Unicode value in some special way). If there are glyphs,
- // the mGlyphID is actually the UTF16 character code. The bit is
- // inverted so we can memset the array to zero to indicate all missing.
- FLAG_NOT_MISSING = 0x01,
- FLAG_NOT_CLUSTER_START = 0x02,
- FLAG_NOT_LIGATURE_GROUP_START = 0x04,
-
- FLAG_CHAR_IS_TAB = 0x08,
- FLAG_CHAR_IS_NEWLINE = 0x10,
- FLAG_CHAR_IS_LOW_SURROGATE = 0x20,
- CHAR_IDENTITY_FLAGS_MASK = 0x38,
-
- GLYPH_COUNT_MASK = 0x00FFFF00U,
- GLYPH_COUNT_SHIFT = 8
- };
-
- // "Simple glyphs" have a simple glyph ID, simple advance and their
- // x and y offsets are zero. Also the glyph extents do not overflow
- // the font-box defined by the font ascent, descent and glyph advance width.
- // These case is optimized to avoid storing DetailedGlyphs.
-
- // Returns true if the glyph ID aGlyph fits into the compressed representation
- static bool IsSimpleGlyphID(uint32_t aGlyph) {
- return (aGlyph & GLYPH_MASK) == aGlyph;
- }
- // Returns true if the advance aAdvance fits into the compressed representation.
- // aAdvance is in appunits.
- static bool IsSimpleAdvance(uint32_t aAdvance) {
- return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
- }
-
- bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
- uint32_t GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
- uint32_t GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
-
- bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; }
- bool IsClusterStart() const {
- return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START);
- }
- bool IsLigatureGroupStart() const {
- return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
- }
- bool IsLigatureContinuation() const {
- return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 &&
- (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) ==
- (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING);
- }
-
- // Return true if the original character was a normal (breakable,
- // trimmable) space (U+0020). Not true for other characters that
- // may happen to map to the space glyph (U+00A0).
- bool CharIsSpace() const {
- return (mValue & FLAG_CHAR_IS_SPACE) != 0;
- }
-
- bool CharIsTab() const {
- return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0;
- }
- bool CharIsNewline() const {
- return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0;
- }
- bool CharIsLowSurrogate() const {
- return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_LOW_SURROGATE) != 0;
- }
-
- uint32_t CharIdentityFlags() const {
- return IsSimpleGlyph() ? 0 : (mValue & CHAR_IDENTITY_FLAGS_MASK);
- }
-
- void SetClusterStart(bool aIsClusterStart) {
- NS_ASSERTION(!IsSimpleGlyph(),
- "can't call SetClusterStart on simple glyphs");
- if (aIsClusterStart) {
- mValue &= ~FLAG_NOT_CLUSTER_START;
- } else {
- mValue |= FLAG_NOT_CLUSTER_START;
- }
- }
-
- uint8_t CanBreakBefore() const {
- return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT;
- }
- // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
- uint32_t SetCanBreakBefore(uint8_t aCanBreakBefore) {
- NS_ASSERTION(aCanBreakBefore <= 2,
- "Bogus break-before value!");
- uint32_t breakMask = (uint32_t(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
- uint32_t toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
- mValue ^= toggle;
- return toggle;
- }
-
- CompressedGlyph& SetSimpleGlyph(uint32_t aAdvanceAppUnits, uint32_t aGlyph) {
- NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
- NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
- NS_ASSERTION(!CharIdentityFlags(), "Char identity flags lost");
- mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
- FLAG_IS_SIMPLE_GLYPH |
- (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
- return *this;
- }
- CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
- uint32_t aGlyphCount) {
- mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_CHAR_IS_SPACE)) |
- FLAG_NOT_MISSING |
- CharIdentityFlags() |
- (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
- (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
- (aGlyphCount << GLYPH_COUNT_SHIFT);
- return *this;
- }
- /**
- * Missing glyphs are treated as ligature group starts; don't mess with
- * the cluster-start flag (see bugs 618870 and 619286).
- */
- CompressedGlyph& SetMissing(uint32_t aGlyphCount) {
- mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START |
- FLAG_CHAR_IS_SPACE)) |
- CharIdentityFlags() |
- (aGlyphCount << GLYPH_COUNT_SHIFT);
- return *this;
- }
- uint32_t GetGlyphCount() const {
- NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
- return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
- }
-
- void SetIsSpace() {
- mValue |= FLAG_CHAR_IS_SPACE;
- }
- void SetIsTab() {
- NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
- mValue |= FLAG_CHAR_IS_TAB;
- }
- void SetIsNewline() {
- NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
- mValue |= FLAG_CHAR_IS_NEWLINE;
- }
- void SetIsLowSurrogate() {
- NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
- mValue |= FLAG_CHAR_IS_LOW_SURROGATE;
- }
-
- private:
- uint32_t mValue;
- };
-
- // Accessor for the array of CompressedGlyph records, which will be in
- // a different place in gfxShapedWord vs gfxTextRun
- virtual CompressedGlyph *GetCharacterGlyphs() = 0;
-
- /**
- * When the glyphs for a character don't fit into a CompressedGlyph record
- * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
- */
- struct DetailedGlyph {
- /** The glyphID, or the Unicode character
- * if this is a missing glyph */
- uint32_t mGlyphID;
- /** The advance, x-offset and y-offset of the glyph, in appunits
- * mAdvance is in the text direction (RTL or LTR)
- * mXOffset is always from left to right
- * mYOffset is always from top to bottom */
- int32_t mAdvance;
- float mXOffset, mYOffset;
- };
-
- void SetGlyphs(uint32_t aCharIndex, CompressedGlyph aGlyph,
- const DetailedGlyph *aGlyphs);
-
- void SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont);
-
- void SetIsSpace(uint32_t aIndex) {
- GetCharacterGlyphs()[aIndex].SetIsSpace();
- }
-
- void SetIsLowSurrogate(uint32_t aIndex) {
- SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
- GetCharacterGlyphs()[aIndex].SetIsLowSurrogate();
- }
-
- bool HasDetailedGlyphs() const {
- return mDetailedGlyphs != nullptr;
- }
-
- bool IsClusterStart(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return GetCharacterGlyphs()[aPos].IsClusterStart();
- }
-
- bool IsLigatureGroupStart(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return GetCharacterGlyphs()[aPos].IsLigatureGroupStart();
- }
-
- // NOTE that this must not be called for a character offset that does
- // not have any DetailedGlyph records; callers must have verified that
- // GetCharacterGlyphs()[aCharIndex].GetGlyphCount() is greater than zero.
- DetailedGlyph *GetDetailedGlyphs(uint32_t aCharIndex) {
- NS_ASSERTION(GetCharacterGlyphs() && HasDetailedGlyphs() &&
- !GetCharacterGlyphs()[aCharIndex].IsSimpleGlyph() &&
- GetCharacterGlyphs()[aCharIndex].GetGlyphCount() > 0,
- "invalid use of GetDetailedGlyphs; check the caller!");
- return mDetailedGlyphs->Get(aCharIndex);
- }
-
- void AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
- uint32_t aOffset, uint32_t aLength);
-
- // Mark clusters in the CompressedGlyph records, starting at aOffset,
- // based on the Unicode properties of the text in aString.
- // This is also responsible to set the IsSpace flag for space characters.
- void SetupClusterBoundaries(uint32_t aOffset,
- const char16_t *aString,
- uint32_t aLength);
- // In 8-bit text, there won't actually be any clusters, but we still need
- // the space-marking functionality.
- void SetupClusterBoundaries(uint32_t aOffset,
- const uint8_t *aString,
- uint32_t aLength);
-
- uint32_t Flags() const {
- return mFlags;
- }
-
- bool IsRightToLeft() const {
- return (Flags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
- }
-
- float GetDirection() const {
- return IsRightToLeft() ? -1.0f : 1.0f;
- }
-
- bool DisableLigatures() const {
- return (Flags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
- }
-
- bool TextIs8Bit() const {
- return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
- }
+// Parameters passed to gfxFont methods for drawing glyphs from a textrun.
+// The TextRunDrawParams are set up once per textrun; the FontDrawParams
+// are dependent on the specific font, so they are set per GlyphRun.
- int32_t GetAppUnitsPerDevUnit() const {
- return mAppUnitsPerDevUnit;
- }
-
- uint32_t GetLength() const {
- return mLength;
- }
-
- bool FilterIfIgnorable(uint32_t aIndex, uint32_t aCh);
-
-protected:
- // Allocate aCount DetailedGlyphs for the given index
- DetailedGlyph *AllocateDetailedGlyphs(uint32_t aCharIndex,
- uint32_t aCount);
-
- // For characters whose glyph data does not fit the "simple" glyph criteria
- // in CompressedGlyph, we use a sorted array to store the association
- // between the source character offset and an index into an array
- // DetailedGlyphs. The CompressedGlyph record includes a count of
- // the number of DetailedGlyph records that belong to the character,
- // starting at the given index.
- class DetailedGlyphStore {
- public:
- DetailedGlyphStore()
- : mLastUsed(0)
- { }
-
- // This is optimized for the most common calling patterns:
- // we rarely need random access to the records, access is most commonly
- // sequential through the textRun, so we record the last-used index
- // and check whether the caller wants the same record again, or the
- // next; if not, it's most likely we're starting over from the start
- // of the run, so we check the first entry before resorting to binary
- // search as a last resort.
- // NOTE that this must not be called for a character offset that does
- // not have any DetailedGlyph records; callers must have verified that
- // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero
- // before calling this, otherwise the assertions here will fire (in a
- // debug build), and we'll probably crash.
- DetailedGlyph* Get(uint32_t aOffset) {
- NS_ASSERTION(mOffsetToIndex.Length() > 0,
- "no detailed glyph records!");
- DetailedGlyph* details = mDetails.Elements();
- // check common cases (fwd iteration, initial entry, etc) first
- if (mLastUsed < mOffsetToIndex.Length() - 1 &&
- aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) {
- ++mLastUsed;
- } else if (aOffset == mOffsetToIndex[0].mOffset) {
- mLastUsed = 0;
- } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) {
- // do nothing
- } else if (mLastUsed > 0 &&
- aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) {
- --mLastUsed;
- } else {
- mLastUsed =
- mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset());
- }
- NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex,
- "detailed glyph record missing!");
- return details + mOffsetToIndex[mLastUsed].mIndex;
- }
-
- DetailedGlyph* Allocate(uint32_t aOffset, uint32_t aCount) {
- uint32_t detailIndex = mDetails.Length();
- DetailedGlyph *details = mDetails.AppendElements(aCount);
- // We normally set up glyph records sequentially, so the common case
- // here is to append new records to the mOffsetToIndex array;
- // test for that before falling back to the InsertElementSorted
- // method.
- if (mOffsetToIndex.Length() == 0 ||
- aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
- mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex));
- } else {
- mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
- CompareRecordOffsets());
- }
- return details;
- }
-
- size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
- return aMallocSizeOf(this) +
- mDetails.SizeOfExcludingThis(aMallocSizeOf) +
- mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf);
- }
-
- private:
- struct DGRec {
- DGRec(const uint32_t& aOffset, const uint32_t& aIndex)
- : mOffset(aOffset), mIndex(aIndex) { }
- uint32_t mOffset; // source character offset in the textrun
- uint32_t mIndex; // index where this char's DetailedGlyphs begin
- };
-
- struct CompareToOffset {
- bool Equals(const DGRec& a, const uint32_t& b) const {
- return a.mOffset == b;
- }
- bool LessThan(const DGRec& a, const uint32_t& b) const {
- return a.mOffset < b;
- }
- };
-
- struct CompareRecordOffsets {
- bool Equals(const DGRec& a, const DGRec& b) const {
- return a.mOffset == b.mOffset;
- }
- bool LessThan(const DGRec& a, const DGRec& b) const {
- return a.mOffset < b.mOffset;
- }
- };
-
- // Concatenated array of all the DetailedGlyph records needed for the
- // textRun; individual character offsets are associated with indexes
- // into this array via the mOffsetToIndex table.
- nsTArray<DetailedGlyph> mDetails;
-
- // For each character offset that needs DetailedGlyphs, we record the
- // index in mDetails where the list of glyphs begins. This array is
- // sorted by mOffset.
- nsTArray<DGRec> mOffsetToIndex;
-
- // Records the most recently used index into mOffsetToIndex, so that
- // we can support sequential access more quickly than just doing
- // a binary search each time.
- nsTArray<DGRec>::index_type mLastUsed;
- };
-
- nsAutoPtr<DetailedGlyphStore> mDetailedGlyphs;
-
- // Number of char16_t characters and CompressedGlyph glyph records
- uint32_t mLength;
-
- // Shaping flags (direction, ligature-suppression)
- uint32_t mFlags;
-
- int32_t mAppUnitsPerDevUnit;
-};
-
-/*
- * gfxShapedWord: an individual (space-delimited) run of text shaped with a
- * particular font, without regard to external context.
- *
- * The glyph data is copied into gfxTextRuns as needed from the cache of
- * ShapedWords associated with each gfxFont instance.
- */
-class gfxShapedWord : public gfxShapedText
-{
-public:
- // Create a ShapedWord that can hold glyphs for aLength characters,
- // with mCharacterGlyphs sized appropriately.
- //
- // Returns null on allocation failure (does NOT use infallible alloc)
- // so caller must check for success.
- //
- // This does NOT perform shaping, so the returned word contains no
- // glyph data; the caller must call gfxFont::ShapeText() with appropriate
- // parameters to set up the glyphs.
- static gfxShapedWord* Create(const uint8_t *aText, uint32_t aLength,
- int32_t aRunScript,
- int32_t aAppUnitsPerDevUnit,
- uint32_t aFlags) {
- NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
- "excessive length for gfxShapedWord!");
-
- // Compute size needed including the mCharacterGlyphs array
- // and a copy of the original text
- uint32_t size =
- offsetof(gfxShapedWord, mCharGlyphsStorage) +
- aLength * (sizeof(CompressedGlyph) + sizeof(uint8_t));
- void *storage = moz_malloc(size);
- if (!storage) {
- return nullptr;
- }
-
- // Construct in the pre-allocated storage, using placement new
- return new (storage) gfxShapedWord(aText, aLength, aRunScript,
- aAppUnitsPerDevUnit, aFlags);
- }
-
- static gfxShapedWord* Create(const char16_t *aText, uint32_t aLength,
- int32_t aRunScript,
- int32_t aAppUnitsPerDevUnit,
- uint32_t aFlags) {
- NS_ASSERTION(aLength <= gfxPlatform::GetPlatform()->WordCacheCharLimit(),
- "excessive length for gfxShapedWord!");
-
- // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
- // then we convert the text to an 8-bit version and call the 8-bit
- // Create function instead.
- if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
- nsAutoCString narrowText;
- LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
- narrowText);
- return Create((const uint8_t*)(narrowText.BeginReading()),
- aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
- }
-
- uint32_t size =
- offsetof(gfxShapedWord, mCharGlyphsStorage) +
- aLength * (sizeof(CompressedGlyph) + sizeof(char16_t));
- void *storage = moz_malloc(size);
- if (!storage) {
- return nullptr;
- }
-
- return new (storage) gfxShapedWord(aText, aLength, aRunScript,
- aAppUnitsPerDevUnit, aFlags);
- }
-
- // Override operator delete to properly free the object that was
- // allocated via moz_malloc.
- void operator delete(void* p) {
- moz_free(p);
- }
-
- CompressedGlyph *GetCharacterGlyphs() {
- return &mCharGlyphsStorage[0];
- }
-
- const uint8_t* Text8Bit() const {
- NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
- return reinterpret_cast<const uint8_t*>(mCharGlyphsStorage + GetLength());
- }
-
- const char16_t* TextUnicode() const {
- NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
- return reinterpret_cast<const char16_t*>(mCharGlyphsStorage + GetLength());
- }
-
- char16_t GetCharAt(uint32_t aOffset) const {
- NS_ASSERTION(aOffset < GetLength(), "aOffset out of range");
- return TextIs8Bit() ?
- char16_t(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
- }
-
- int32_t Script() const {
- return mScript;
- }
-
- void ResetAge() {
- mAgeCounter = 0;
- }
- uint32_t IncrementAge() {
- return ++mAgeCounter;
- }
-
-private:
- // so that gfxTextRun can share our DetailedGlyphStore class
- friend class gfxTextRun;
-
- // Construct storage for a ShapedWord, ready to receive glyph data
- gfxShapedWord(const uint8_t *aText, uint32_t aLength,
- int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
- uint32_t aFlags)
- : gfxShapedText(aLength, aFlags | gfxTextRunFactory::TEXT_IS_8BIT,
- aAppUnitsPerDevUnit)
- , mScript(aRunScript)
- , mAgeCounter(0)
- {
- memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
- uint8_t *text = reinterpret_cast<uint8_t*>(&mCharGlyphsStorage[aLength]);
- memcpy(text, aText, aLength * sizeof(uint8_t));
- }
-
- gfxShapedWord(const char16_t *aText, uint32_t aLength,
- int32_t aRunScript, int32_t aAppUnitsPerDevUnit,
- uint32_t aFlags)
- : gfxShapedText(aLength, aFlags, aAppUnitsPerDevUnit)
- , mScript(aRunScript)
- , mAgeCounter(0)
- {
- memset(mCharGlyphsStorage, 0, aLength * sizeof(CompressedGlyph));
- char16_t *text = reinterpret_cast<char16_t*>(&mCharGlyphsStorage[aLength]);
- memcpy(text, aText, aLength * sizeof(char16_t));
- SetupClusterBoundaries(0, aText, aLength);
- }
-
- int32_t mScript;
-
- uint32_t mAgeCounter;
-
- // The mCharGlyphsStorage array is actually a variable-size member;
- // when the ShapedWord is created, its size will be increased as necessary
- // to allow the proper number of glyphs to be stored.
- // The original text, in either 8-bit or 16-bit form, will be stored
- // immediately following the CompressedGlyphs.
- CompressedGlyph mCharGlyphsStorage[1];
-};
-
-/**
- * Callback for Draw() to use when drawing text with mode
- * DrawMode::GLYPH_PATH.
- */
-struct gfxTextRunDrawCallbacks {
-
- /**
- * Constructs a new DrawCallbacks object.
- *
- * @param aShouldPaintSVGGlyphs If true, SVG glyphs will be
- * painted and the NotifyBeforeSVGGlyphPainted/NotifyAfterSVGGlyphPainted
- * callbacks will be invoked for each SVG glyph. If false, SVG glyphs
- * will not be painted; fallback plain glyphs are not emitted either.
- */
- explicit gfxTextRunDrawCallbacks(bool aShouldPaintSVGGlyphs = false)
- : mShouldPaintSVGGlyphs(aShouldPaintSVGGlyphs)
- {
- }
-
- /**
- * Called when a path has been emitted to the gfxContext when
- * painting a text run. This can be called any number of times,
- * due to partial ligatures and intervening SVG glyphs.
- */
- virtual void NotifyGlyphPathEmitted() = 0;
-
- /**
- * Called just before an SVG glyph has been painted to the gfxContext.
- */
- virtual void NotifyBeforeSVGGlyphPainted() { }
-
- /**
- * Called just after an SVG glyph has been painted to the gfxContext.
- */
- virtual void NotifyAfterSVGGlyphPainted() { }
-
- bool mShouldPaintSVGGlyphs;
+struct TextRunDrawParams {
+ mozilla::RefPtr<mozilla::gfx::DrawTarget> dt;
+ gfxContext *context;
+ gfxFont::Spacing *spacing;
+ gfxTextRunDrawCallbacks *callbacks;
+ gfxTextContextPaint *runContextPaint;
+ gfxFloat direction;
+ double devPerApp;
+ DrawMode drawMode;
+ bool isRTL;
+ bool paintSVGGlyphs;
};
-/**
- * gfxTextRun is an abstraction for drawing and measuring substrings of a run
- * of text. It stores runs of positioned glyph data, each run having a single
- * gfxFont. The glyphs are associated with a string of source text, and the
- * gfxTextRun APIs take parameters that are offsets into that source text.
- *
- * gfxTextRuns are not refcounted. They should be deleted when no longer required.
- *
- * gfxTextRuns are mostly immutable. The only things that can change are
- * inter-cluster spacing and line break placement. Spacing is always obtained
- * lazily by methods that need it, it is not cached. Line breaks are stored
- * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
- * not actually do anything to explicitly account for line breaks). Initially
- * there are no line breaks. The textrun can record line breaks before or after
- * any given cluster. (Line breaks specified inside clusters are ignored.)
- *
- * It is important that zero-length substrings are handled correctly. This will
- * be on the test!
- */
-class gfxTextRun : public gfxShapedText {
-public:
-
- // Override operator delete to properly free the object that was
- // allocated via moz_malloc.
- void operator delete(void* p) {
- moz_free(p);
- }
-
- virtual ~gfxTextRun();
-
- typedef gfxFont::RunMetrics Metrics;
-
- // Public textrun API for general use
-
- bool IsClusterStart(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].IsClusterStart();
- }
- bool IsLigatureGroupStart(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].IsLigatureGroupStart();
- }
- bool CanBreakLineBefore(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CanBreakBefore() ==
- CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
- }
- bool CanHyphenateBefore(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CanBreakBefore() ==
- CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
- }
-
- bool CharIsSpace(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CharIsSpace();
- }
- bool CharIsTab(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CharIsTab();
- }
- bool CharIsNewline(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CharIsNewline();
- }
- bool CharIsLowSurrogate(uint32_t aPos) {
- NS_ASSERTION(aPos < GetLength(), "aPos out of range");
- return mCharacterGlyphs[aPos].CharIsLowSurrogate();
- }
-
- uint32_t GetLength() { return mLength; }
-
- // All uint32_t aStart, uint32_t aLength ranges below are restricted to
- // grapheme cluster boundaries! All offsets are in terms of the string
- // passed into MakeTextRun.
-
- // All coordinates are in layout/app units
-
- /**
- * Set the potential linebreaks for a substring of the textrun. These are
- * the "allow break before" points. Initially, there are no potential
- * linebreaks.
- *
- * This can change glyphs and/or geometry! Some textruns' shapes
- * depend on potential line breaks (e.g., title-case-converting textruns).
- * This function is virtual so that those textruns can reshape themselves.
- *
- * @return true if this changed the linebreaks, false if the new line
- * breaks are the same as the old
- */
- virtual bool SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
- uint8_t *aBreakBefore,
- gfxContext *aRefContext);
-
- /**
- * Layout provides PropertyProvider objects. These allow detection of
- * potential line break points and computation of spacing. We pass the data
- * this way to allow lazy data acquisition; for example BreakAndMeasureText
- * will want to only ask for properties of text it's actually looking at.
- *
- * NOTE that requested spacing may not actually be applied, if the textrun
- * is unable to apply it in some context. Exception: spacing around a
- * whitespace character MUST always be applied.
- */
- class PropertyProvider {
- public:
- // Detect hyphenation break opportunities in the given range; breaks
- // not at cluster boundaries will be ignored.
- virtual void GetHyphenationBreaks(uint32_t aStart, uint32_t aLength,
- bool *aBreakBefore) = 0;
-
- // Returns the provider's hyphenation setting, so callers can decide
- // whether it is necessary to call GetHyphenationBreaks.
- // Result is an NS_STYLE_HYPHENS_* value.
- virtual int8_t GetHyphensOption() = 0;
-
- // Returns the extra width that will be consumed by a hyphen. This should
- // be constant for a given textrun.
- virtual gfxFloat GetHyphenWidth() = 0;
-
- typedef gfxFont::Spacing Spacing;
-
- /**
- * Get the spacing around the indicated characters. Spacing must be zero
- * inside clusters. In other words, if character i is not
- * CLUSTER_START, then character i-1 must have zero after-spacing and
- * character i must have zero before-spacing.
- */
- virtual void GetSpacing(uint32_t aStart, uint32_t aLength,
- Spacing *aSpacing) = 0;
-
- // Returns a gfxContext that can be used to measure the hyphen glyph.
- // Only called if the hyphen width is requested.
- virtual already_AddRefed<gfxContext> GetContext() = 0;
-
- // Return the appUnitsPerDevUnit value to be used when measuring.
- // Only called if the hyphen width is requested.
- virtual uint32_t GetAppUnitsPerDevUnit() = 0;
- };
-
- class ClusterIterator {
- public:
- explicit ClusterIterator(gfxTextRun *aTextRun);
-
- void Reset();
-
- bool NextCluster();
-
- uint32_t Position() const {
- return mCurrentChar;
- }
-
- uint32_t ClusterLength() const;
-
- gfxFloat ClusterAdvance(PropertyProvider *aProvider) const;
-
- private:
- gfxTextRun *mTextRun;
- uint32_t mCurrentChar;
- };
-
- /**
- * Draws a substring. Uses only GetSpacing from aBreakProvider.
- * The provided point is the baseline origin on the left of the string
- * for LTR, on the right of the string for RTL.
- * @param aAdvanceWidth if non-null, the advance width of the substring
- * is returned here.
- *
- * Drawing should respect advance widths in the sense that for LTR runs,
- * Draw(ctx, pt, offset1, length1, dirty, &provider, &advance) followed by
- * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1 + length1, length2,
- * dirty, &provider, nullptr) should have the same effect as
- * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr).
- * For RTL runs the rule is:
- * Draw(ctx, pt, offset1 + length1, length2, dirty, &provider, &advance) followed by
- * Draw(ctx, gfxPoint(pt.x + advance, pt.y), offset1, length1,
- * dirty, &provider, nullptr) should have the same effect as
- * Draw(ctx, pt, offset1, length1+length2, dirty, &provider, nullptr).
- *
- * Glyphs should be drawn in logical content order, which can be significant
- * if they overlap (perhaps due to negative spacing).
- */
- void Draw(gfxContext *aContext, gfxPoint aPt,
- DrawMode aDrawMode,
- uint32_t aStart, uint32_t aLength,
- PropertyProvider *aProvider,
- gfxFloat *aAdvanceWidth, gfxTextContextPaint *aContextPaint,
- gfxTextRunDrawCallbacks *aCallbacks = nullptr);
-
- /**
- * Computes the ReflowMetrics for a substring.
- * Uses GetSpacing from aBreakProvider.
- * @param aBoundingBoxType which kind of bounding box (loose/tight)
- */
- Metrics MeasureText(uint32_t aStart, uint32_t aLength,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContextForTightBoundingBox,
- PropertyProvider *aProvider);
-
- /**
- * Computes just the advance width for a substring.
- * Uses GetSpacing from aBreakProvider.
- */
- gfxFloat GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
- PropertyProvider *aProvider);
-
- /**
- * Clear all stored line breaks for the given range (both before and after),
- * and then set the line-break state before aStart to aBreakBefore and
- * after the last cluster to aBreakAfter.
- *
- * We require that before and after line breaks be consistent. For clusters
- * i and i+1, we require that if there is a break after cluster i, a break
- * will be specified before cluster i+1. This may be temporarily violated
- * (e.g. after reflowing line L and before reflowing line L+1); to handle
- * these temporary violations, we say that there is a break betwen i and i+1
- * if a break is specified after i OR a break is specified before i+1.
- *
- * This can change textrun geometry! The existence of a linebreak can affect
- * the advance width of the cluster before the break (when kerning) or the
- * geometry of one cluster before the break or any number of clusters
- * after the break. (The one-cluster-before-the-break limit is somewhat
- * arbitrary; if some scripts require breaking it, then we need to
- * alter nsTextFrame::TrimTrailingWhitespace, perhaps drastically becase
- * it could affect the layout of frames before it...)
- *
- * We return true if glyphs or geometry changed, false otherwise. This
- * function is virtual so that gfxTextRun subclasses can reshape
- * properly.
- *
- * @param aAdvanceWidthDelta if non-null, returns the change in advance
- * width of the given range.
- */
- virtual bool SetLineBreaks(uint32_t aStart, uint32_t aLength,
- bool aLineBreakBefore, bool aLineBreakAfter,
- gfxFloat *aAdvanceWidthDelta,
- gfxContext *aRefContext);
-
- /**
- * Finds the longest substring that will fit into the given width.
- * Uses GetHyphenationBreaks and GetSpacing from aBreakProvider.
- * Guarantees the following:
- * -- 0 <= result <= aMaxLength
- * -- result is the maximal value of N such that either
- * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
- * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
- * OR N == aMaxLength && GetAdvanceWidth(aStart, N) <= aWidth
- * where GetAdvanceWidth assumes the effect of
- * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
- * -- if no such N exists, then result is the smallest N such that
- * N < aMaxLength && line break at N
- * OR N < aMaxLength && hyphen break at N
- * OR N == aMaxLength
- *
- * The call has the effect of
- * SetLineBreaks(aStart, result, aLineBreakBefore, result < aMaxLength, aProvider)
- * and the returned metrics and the invariants above reflect this.
- *
- * @param aMaxLength this can be UINT32_MAX, in which case the length used
- * is up to the end of the string
- * @param aLineBreakBefore set to true if and only if there is an actual
- * line break at the start of this string.
- * @param aSuppressInitialBreak if true, then we assume there is no possible
- * linebreak before aStart. If false, then we will check the internal
- * line break opportunity state before deciding whether to return 0 as the
- * character to break before.
- * @param aTrimWhitespace if non-null, then we allow a trailing run of
- * spaces to be trimmed; the width of the space(s) will not be included in
- * the measured string width for comparison with the limit aWidth, and
- * trimmed spaces will not be included in returned metrics. The width
- * of the trimmed spaces will be returned in aTrimWhitespace.
- * Trimmed spaces are still counted in the "characters fit" result.
- * @param aMetrics if non-null, we fill this in for the returned substring.
- * If a hyphenation break was used, the hyphen is NOT included in the returned metrics.
- * @param aBoundingBoxType whether to make the bounding box in aMetrics tight
- * @param aRefContextForTightBoundingBox a reference context to get the
- * tight bounding box, if requested
- * @param aUsedHyphenation if non-null, records if we selected a hyphenation break
- * @param aLastBreak if non-null and result is aMaxLength, we set this to
- * the maximal N such that
- * N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
- * OR N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
- * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes
- * the effect of
- * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
- *
- * @param aCanWordWrap true if we can break between any two grapheme
- * clusters. This is set by word-wrap: break-word
- *
- * @param aBreakPriority in/out the priority of the break opportunity
- * saved in the line. If we are prioritizing break opportunities, we will
- * not set a break with a lower priority. @see gfxBreakPriority.
- *
- * Note that negative advance widths are possible especially if negative
- * spacing is provided.
- */
- uint32_t BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
- bool aLineBreakBefore, gfxFloat aWidth,
- PropertyProvider *aProvider,
- bool aSuppressInitialBreak,
- gfxFloat *aTrimWhitespace,
- Metrics *aMetrics,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContextForTightBoundingBox,
- bool *aUsedHyphenation,
- uint32_t *aLastBreak,
- bool aCanWordWrap,
- gfxBreakPriority *aBreakPriority);
-
- /**
- * Update the reference context.
- * XXX this is a hack. New text frame does not call this. Use only
- * temporarily for old text frame.
- */
- void SetContext(gfxContext *aContext) {}
-
- // Utility getters
-
- gfxFloat GetDirection() const { return (mFlags & gfxTextRunFactory::TEXT_IS_RTL) ? -1.0 : 1.0; }
- void *GetUserData() const { return mUserData; }
- void SetUserData(void *aUserData) { mUserData = aUserData; }
- uint32_t GetFlags() const { return mFlags; }
- void SetFlagBits(uint32_t aFlags) {
- NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
- "Only user flags should be mutable");
- mFlags |= aFlags;
- }
- void ClearFlagBits(uint32_t aFlags) {
- NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
- "Only user flags should be mutable");
- mFlags &= ~aFlags;
- }
- const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
- gfxFontGroup *GetFontGroup() const { return mFontGroup; }
-
-
- // Call this, don't call "new gfxTextRun" directly. This does custom
- // allocation and initialization
- static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams,
- uint32_t aLength, gfxFontGroup *aFontGroup,
- uint32_t aFlags);
-
- // The text is divided into GlyphRuns as necessary
- struct GlyphRun {
- nsRefPtr<gfxFont> mFont; // never null
- uint32_t mCharacterOffset; // into original UTF16 string
- uint8_t mMatchType;
- };
-
- class GlyphRunIterator {
- public:
- GlyphRunIterator(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aLength)
- : mTextRun(aTextRun), mStartOffset(aStart), mEndOffset(aStart + aLength) {
- mNextIndex = mTextRun->FindFirstGlyphRunContaining(aStart);
- }
- bool NextRun();
- GlyphRun *GetGlyphRun() { return mGlyphRun; }
- uint32_t GetStringStart() { return mStringStart; }
- uint32_t GetStringEnd() { return mStringEnd; }
- private:
- gfxTextRun *mTextRun;
- GlyphRun *mGlyphRun;
- uint32_t mStringStart;
- uint32_t mStringEnd;
- uint32_t mNextIndex;
- uint32_t mStartOffset;
- uint32_t mEndOffset;
- };
-
- class GlyphRunOffsetComparator {
- public:
- bool Equals(const GlyphRun& a,
- const GlyphRun& b) const
- {
- return a.mCharacterOffset == b.mCharacterOffset;
- }
-
- bool LessThan(const GlyphRun& a,
- const GlyphRun& b) const
- {
- return a.mCharacterOffset < b.mCharacterOffset;
- }
- };
-
- friend class GlyphRunIterator;
- friend class FontSelector;
-
- // API for setting up the textrun glyphs. Should only be called by
- // things that construct textruns.
- /**
- * We've found a run of text that should use a particular font. Call this
- * only during initialization when font substitution has been computed.
- * Call it before setting up the glyphs for the characters in this run;
- * SetMissingGlyph requires that the correct glyphrun be installed.
- *
- * If aForceNewRun, a new glyph run will be added, even if the
- * previously added run uses the same font. If glyph runs are
- * added out of strictly increasing aStartCharIndex order (via
- * force), then SortGlyphRuns must be called after all glyph runs
- * are added before any further operations are performed with this
- * TextRun.
- */
- nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
- uint32_t aStartCharIndex, bool aForceNewRun);
- void ResetGlyphRuns() { mGlyphRuns.Clear(); }
- void SortGlyphRuns();
- void SanitizeGlyphRuns();
-
- CompressedGlyph* GetCharacterGlyphs() {
- NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
- return mCharacterGlyphs;
- }
-
- // clean out results from shaping in progress, used for fallback scenarios
- void ClearGlyphsAndCharacters();
-
- void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, uint32_t aCharIndex);
-
- // Set the glyph data for the given character index to the font's
- // space glyph, IF this can be done as a "simple" glyph record
- // (not requiring a DetailedGlyph entry). This avoids the need to call
- // the font shaper and go through the shaped-word cache for most spaces.
- //
- // The parameter aSpaceChar is the original character code for which
- // this space glyph is being used; if this is U+0020, we need to record
- // that it could be trimmed at a run edge, whereas other kinds of space
- // (currently just U+00A0) would not be trimmable/breakable.
- //
- // Returns true if it was able to set simple glyph data for the space;
- // if it returns false, the caller needs to fall back to some other
- // means to create the necessary (detailed) glyph data.
- bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
- uint32_t aCharIndex, char16_t aSpaceChar);
-
- // Record the positions of specific characters that layout may need to
- // detect in the textrun, even though it doesn't have an explicit copy
- // of the original text. These are recorded using flag bits in the
- // CompressedGlyph record; if necessary, we convert "simple" glyph records
- // to "complex" ones as the Tab and Newline flags are not present in
- // simple CompressedGlyph records.
- void SetIsTab(uint32_t aIndex) {
- CompressedGlyph *g = &mCharacterGlyphs[aIndex];
- if (g->IsSimpleGlyph()) {
- DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
- details->mGlyphID = g->GetSimpleGlyph();
- details->mAdvance = g->GetSimpleAdvance();
- details->mXOffset = details->mYOffset = 0;
- SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
- }
- g->SetIsTab();
- }
- void SetIsNewline(uint32_t aIndex) {
- CompressedGlyph *g = &mCharacterGlyphs[aIndex];
- if (g->IsSimpleGlyph()) {
- DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
- details->mGlyphID = g->GetSimpleGlyph();
- details->mAdvance = g->GetSimpleAdvance();
- details->mXOffset = details->mYOffset = 0;
- SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
- }
- g->SetIsNewline();
- }
- void SetIsLowSurrogate(uint32_t aIndex) {
- SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nullptr);
- mCharacterGlyphs[aIndex].SetIsLowSurrogate();
- }
-
- /**
- * Prefetch all the glyph extents needed to ensure that Measure calls
- * on this textrun not requesting tight boundingBoxes will succeed. Note
- * that some glyph extents might not be fetched due to OOM or other
- * errors.
- */
- void FetchGlyphExtents(gfxContext *aRefContext);
-
- uint32_t CountMissingGlyphs();
- const GlyphRun *GetGlyphRuns(uint32_t *aNumGlyphRuns) {
- *aNumGlyphRuns = mGlyphRuns.Length();
- return mGlyphRuns.Elements();
- }
- // Returns the index of the GlyphRun containing the given offset.
- // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
- uint32_t FindFirstGlyphRunContaining(uint32_t aOffset);
-
- // Copy glyph data from a ShapedWord into this textrun.
- void CopyGlyphDataFrom(gfxShapedWord *aSource, uint32_t aStart);
-
- // Copy glyph data for a range of characters from aSource to this
- // textrun.
- void CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
- uint32_t aLength, uint32_t aDest);
-
- nsExpirationState *GetExpirationState() { return &mExpirationState; }
-
- // Tell the textrun to release its reference to its creating gfxFontGroup
- // immediately, rather than on destruction. This is used for textruns
- // that are actually owned by a gfxFontGroup, so that they don't keep it
- // permanently alive due to a circular reference. (The caller of this is
- // taking responsibility for ensuring the textrun will not outlive its
- // mFontGroup.)
- void ReleaseFontGroup();
-
- struct LigatureData {
- // textrun offsets of the start and end of the containing ligature
- uint32_t mLigatureStart;
- uint32_t mLigatureEnd;
- // appunits advance to the start of the ligature part within the ligature;
- // never includes any spacing
- gfxFloat mPartAdvance;
- // appunits width of the ligature part; includes before-spacing
- // when the part is at the start of the ligature, and after-spacing
- // when the part is as the end of the ligature
- gfxFloat mPartWidth;
-
- bool mClipBeforePart;
- bool mClipAfterPart;
- };
-
- // return storage used by this run, for memory reporter;
- // nsTransformedTextRun needs to override this as it holds additional data
- virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
- MOZ_MUST_OVERRIDE;
- virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
- MOZ_MUST_OVERRIDE;
-
- // Get the size, if it hasn't already been gotten, marking as it goes.
- size_t MaybeSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) {
- if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
- return 0;
- }
- mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
- return SizeOfIncludingThis(aMallocSizeOf);
- }
- void ResetSizeOfAccountingFlags() {
- mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
- }
-
- // shaping state - for some font features, fallback is required that
- // affects the entire run. for example, fallback for one script/font
- // portion of a textrun requires fallback to be applied to the entire run
-
- enum ShapingState {
- eShapingState_Normal, // default state
- eShapingState_ShapingWithFeature, // have shaped with feature
- eShapingState_ShapingWithFallback, // have shaped with fallback
- eShapingState_Aborted, // abort initial iteration
- eShapingState_ForceFallbackFeature // redo with fallback forced on
- };
-
- ShapingState GetShapingState() const { return mShapingState; }
- void SetShapingState(ShapingState aShapingState) {
- mShapingState = aShapingState;
- }
-
-#ifdef DEBUG
- void Dump(FILE* aOutput);
-#endif
-
-protected:
- /**
- * Create a textrun, and set its mCharacterGlyphs to point immediately
- * after the base object; this is ONLY used in conjunction with placement
- * new, after allocating a block large enough for the glyph records to
- * follow the base textrun object.
- */
- gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
- uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags);
-
- /**
- * Helper for the Create() factory method to allocate the required
- * glyph storage for a textrun object with the basic size aSize,
- * plus room for aLength glyph records.
- */
- static void* AllocateStorageForTextRun(size_t aSize, uint32_t aLength);
-
- // Pointer to the array of CompressedGlyph records; must be initialized
- // when the object is constructed.
- CompressedGlyph *mCharacterGlyphs;
-
-private:
- // **** general helpers ****
-
- // Get the total advance for a range of glyphs.
- int32_t GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd);
-
- // Spacing for characters outside the range aSpacingStart/aSpacingEnd
- // is assumed to be zero; such characters are not passed to aProvider.
- // This is useful to protect aProvider from being passed character indices
- // it is not currently able to handle.
- bool GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
- PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- nsTArray<PropertyProvider::Spacing> *aSpacing);
-
- // **** ligature helpers ****
- // (Platforms do the actual ligaturization, but we need to do a bunch of stuff
- // to handle requests that begin or end inside a ligature)
-
- // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
- LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
- PropertyProvider *aProvider);
- gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
- PropertyProvider *aProvider);
- void DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
- gfxPoint *aPt, PropertyProvider *aProvider,
- TextRunDrawParams& aParams);
- // Advance aStart to the start of the nearest ligature; back up aEnd
- // to the nearest ligature end; may result in *aStart == *aEnd
- void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd);
- // result in appunits
- gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider);
- void AccumulatePartialLigatureMetrics(gfxFont *aFont,
- uint32_t aStart, uint32_t aEnd,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContext,
- PropertyProvider *aProvider,
- Metrics *aMetrics);
-
- // **** measurement helper ****
- void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
- gfxFont::BoundingBoxType aBoundingBoxType,
- gfxContext *aRefContext,
- PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- Metrics *aMetrics);
-
- // **** drawing helper ****
- void DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
- gfxPoint *aPt, PropertyProvider *aProvider,
- uint32_t aSpacingStart, uint32_t aSpacingEnd,
- TextRunDrawParams& aParams);
-
- // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
- // for smaller size especially in the super-common one-glyphrun case
- nsAutoTArray<GlyphRun,1> mGlyphRuns;
-
- void *mUserData;
- gfxFontGroup *mFontGroup; // addrefed on creation, but our reference
- // may be released by ReleaseFontGroup()
- gfxSkipChars mSkipChars;
- nsExpirationState mExpirationState;
-
- bool mSkipDrawing; // true if the font group we used had a user font
- // download that's in progress, so we should hide text
- // until the download completes (or timeout fires)
- bool mReleasedFontGroup; // we already called NS_RELEASE on
- // mFontGroup, so don't do it again
-
- // shaping state for handling variant fallback features
- // such as subscript/superscript variant glyphs
- ShapingState mShapingState;
+struct FontDrawParams {
+ mozilla::RefPtr<mozilla::gfx::ScaledFont> scaledFont;
+ mozilla::RefPtr<mozilla::gfx::GlyphRenderingOptions> renderingOptions;
+ gfxTextContextPaint *contextPaint;
+ mozilla::gfx::Matrix *passedInvMatrix;
+ mozilla::gfx::Matrix matInv;
+ double synBoldOnePixelOffset;
+ int32_t extraStrikes;
+ mozilla::gfx::DrawOptions drawOptions;
+ bool haveSVGGlyphs;
+ bool haveColorGlyphs;
};
-class gfxFontGroup : public gfxTextRunFactory {
-public:
- class FamilyFace {
- public:
- FamilyFace() { }
-
- FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
- : mFamily(aFamily), mFont(aFont)
- {
- NS_ASSERTION(aFont, "font pointer must not be null");
- NS_ASSERTION(!aFamily ||
- aFamily->ContainsFace(aFont->GetFontEntry()),
- "font is not a member of the given family");
- }
-
- gfxFontFamily* Family() const { return mFamily.get(); }
- gfxFont* Font() const { return mFont.get(); }
-
- private:
- nsRefPtr<gfxFontFamily> mFamily;
- nsRefPtr<gfxFont> mFont;
- };
-
- static void Shutdown(); // platform must call this to release the languageAtomService
-
- gfxFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
- const gfxFontStyle *aStyle,
- gfxUserFontSet *aUserFontSet = nullptr);
-
- virtual ~gfxFontGroup();
-
- virtual gfxFont *GetFontAt(int32_t i) {
- // If it turns out to be hard for all clients that cache font
- // groups to call UpdateFontList at appropriate times, we could
- // instead consider just calling UpdateFontList from someplace
- // more central (such as here).
- NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
- "Whoever was caching this font group should have "
- "called UpdateFontList on it");
- NS_ASSERTION(mFonts.Length() > uint32_t(i) && mFonts[i].Font(),
- "Requesting a font index that doesn't exist");
-
- return mFonts[i].Font();
- }
-
- // Returns the first font in the font-group that has an OpenType MATH table,
- // or null if no such font is available. The GetMathConstant methods may be
- // called on the returned font.
- gfxFont *GetFirstMathFont();
-
- uint32_t FontListLength() const {
- return mFonts.Length();
- }
-
- const gfxFontStyle *GetStyle() const { return &mStyle; }
-
- virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
-
- /**
- * The listed characters should be treated as invisible and zero-width
- * when creating textruns.
- */
- static bool IsInvalidChar(uint8_t ch);
- static bool IsInvalidChar(char16_t ch);
-
- /**
- * Make a textrun for a given string.
- * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
- * textrun will copy it.
- * This calls FetchGlyphExtents on the textrun.
- */
- virtual gfxTextRun *MakeTextRun(const char16_t *aString, uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags);
- /**
- * Make a textrun for a given string.
- * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
- * textrun will copy it.
- * This calls FetchGlyphExtents on the textrun.
- */
- virtual gfxTextRun *MakeTextRun(const uint8_t *aString, uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags);
-
- /**
- * Textrun creation helper for clients that don't want to pass
- * a full Parameters record.
- */
- template<typename T>
- gfxTextRun *MakeTextRun(const T *aString, uint32_t aLength,
- gfxContext *aRefContext,
- int32_t aAppUnitsPerDevUnit,
- uint32_t aFlags)
- {
- gfxTextRunFactory::Parameters params = {
- aRefContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevUnit
- };
- return MakeTextRun(aString, aLength, ¶ms, aFlags);
- }
-
- /**
- * Get the (possibly-cached) width of the hyphen character.
- * The aCtx and aAppUnitsPerDevUnit parameters will be used only if
- * needed to initialize the cached hyphen width; otherwise they are
- * ignored.
- */
- gfxFloat GetHyphenWidth(gfxTextRun::PropertyProvider* aProvider);
-
- /**
- * Make a text run representing a single hyphen character.
- * This will use U+2010 HYPHEN if available in the first font,
- * otherwise fall back to U+002D HYPHEN-MINUS.
- * The caller is responsible for deleting the returned text run
- * when no longer required.
- */
- gfxTextRun *MakeHyphenTextRun(gfxContext *aCtx,
- uint32_t aAppUnitsPerDevUnit);
-
- /**
- * Check whether a given font (specified by its gfxFontEntry)
- * is already in the fontgroup's list of actual fonts
- */
- bool HasFont(const gfxFontEntry *aFontEntry);
-
- // This returns the preferred underline for this font group.
- // Some CJK fonts have wrong underline offset in its metrics.
- // If this group has such "bad" font, each platform's gfxFontGroup initialized mUnderlineOffset.
- // The value should be lower value of first font's metrics and the bad font's metrics.
- // Otherwise, this returns from first font's metrics.
- enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX };
- virtual gfxFloat GetUnderlineOffset() {
- if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET)
- mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset;
- return mUnderlineOffset;
- }
-
- virtual already_AddRefed<gfxFont>
- FindFontForChar(uint32_t ch, uint32_t prevCh, int32_t aRunScript,
- gfxFont *aPrevMatchedFont,
- uint8_t *aMatchType);
-
- // search through pref fonts for a character, return nullptr if no matching pref font
- virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh);
-
- virtual already_AddRefed<gfxFont>
- WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript);
-
- template<typename T>
- void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
- const T *aString, uint32_t aLength,
- int32_t aRunScript);
-
- gfxUserFontSet* GetUserFontSet();
-
- // With downloadable fonts, the composition of the font group can change as fonts are downloaded
- // for each change in state of the user font set, the generation value is bumped to avoid picking up
- // previously created text runs in the text run word cache. For font groups based on stylesheets
- // with no @font-face rule, this always returns 0.
- uint64_t GetGeneration();
-
- // used when logging text performance
- gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; }
- void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; }
-
- // This will call UpdateFontList() if the user font set is changed.
- void SetUserFontSet(gfxUserFontSet *aUserFontSet);
-
- // If there is a user font set, check to see whether the font list or any
- // caches need updating.
- virtual void UpdateFontList();
-
- bool ShouldSkipDrawing() const {
- return mSkipDrawing;
- }
-
- class LazyReferenceContextGetter {
- public:
- virtual already_AddRefed<gfxContext> GetRefContext() = 0;
- };
- // The gfxFontGroup keeps ownership of this textrun.
- // It is only guaranteed to exist until the next call to GetEllipsisTextRun
- // (which might use a different appUnitsPerDev value) for the font group,
- // or until UpdateFontList is called, or the fontgroup is destroyed.
- // Get it/use it/forget it :) - don't keep a reference that might go stale.
- gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
- LazyReferenceContextGetter& aRefContextGetter);
-
- // helper method for resolving generic font families
- static void
- ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
- nsIAtom *aLanguage,
- nsTArray<nsString>& aGenericFamilies);
-
-protected:
- mozilla::FontFamilyList mFamilyList;
- gfxFontStyle mStyle;
- nsTArray<FamilyFace> mFonts;
- gfxFloat mUnderlineOffset;
- gfxFloat mHyphenWidth;
-
- nsRefPtr<gfxUserFontSet> mUserFontSet;
- uint64_t mCurrGeneration; // track the current user font set generation, rebuild font list if needed
-
- gfxTextPerfMetrics *mTextPerf;
-
- // Cache a textrun representing an ellipsis (useful for CSS text-overflow)
- // at a specific appUnitsPerDevPixel size
- nsAutoPtr<gfxTextRun> mCachedEllipsisTextRun;
-
- // cache the most recent pref font to avoid general pref font lookup
- nsRefPtr<gfxFontFamily> mLastPrefFamily;
- nsRefPtr<gfxFont> mLastPrefFont;
- eFontPrefLang mLastPrefLang; // lang group for last pref font
- eFontPrefLang mPageLang;
- bool mLastPrefFirstFont; // is this the first font in the list of pref fonts for this lang group?
-
- bool mSkipDrawing; // hide text while waiting for a font
- // download to complete (or fallback
- // timer to fire)
-
- /**
- * Textrun creation short-cuts for special cases where we don't need to
- * call a font shaper to generate glyphs.
- */
- gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
- gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
- gfxTextRun *MakeBlankTextRun(uint32_t aLength,
- const Parameters *aParams, uint32_t aFlags);
-
- // Initialize the list of fonts
- void BuildFontList();
-
- // Init this font group's font metrics. If there no bad fonts, you don't need to call this.
- // But if there are one or more bad fonts which have bad underline offset,
- // you should call this with the *first* bad font.
- void InitMetricsForBadFont(gfxFont* aBadFont);
-
- // Set up the textrun glyphs for an entire text run:
- // find script runs, and then call InitScriptRun for each
- template<typename T>
- void InitTextRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const T *aString,
- uint32_t aLength);
-
- // InitTextRun helper to handle a single script run, by finding font ranges
- // and calling each font's InitTextRun() as appropriate
- template<typename T>
- void InitScriptRun(gfxContext *aContext,
- gfxTextRun *aTextRun,
- const T *aString,
- uint32_t aScriptRunStart,
- uint32_t aScriptRunEnd,
- int32_t aRunScript);
-
- // Helper for font-matching:
- // see if aCh is supported in any of the faces from aFamily;
- // if so return the best style match, else return null.
- already_AddRefed<gfxFont> TryAllFamilyMembers(gfxFontFamily* aFamily,
- uint32_t aCh);
-
- // helper methods for looking up fonts
-
- // iterate over the fontlist, lookup names and expand generics
- void EnumerateFontList(nsIAtom *aLanguage, void *aClosure = nullptr);
-
- // expand a generic to a list of specific names based on prefs
- void FindGenericFonts(mozilla::FontFamilyType aGenericType,
- nsIAtom *aLanguage,
- void *aClosure);
-
- // lookup and add a font with a given name (i.e. *not* a generic!)
- virtual void FindPlatformFont(const nsAString& aName,
- bool aUseFontSet,
- void *aClosure);
-
- static nsILanguageAtomService* gLangService;
-};
#endif