diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 851e775f5939..b34814c865bb 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -253,23 +253,13 @@ gfxFontEntry::TryGetSVGData() FallibleTArray svgTable; rv = GetFontTable(TRUETYPE_TAG('S', 'V', 'G', ' '), svgTable); - if (NS_FAILED(rv)) { - return false; - } + NS_ENSURE_SUCCESS(rv, false); - nsAutoPtr - svgGlyphs(gfxSVGGlyphs::ParseFromBuffer(svgTable.Elements(), - svgTable.Length())); + FallibleTArray cmapTable; + rv = GetFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'), cmapTable); + NS_ENSURE_SUCCESS(rv, false); - if (svgGlyphs) { - FallibleTArray cmapTable; - nsresult rv = GetFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'), cmapTable); - NS_ENSURE_SUCCESS(rv, false); - - if (svgGlyphs->Init(this, cmapTable)) { - mSVGGlyphs = svgGlyphs.forget(); - } - } + mSVGGlyphs = new gfxSVGGlyphs(svgTable, cmapTable); } return !!mSVGGlyphs; diff --git a/gfx/thebes/gfxSVGGlyphs.cpp b/gfx/thebes/gfxSVGGlyphs.cpp index 19b4af7d4030..11c70b8f3c96 100644 --- a/gfx/thebes/gfxSVGGlyphs.cpp +++ b/gfx/thebes/gfxSVGGlyphs.cpp @@ -65,6 +65,7 @@ #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml") #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8") +typedef mozilla::dom::Element Element; mozilla::gfx::UserDataKey gfxTextObjectPaint::sUserDataKey; @@ -72,102 +73,139 @@ const float gfxSVGGlyphs::SVG_UNITS_PER_EM = 1000.0f; const gfxRGBA SimpleTextObjectPaint::sZero = gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f); -/* static */ gfxSVGGlyphs* -gfxSVGGlyphs::ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen) +gfxSVGGlyphs::gfxSVGGlyphs(FallibleTArray& aSVGTable, + const FallibleTArray& aCmapTable) { - nsCOMPtr doc; - nsresult rv = ParseDocument(aBuffer, aBufLen, getter_AddRefs(doc)); - NS_ENSURE_SUCCESS(rv, nullptr); + mSVGData.SwapElements(aSVGTable); - doc->SetIsBeingUsedAsImage(); + mHeader = reinterpret_cast(mSVGData.Elements()); + UnmangleHeaders(); - nsAutoPtr result(new gfxSVGGlyphs()); + mGlyphDocs.Init(); + mGlyphIdMap.Init(); + mCmapData = aCmapTable; +} + +void +gfxSVGGlyphs::UnmangleHeaders() +{ + mHeader->mIndexLength = NS_SWAP16(mHeader->mIndexLength); + + mIndex = reinterpret_cast(mSVGData.Elements() + sizeof(Header)); + + for (uint16_t i = 0; i < mHeader->mIndexLength; i++) { + mIndex[i].mStartGlyph = NS_SWAP16(mIndex[i].mStartGlyph); + mIndex[i].mEndGlyph = NS_SWAP16(mIndex[i].mEndGlyph); + mIndex[i].mDocOffset = NS_SWAP32(mIndex[i].mDocOffset); + mIndex[i].mDocLength = NS_SWAP32(mIndex[i].mDocLength); + } +} + +/* + * Comparison operator for finding a range containing a given glyph ID. Simply + * checks whether |key| is less (greater) than every element of |range|, in + * which case return |key| < |range| (|key| > |range|). Otherwise |key| is in + * |range|, in which case return equality. + * The total ordering here is guaranteed by + * (1) the index ranges being disjoint; and + * (2) the (sole) key always being a singleton, so intersection => containment + * (note that this is wrong if we have more than one intersection or two + * sets intersecting of size > 1 -- so... don't do that) + */ +/* static */ int +gfxSVGGlyphs::CompareIndexEntries(const void *_key, const void *_entry) +{ + const uint32_t key = *(uint32_t*)_key; + const IndexEntry *entry = (const IndexEntry*)_entry; + + if (key < entry->mStartGlyph) return -1; + if (key >= entry->mEndGlyph) return 1; + return 0; +} + +gfxSVGGlyphsDocument * +gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId) +{ + IndexEntry *entry = (IndexEntry*)bsearch(&aGlyphId, mIndex, + mHeader->mIndexLength, + sizeof(IndexEntry), + CompareIndexEntries); + if (!entry) { + return nullptr; + } + + gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset); + + if (!result) { + result = new gfxSVGGlyphsDocument(mSVGData.Elements() + entry->mDocOffset, + entry->mDocLength, mCmapData); + mGlyphDocs.Put(entry->mDocOffset, result); + } + + return result; +} + +nsresult +gfxSVGGlyphsDocument::SetupPresentation() +{ + mDocument->SetIsBeingUsedAsImage(); nsCOMPtr catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); nsXPIDLCString contractId; - rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId)); - NS_ENSURE_SUCCESS(rv, nullptr); + nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId)); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr docLoaderFactory = do_GetService(contractId); NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory"); nsCOMPtr viewer; - rv = docLoaderFactory->CreateInstanceForDocument(nullptr, doc, nullptr, getter_AddRefs(viewer)); - NS_ENSURE_SUCCESS(rv, nullptr); + rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer)); + NS_ENSURE_SUCCESS(rv, rv); rv = viewer->Init(nullptr, nsIntRect(0, 0, 1000, 1000)); if (NS_SUCCEEDED(rv)) { rv = viewer->Open(nullptr, nullptr); - NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr presShell; rv = viewer->GetPresShell(getter_AddRefs(presShell)); - NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_SUCCESS(rv, rv); presShell->GetPresContext()->SetIsGlyph(true); if (!presShell->DidInitialReflow()) { nsRect rect = presShell->GetPresContext()->GetVisibleArea(); rv = presShell->InitialReflow(rect.width, rect.height); - NS_ENSURE_SUCCESS(rv, nullptr); + NS_ENSURE_SUCCESS(rv, rv); } - doc->FlushPendingNotifications(Flush_Layout); + mDocument->FlushPendingNotifications(Flush_Layout); - result->mViewer = viewer; - result->mPresShell = presShell; - result->mDocument = doc; + mViewer = viewer; + mPresShell = presShell; - return result.forget(); -} - -/** - * Initialize the glyphid -> glyph map. - * @param aFontEntry The owning font entry. Used for resolving glyphchar attributes. - * @param aCmapTable Buffer containing the raw cmap table data - */ -bool -gfxSVGGlyphs::Init(const gfxFontEntry *aFontEntry, - const FallibleTArray &aCmapTable) -{ - NS_ASSERTION(mDocument, "Document not set"); - - if (!mGlyphIdMap.Init()) { - return false; - } - - Element *svgRoot = mDocument->GetRootElement(); - if (!svgRoot) { - return false; - } - - FindGlyphElements(svgRoot, aFontEntry, aCmapTable); - - return true; + return NS_OK; } /** * Walk the DOM tree to find all glyph elements and insert them into the lookup * table * @param aElem The element to search from - * @param aFontEntry The font entry owning this document, used for resolving - * glyphchar attributes. * @param aCmapTable Buffer containing the raw cmap table data */ void -gfxSVGGlyphs::FindGlyphElements(Element *aElem, - const gfxFontEntry *aFontEntry, - const FallibleTArray &aCmapTable) +gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem, + const FallibleTArray &aCmapTable) { for (nsIContent *child = aElem->GetLastChild(); child; child = child->GetPreviousSibling()) { if (!child->IsElement()) { continue; } - FindGlyphElements(child->AsElement(), aFontEntry, aCmapTable); + FindGlyphElements(child->AsElement(), aCmapTable); } - InsertGlyphChar(aElem, aFontEntry, aCmapTable); + InsertGlyphChar(aElem, aCmapTable); InsertGlyphId(aElem); } @@ -205,14 +243,60 @@ gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace return nsContentUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult); } +Element * +gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId) +{ + Element *elem; + + if (!mGlyphIdMap.Get(aGlyphId, &elem)) { + elem = nullptr; + if (gfxSVGGlyphsDocument *set = FindOrCreateGlyphsDocument(aGlyphId)) { + elem = set->GetGlyphElement(aGlyphId); + } + mGlyphIdMap.Put(aGlyphId, elem); + } + + return elem; +} + bool gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId) { - Element *glyph = mGlyphIdMap.Get(aGlyphId); - return !!glyph; + return !!GetGlyphElement(aGlyphId); } -nsresult +Element * +gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId) +{ + return mGlyphIdMap.Get(aGlyphId); +} + +gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(uint8_t *aBuffer, uint32_t aBufLen, + const FallibleTArray& aCmapTable) +{ + mGlyphIdMap.Init(); + ParseDocument(aBuffer, aBufLen); + if (!mDocument) { + NS_WARNING("Could not parse SVG glyphs document"); + return; + } + + Element *root = mDocument->GetRootElement(); + if (!root) { + NS_WARNING("Could not parse SVG glyphs document"); + return; + } + + nsresult rv = SetupPresentation(); + if (NS_FAILED(rv)) { + NS_WARNING("Couldn't setup presentation for SVG glyphs document"); + return; + } + + FindGlyphElements(root, aCmapTable); +} + +static nsresult CreateBufferedStream(uint8_t *aBuffer, uint32_t aBufLen, nsCOMPtr &aResult) { @@ -234,14 +318,11 @@ CreateBufferedStream(uint8_t *aBuffer, uint32_t aBufLen, return NS_OK; } -/* static */ nsresult -gfxSVGGlyphs::ParseDocument(uint8_t *aBuffer, uint32_t aBufLen, - nsIDocument **aResult) +nsresult +gfxSVGGlyphsDocument::ParseDocument(uint8_t *aBuffer, uint32_t aBufLen) { // Mostly pulled from nsDOMParser::ParseFromStream - *aResult = nullptr; - nsCOMPtr stream; nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream); NS_ENSURE_SUCCESS(rv, rv); @@ -308,13 +389,13 @@ gfxSVGGlyphs::ParseDocument(uint8_t *aBuffer, uint32_t aBufLen, rv = listener->OnStopRequest(channel, nullptr /* aContext */, status); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); - document.swap(*aResult); + document.swap(mDocument); return NS_OK; } void -gfxSVGGlyphs::InsertGlyphId(Element *aGlyphElement) +gfxSVGGlyphsDocument::InsertGlyphId(Element *aGlyphElement) { nsAutoString glyphIdStr; if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::glyphid, glyphIdStr)) { @@ -332,9 +413,8 @@ gfxSVGGlyphs::InsertGlyphId(Element *aGlyphElement) } void -gfxSVGGlyphs::InsertGlyphChar(Element *aGlyphElement, - const gfxFontEntry *aFontEntry, - const FallibleTArray &aCmapTable) +gfxSVGGlyphsDocument::InsertGlyphChar(Element *aGlyphElement, + const FallibleTArray &aCmapTable) { nsAutoString glyphChar; if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::glyphchar, glyphChar)) { diff --git a/gfx/thebes/gfxSVGGlyphs.h b/gfx/thebes/gfxSVGGlyphs.h index 41535c4d8412..f5e34e0d099f 100644 --- a/gfx/thebes/gfxSVGGlyphs.h +++ b/gfx/thebes/gfxSVGGlyphs.h @@ -43,6 +43,7 @@ #include "nsAutoPtr.h" #include "nsIContentViewer.h" #include "nsIPresShell.h" +#include "nsClassHashtable.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" #include "nsIDocument.h" @@ -52,10 +53,57 @@ /** - * Used by gfxFontEntry for OpenType fonts which contains SVG tables to parse - * the SVG document and store and render SVG glyphs + * Wraps an SVG document contained in the SVG table of an OpenType font. + * There may be multiple SVG documents in an SVG table which we lazily parse + * so we have an instance of this class for every document in the SVG table + * which contains a glyph ID which has been used + * Finds and looks up elements contained in the SVG document which have glyph + * mappings to be drawn by gfxSVGGlyphs */ -class gfxSVGGlyphs { +class gfxSVGGlyphsDocument +{ + typedef mozilla::dom::Element Element; + typedef gfxFont::DrawMode DrawMode; + +public: + gfxSVGGlyphsDocument(uint8_t *aBuffer, uint32_t aBufLen, + const FallibleTArray& aCmapTable); + + Element *GetGlyphElement(uint32_t aGlyphId); + + ~gfxSVGGlyphsDocument() { + if (mViewer) { + mViewer->Destroy(); + } + } + +private: + nsresult ParseDocument(uint8_t *aBuffer, uint32_t aBufLen); + + nsresult SetupPresentation(); + + void FindGlyphElements(Element *aElement, + const FallibleTArray &aCmapTable); + + void InsertGlyphId(Element *aGlyphElement); + void InsertGlyphChar(Element *aGlyphElement, + const FallibleTArray &aCmapTable); + + nsCOMPtr mDocument; + nsCOMPtr mViewer; + nsCOMPtr mPresShell; + + nsBaseHashtable mGlyphIdMap; +}; + +/** + * Used by |gfxFontEntry| to represent the SVG table of an OpenType font. + * Handles lazy parsing of the SVG documents in the table, looking up SVG glyphs + * and rendering SVG glyphs. + * Each |gfxFontEntry| owns at most one |gfxSVGGlyphs| instance. + */ +class gfxSVGGlyphs +{ private: typedef mozilla::dom::Element Element; typedef gfxFont::DrawMode DrawMode; @@ -63,42 +111,69 @@ private: public: static const float SVG_UNITS_PER_EM; - static gfxSVGGlyphs* ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen); + /** + * @param aSVGTable The SVG table from the OpenType font + * @param aCmapTable The CMAP table from the OpenType font + */ + gfxSVGGlyphs(FallibleTArray& aSVGTable, + const FallibleTArray& aCmapTable); + /** + * Big- to little-endian conversion for headers + */ + void UnmangleHeaders(); + + /** + * Find the |gfxSVGGlyphsDocument| containing an SVG glyph for |aGlyphId|. + * If |aGlyphId| does not map to an SVG document, return null. + * If a |gfxSVGGlyphsDocument| has not been created for the document, create one. + */ + gfxSVGGlyphsDocument *FindOrCreateGlyphsDocument(uint32_t aGlyphId); + + /** + * Return true iff there is an SVG glyph for |aGlyphId| + */ bool HasSVGGlyph(uint32_t aGlyphId); + /** + * Render the SVG glyph for |aGlyphId| + * @param aDrawMode Whether to fill or stroke or both; see gfxFont::DrawMode + * @param aObjectPaint Information on outer text object paints. + * See |gfxTextObjectPaint|. + */ bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode, gfxTextObjectPaint *aObjectPaint); + /** + * Get the extents for the SVG glyph associated with |aGlyphId| + * @param aSVGToAppSpace The matrix mapping the SVG glyph space to the + * target context space + */ bool GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace, gfxRect *aResult); - bool Init(const gfxFontEntry *aFont, - const FallibleTArray &aCmapTable); - - ~gfxSVGGlyphs() { - mViewer->Destroy(); - } - private: - gfxSVGGlyphs() { - } - - static nsresult ParseDocument(uint8_t *aBuffer, uint32_t aBufLen, - nsIDocument **aResult); - - void FindGlyphElements(Element *aElement, const gfxFontEntry *aFontEntry, - const FallibleTArray &aCmapTable); - - void InsertGlyphId(Element *aGlyphElement); - void InsertGlyphChar(Element *aGlyphElement, const gfxFontEntry *aFontEntry, - const FallibleTArray &aCmapTable); - - nsCOMPtr mDocument; - nsCOMPtr mViewer; - nsCOMPtr mPresShell; + Element *GetGlyphElement(uint32_t aGlyphId); + nsClassHashtable mGlyphDocs; nsBaseHashtable mGlyphIdMap; + + FallibleTArray mSVGData; + FallibleTArray mCmapData; + + struct Header { + uint16_t mVersion; + uint16_t mIndexLength; + } *mHeader; + + struct IndexEntry { + uint16_t mStartGlyph; + uint16_t mEndGlyph; + uint32_t mDocOffset; + uint32_t mDocLength; + } *mIndex; + + static int CompareIndexEntries(const void *_a, const void *_b); }; /**