gfx/thebes/gfxSVGGlyphs.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "gfxSVGGlyphs.h"
     7 #include "nsError.h"
     8 #include "nsIDOMDocument.h"
     9 #include "nsString.h"
    10 #include "nsIDocument.h"
    11 #include "nsICategoryManager.h"
    12 #include "nsIDocumentLoaderFactory.h"
    13 #include "nsIContentViewer.h"
    14 #include "nsIStreamListener.h"
    15 #include "nsServiceManagerUtils.h"
    16 #include "nsIPresShell.h"
    17 #include "nsNetUtil.h"
    18 #include "nsIInputStream.h"
    19 #include "nsStringStream.h"
    20 #include "nsStreamUtils.h"
    21 #include "nsIPrincipal.h"
    22 #include "mozilla/dom/Element.h"
    23 #include "nsSVGUtils.h"
    24 #include "nsIScriptSecurityManager.h"
    25 #include "nsHostObjectProtocolHandler.h"
    26 #include "nsContentUtils.h"
    27 #include "gfxFont.h"
    28 #include "nsSMILAnimationController.h"
    29 #include "gfxContext.h"
    30 #include "gfxColor.h"
    31 #include "harfbuzz/hb.h"
    33 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
    34 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
    36 using namespace mozilla;
    38 typedef mozilla::dom::Element Element;
    40 mozilla::gfx::UserDataKey gfxTextContextPaint::sUserDataKey;
    42 const gfxRGBA SimpleTextContextPaint::sZero = gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f);
    44 gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
    45     : mSVGData(aSVGTable)
    46     , mFontEntry(aFontEntry)
    47 {
    48     unsigned int length;
    49     const char* svgData = hb_blob_get_data(mSVGData, &length);
    50     mHeader = reinterpret_cast<const Header*>(svgData);
    51     mDocIndex = nullptr;
    53     if (sizeof(Header) <= length && uint16_t(mHeader->mVersion) == 0 &&
    54         uint64_t(mHeader->mDocIndexOffset) + 2 <= length) {
    55         const DocIndex* docIndex = reinterpret_cast<const DocIndex*>
    56             (svgData + mHeader->mDocIndexOffset);
    57         // Limit the number of documents to avoid overflow
    58         if (uint64_t(mHeader->mDocIndexOffset) + 2 +
    59                 uint16_t(docIndex->mNumEntries) * sizeof(IndexEntry) <= length) {
    60             mDocIndex = docIndex;
    61         }
    62     }
    63 }
    65 gfxSVGGlyphs::~gfxSVGGlyphs()
    66 {
    67     hb_blob_destroy(mSVGData);
    68 }
    70 void
    71 gfxSVGGlyphs::DidRefresh()
    72 {
    73     mFontEntry->NotifyGlyphsChanged();
    74 }
    76 /*
    77  * Comparison operator for finding a range containing a given glyph ID. Simply
    78  *   checks whether |key| is less (greater) than every element of |range|, in
    79  *   which case return |key| < |range| (|key| > |range|). Otherwise |key| is in
    80  *   |range|, in which case return equality.
    81  * The total ordering here is guaranteed by
    82  *   (1) the index ranges being disjoint; and
    83  *   (2) the (sole) key always being a singleton, so intersection => containment
    84  *       (note that this is wrong if we have more than one intersection or two 
    85  *        sets intersecting of size > 1 -- so... don't do that)
    86  */
    87 /* static */ int
    88 gfxSVGGlyphs::CompareIndexEntries(const void *aKey, const void *aEntry)
    89 {
    90     const uint32_t key = *(uint32_t*)aKey;
    91     const IndexEntry *entry = (const IndexEntry*)aEntry;
    93     if (key < uint16_t(entry->mStartGlyph)) {
    94         return -1;
    95     }
    96     if (key > uint16_t(entry->mEndGlyph)) {
    97         return 1;
    98     }
    99     return 0;
   100 }
   102 gfxSVGGlyphsDocument *
   103 gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
   104 {
   105     if (!mDocIndex) {
   106         // Invalid table
   107         return nullptr;
   108     }
   110     IndexEntry *entry = (IndexEntry*)bsearch(&aGlyphId, mDocIndex->mEntries,
   111                                              uint16_t(mDocIndex->mNumEntries),
   112                                              sizeof(IndexEntry),
   113                                              CompareIndexEntries);
   114     if (!entry) {
   115         return nullptr;
   116     }
   118     gfxSVGGlyphsDocument *result = mGlyphDocs.Get(entry->mDocOffset);
   120     if (!result) {
   121         unsigned int length;
   122         const uint8_t *data = (const uint8_t*)hb_blob_get_data(mSVGData, &length);
   123         if (entry->mDocOffset > 0 &&
   124             uint64_t(mHeader->mDocIndexOffset) + entry->mDocOffset + entry->mDocLength <= length) {
   125             result = new gfxSVGGlyphsDocument(data + mHeader->mDocIndexOffset + entry->mDocOffset,
   126                                               entry->mDocLength, this);
   127             mGlyphDocs.Put(entry->mDocOffset, result);
   128         }
   129     }
   131     return result;
   132 }
   134 nsresult
   135 gfxSVGGlyphsDocument::SetupPresentation()
   136 {
   137     nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   138     nsXPIDLCString contractId;
   139     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
   140     NS_ENSURE_SUCCESS(rv, rv);
   142     nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = do_GetService(contractId);
   143     NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
   145     nsCOMPtr<nsIContentViewer> viewer;
   146     rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer));
   147     NS_ENSURE_SUCCESS(rv, rv);
   149     rv = viewer->Init(nullptr, nsIntRect(0, 0, 1000, 1000));
   150     if (NS_SUCCEEDED(rv)) {
   151         rv = viewer->Open(nullptr, nullptr);
   152         NS_ENSURE_SUCCESS(rv, rv);
   153     }
   155     nsCOMPtr<nsIPresShell> presShell;
   156     rv = viewer->GetPresShell(getter_AddRefs(presShell));
   157     NS_ENSURE_SUCCESS(rv, rv);
   158     nsPresContext* presContext = presShell->GetPresContext();
   159     presContext->SetIsGlyph(true);
   161     if (!presShell->DidInitialize()) {
   162         nsRect rect = presContext->GetVisibleArea();
   163         rv = presShell->Initialize(rect.width, rect.height);
   164         NS_ENSURE_SUCCESS(rv, rv);
   165     }
   167     mDocument->FlushPendingNotifications(Flush_Layout);
   169     nsSMILAnimationController* controller = mDocument->GetAnimationController();
   170     if (controller) {
   171       controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
   172     }
   173     mDocument->SetImagesNeedAnimating(true);
   175     mViewer = viewer;
   176     mPresShell = presShell;
   177     mPresShell->AddPostRefreshObserver(this);
   179     return NS_OK;
   180 }
   182 void
   183 gfxSVGGlyphsDocument::DidRefresh()
   184 {
   185     mOwner->DidRefresh();
   186 }
   188 /**
   189  * Walk the DOM tree to find all glyph elements and insert them into the lookup
   190  * table
   191  * @param aElem The element to search from
   192  */
   193 void
   194 gfxSVGGlyphsDocument::FindGlyphElements(Element *aElem)
   195 {
   196     for (nsIContent *child = aElem->GetLastChild(); child;
   197             child = child->GetPreviousSibling()) {
   198         if (!child->IsElement()) {
   199             continue;
   200         }
   201         FindGlyphElements(child->AsElement());
   202     }
   204     InsertGlyphId(aElem);
   205 }
   207 /**
   208  * If there exists an SVG glyph with the specified glyph id, render it and return true
   209  * If no such glyph exists, or in the case of an error return false
   210  * @param aContext The thebes aContext to draw to
   211  * @param aGlyphId The glyph id
   212  * @param aDrawMode Whether to fill or stroke or both (see |DrawMode|)
   213  * @return true iff rendering succeeded
   214  */
   215 bool
   216 gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
   217                           DrawMode aDrawMode, gfxTextContextPaint *aContextPaint)
   218 {
   219     if (aDrawMode == DrawMode::GLYPH_PATH) {
   220         return false;
   221     }
   223     gfxContextAutoSaveRestore aContextRestorer(aContext);
   225     Element *glyph = mGlyphIdMap.Get(aGlyphId);
   226     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
   228     return nsSVGUtils::PaintSVGGlyph(glyph, aContext, aDrawMode, aContextPaint);
   229 }
   231 bool
   232 gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
   233                               gfxRect *aResult)
   234 {
   235     Element *glyph = mGlyphIdMap.Get(aGlyphId);
   236     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
   238     return nsSVGUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
   239 }
   241 Element *
   242 gfxSVGGlyphs::GetGlyphElement(uint32_t aGlyphId)
   243 {
   244     Element *elem;
   246     if (!mGlyphIdMap.Get(aGlyphId, &elem)) {
   247         elem = nullptr;
   248         if (gfxSVGGlyphsDocument *set = FindOrCreateGlyphsDocument(aGlyphId)) {
   249             elem = set->GetGlyphElement(aGlyphId);
   250         }
   251         mGlyphIdMap.Put(aGlyphId, elem);
   252     }
   254     return elem;
   255 }
   257 bool
   258 gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
   259 {
   260     return !!GetGlyphElement(aGlyphId);
   261 }
   263 Element *
   264 gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
   265 {
   266     return mGlyphIdMap.Get(aGlyphId);
   267 }
   269 gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
   270                                            uint32_t aBufLen,
   271                                            gfxSVGGlyphs *aSVGGlyphs)
   272     : mOwner(aSVGGlyphs)
   273 {
   274     ParseDocument(aBuffer, aBufLen);
   275     if (!mDocument) {
   276         NS_WARNING("Could not parse SVG glyphs document");
   277         return;
   278     }
   280     Element *root = mDocument->GetRootElement();
   281     if (!root) {
   282         NS_WARNING("Could not parse SVG glyphs document");
   283         return;
   284     }
   286     nsresult rv = SetupPresentation();
   287     if (NS_FAILED(rv)) {
   288         NS_WARNING("Couldn't setup presentation for SVG glyphs document");
   289         return;
   290     }
   292     FindGlyphElements(root);
   293 }
   295 gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
   296 {
   297     if (mDocument) {
   298         nsSMILAnimationController* controller = mDocument->GetAnimationController();
   299         if (controller) {
   300             controller->Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
   301         }
   302     }
   303     if (mPresShell) {
   304         mPresShell->RemovePostRefreshObserver(this);
   305     }
   306     if (mViewer) {
   307         mViewer->Destroy();
   308     }
   309 }
   311 static nsresult
   312 CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
   313                      nsCOMPtr<nsIInputStream> &aResult)
   314 {
   315     nsCOMPtr<nsIInputStream> stream;
   316     nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
   317                                         reinterpret_cast<const char *>(aBuffer),
   318                                         aBufLen, NS_ASSIGNMENT_DEPEND);
   319     NS_ENSURE_SUCCESS(rv, rv);
   321     nsCOMPtr<nsIInputStream> aBufferedStream;
   322     if (!NS_InputStreamIsBuffered(stream)) {
   323         rv = NS_NewBufferedInputStream(getter_AddRefs(aBufferedStream), stream, 4096);
   324         NS_ENSURE_SUCCESS(rv, rv);
   325         stream = aBufferedStream;
   326     }
   328     aResult = stream;
   330     return NS_OK;
   331 }
   333 nsresult
   334 gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen)
   335 {
   336     // Mostly pulled from nsDOMParser::ParseFromStream
   338     nsCOMPtr<nsIInputStream> stream;
   339     nsresult rv = CreateBufferedStream(aBuffer, aBufLen, stream);
   340     NS_ENSURE_SUCCESS(rv, rv);
   342     nsCOMPtr<nsIURI> uri;
   343     nsHostObjectProtocolHandler::GenerateURIString(NS_LITERAL_CSTRING(FONTTABLEURI_SCHEME),
   344                                                    mSVGGlyphsDocumentURI);
   346     rv = NS_NewURI(getter_AddRefs(uri), mSVGGlyphsDocumentURI);
   347     NS_ENSURE_SUCCESS(rv, rv);
   349     nsCOMPtr<nsIPrincipal> principal;
   350     nsContentUtils::GetSecurityManager()->
   351         GetNoAppCodebasePrincipal(uri, getter_AddRefs(principal));
   353     nsCOMPtr<nsIDOMDocument> domDoc;
   354     rv = NS_NewDOMDocument(getter_AddRefs(domDoc),
   355                            EmptyString(),   // aNamespaceURI
   356                            EmptyString(),   // aQualifiedName
   357                            nullptr,          // aDoctype
   358                            uri, uri, principal,
   359                            false,           // aLoadedAsData
   360                            nullptr,          // aEventObject
   361                            DocumentFlavorSVG);
   362     NS_ENSURE_SUCCESS(rv, rv);
   364     nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
   365     if (!document) {
   366         return NS_ERROR_FAILURE;
   367     }
   369     nsCOMPtr<nsIChannel> channel;
   370     rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, nullptr /* stream */,
   371                                   SVG_CONTENT_TYPE, UTF8_CHARSET);
   372     NS_ENSURE_SUCCESS(rv, rv);
   374     channel->SetOwner(principal);
   376     // Set this early because various decisions during page-load depend on it.
   377     document->SetIsBeingUsedAsImage();
   378     document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
   380     nsCOMPtr<nsIStreamListener> listener;
   381     rv = document->StartDocumentLoad("external-resource", channel,
   382                                      nullptr,    // aLoadGroup
   383                                      nullptr,    // aContainer
   384                                      getter_AddRefs(listener),
   385                                      true /* aReset */);
   386     if (NS_FAILED(rv) || !listener) {
   387         return NS_ERROR_FAILURE;
   388     }
   390     rv = listener->OnStartRequest(channel, nullptr /* aContext */);
   391     if (NS_FAILED(rv)) {
   392         channel->Cancel(rv);
   393     }
   395     nsresult status;
   396     channel->GetStatus(&status);
   397     if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) {
   398         rv = listener->OnDataAvailable(channel, nullptr /* aContext */, stream, 0, aBufLen);
   399         if (NS_FAILED(rv)) {
   400             channel->Cancel(rv);
   401         }
   402         channel->GetStatus(&status);
   403     }
   405     rv = listener->OnStopRequest(channel, nullptr /* aContext */, status);
   406     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
   408     document.swap(mDocument);
   410     return NS_OK;
   411 }
   413 void
   414 gfxSVGGlyphsDocument::InsertGlyphId(Element *aGlyphElement)
   415 {
   416     nsAutoString glyphIdStr;
   417     static const uint32_t glyphPrefixLength = 5;
   418     // The maximum glyph ID is 65535 so the maximum length of the numeric part
   419     // is 5.
   420     if (!aGlyphElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, glyphIdStr) ||
   421         !StringBeginsWith(glyphIdStr, NS_LITERAL_STRING("glyph")) ||
   422         glyphIdStr.Length() > glyphPrefixLength + 5) {
   423         return;
   424     }
   426     uint32_t id = 0;
   427     for (uint32_t i = glyphPrefixLength; i < glyphIdStr.Length(); ++i) {
   428       char16_t ch = glyphIdStr.CharAt(i);
   429       if (ch < '0' || ch > '9') {
   430         return;
   431       }
   432       if (ch == '0' && i == glyphPrefixLength) {
   433         return;
   434       }
   435       id = id * 10 + (ch - '0');
   436     }
   438     mGlyphIdMap.Put(id, aGlyphElement);
   439 }
   441 void
   442 gfxTextContextPaint::InitStrokeGeometry(gfxContext *aContext,
   443                                         float devUnitsPerSVGUnit)
   444 {
   445     mStrokeWidth = aContext->CurrentLineWidth() / devUnitsPerSVGUnit;
   446     aContext->CurrentDash(mDashes, &mDashOffset);
   447     for (uint32_t i = 0; i < mDashes.Length(); i++) {
   448         mDashes[i] /= devUnitsPerSVGUnit;
   449     }
   450     mDashOffset /= devUnitsPerSVGUnit;
   451 }

mercurial