Bug 719286 - Include SVG glyphs when calculating glyph extents r=jfkthame
authorEdwin Flores <eflores@mozilla.com>
Thu, 06 Sep 2012 16:58:46 +1200
changeset 111300 781783d7bc18872a40a451daf9d95672a16011b3
parent 111299 ec84c72f3c5e7a0bd80a78b8f436cf2f1339a1b5
child 111301 f0a81d96a7ba32c44a5957fb3e05dff466ad6d5a
push idunknown
push userunknown
push dateunknown
reviewersjfkthame
bugs719286
milestone18.0a1
Bug 719286 - Include SVG glyphs when calculating glyph extents r=jfkthame
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxSVGGlyphs.cpp
gfx/thebes/gfxSVGGlyphs.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2085,16 +2085,19 @@ public:
                                         int32_t& aOutEndOffset);
 
   static nsIEditor* GetHTMLEditor(nsPresContext* aPresContext);
 
   static bool PaintSVGGlyph(Element *aElement, gfxContext *aContext,
                             gfxFont::DrawMode aDrawMode,
                             gfxTextObjectPaint *aObjectPaint);
 
+  static bool GetSVGGlyphExtents(Element *aElement, const gfxMatrix& aSVGToAppSpace,
+                                 gfxRect *aResult);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6973,16 +6973,41 @@ nsContentUtils::PaintSVGGlyph(Element *a
   context.AddUserData(&gfxTextObjectPaint::sUserDataKey, aObjectPaint, nullptr);
 
   nsresult rv = displayFrame->PaintSVG(&context, nullptr);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
+/* static */
+bool
+nsContentUtils::GetSVGGlyphExtents(Element *aElement, const gfxMatrix& aSVGToAppSpace,
+                                   gfxRect *aResult)
+{
+  nsIFrame *frame = aElement->GetPrimaryFrame();
+  if (!frame) {
+    NS_WARNING("No frame for SVG glyph");
+    return false;
+  }
+
+  nsISVGChildFrame *displayFrame = do_QueryFrame(frame);
+  if (!displayFrame) {
+    NS_WARNING("Non SVG frame for SVG glyph");
+    return false;
+  }
+
+  *aResult = displayFrame->GetBBoxContribution(aSVGToAppSpace,
+      nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
+      nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
+      nsSVGUtils::eBBoxIncludeMarkers);
+
+  return true;
+}
+
 // static
 void
 nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
                                           Element* aRoot,
                                           int32_t& aOutStartOffset,
                                           int32_t& aOutEndOffset)
 {
   MOZ_ASSERT(aSelection && aRoot);
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -206,16 +206,33 @@ gfxFontEntry::FindOrMakeFont(const gfxFo
 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.");
+
+    gfxContextAutoSaveRestore matrixRestore(aContext);
+    cairo_matrix_t fontMatrix;
+    cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
+
+    gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
+    svgToAppSpace.Scale(1.0f / gfxSVGGlyphs::SVG_UNITS_PER_EM,
+                        1.0f / gfxSVGGlyphs::SVG_UNITS_PER_EM);
+
+    return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
+}
+
+bool
 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
                              int aDrawMode, gfxTextObjectPaint *aObjectPaint)
 {
     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
     return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, gfxFont::DrawMode(aDrawMode),
                                    aObjectPaint);
 }
 
@@ -2058,23 +2075,21 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
 
     *aPt = gfxPoint(x, y);
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
                         uint32_t aGlyphId, gfxTextObjectPaint *aObjectPaint)
 {
-    static const float SVG_UNITS_PER_EM = 1000.0;
-
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
         return false;
     }
 
-    const float devUnitsPerSVGUnit = GetStyle()->size / SVG_UNITS_PER_EM;
+    const gfxFloat devUnitsPerSVGUnit = GetStyle()->size / gfxSVGGlyphs::SVG_UNITS_PER_EM;
     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
 
     aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
     aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
 
     return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aDrawMode,
                                           aObjectPaint);
 }
@@ -2586,25 +2601,24 @@ gfxFont::GetOrCreateGlyphExtents(uint32_
     }
     return glyphExtents;
 }
 
 void
 gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight,
                            gfxGlyphExtents *aExtents)
 {
-    gfxMatrix matrix = aContext->CurrentMatrix();
+    gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
     aContext->IdentityMatrix();
     cairo_glyph_t glyph;
     glyph.index = aGlyphID;
     glyph.x = 0;
     glyph.y = 0;
     cairo_text_extents_t extents;
     cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
-    aContext->SetMatrix(matrix);
 
     const Metrics& fontMetrics = GetMetrics();
     uint32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
     if (!aNeedTight && extents.x_bearing >= 0 &&
         extents.y_bearing >= -fontMetrics.maxAscent &&
         extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
         uint32_t appUnitsWidth =
             uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
@@ -2614,19 +2628,29 @@ gfxFont::SetupGlyphExtents(gfxContext *a
         }
     }
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     if (!aNeedTight) {
         ++gGlyphExtentsSetupFallBackToTight;
     }
 #endif
 
-    double d2a = appUnitsPerDevUnit;
+    gfxFloat d2a = appUnitsPerDevUnit;
     gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
                    extents.width*d2a, extents.height*d2a);
+
+    gfxRect svgBounds;
+    if (mFontEntry->TryGetSVGData() &&
+        mFontEntry->HasSVGGlyph(aGlyphID) &&
+        mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
+
+        bounds = bounds.Union(gfxRect(svgBounds.x * d2a, svgBounds.y * d2a,
+                                      svgBounds.width * d2a, svgBounds.height * d2a));
+    }
+
     aExtents->SetTightGlyphExtents(aGlyphID, bounds);
 }
 
 // Try to initialize font metrics by reading sfnt tables directly;
 // set mIsValid=TRUE and return TRUE on success.
 // Return FALSE if the gfxFontEntry subclass does not
 // implement GetFontTable(), or for non-sfnt fonts where tables are
 // not available.
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -276,16 +276,18 @@ public:
     virtual bool SkipDuringSystemFallback() { return false; }
     virtual bool TestCharacterMap(uint32_t aCh);
     nsresult InitializeUVSMap();
     uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS);
     virtual nsresult ReadCMAP();
 
     bool TryGetSVGData();
     bool HasSVGGlyph(uint32_t aGlyphId);
+    bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
+                            gfxRect *aResult);
     bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
                         gfxTextObjectPaint *aObjectPaint);
 
     virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
         return true;
     }
     virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const {
         return true;
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -63,16 +63,18 @@
 #include "Element.h"
 
 #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
 #define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
 
 
 mozilla::gfx::UserDataKey gfxTextObjectPaint::sUserDataKey;
 
+const float gfxSVGGlyphs::SVG_UNITS_PER_EM = 1000.0f;
+
 /* static */ gfxSVGGlyphs*
 gfxSVGGlyphs::ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen)
 {
     nsCOMPtr<nsIDocument> doc;
     nsresult rv = ParseDocument(aBuffer, aBufLen, getter_AddRefs(doc));
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     doc->SetIsBeingUsedAsImage();
@@ -187,16 +189,26 @@ gfxSVGGlyphs::RenderGlyph(gfxContext *aC
 
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
 
     return nsContentUtils::PaintSVGGlyph(glyph, aContext, aDrawMode, aObjectPaint);
 }
 
 bool
+gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
+                              gfxRect *aResult)
+{
+    Element *glyph = mGlyphIdMap.Get(aGlyphId);
+    NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
+
+    return nsContentUtils::GetSVGGlyphExtents(glyph, aSVGToAppSpace, aResult);
+}
+
+bool
 gfxSVGGlyphs::HasSVGGlyph(uint32_t aGlyphId)
 {
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     return !!glyph;
 }
 
 nsresult
 CreateBufferedStream(uint8_t *aBuffer, uint32_t aBufLen,
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -56,23 +56,28 @@
  * the SVG document and store and render SVG glyphs
  */
 class gfxSVGGlyphs {
 private:
     typedef mozilla::dom::Element Element;
     typedef gfxFont::DrawMode DrawMode;
 
 public:
+    static const float SVG_UNITS_PER_EM;
+
     static gfxSVGGlyphs* ParseFromBuffer(uint8_t *aBuffer, uint32_t aBufLen);
 
     bool HasSVGGlyph(uint32_t aGlyphId);
 
     bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode,
                      gfxTextObjectPaint *aObjectPaint);
 
+    bool GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
+                         gfxRect *aResult);
+
     bool Init(const gfxFontEntry *aFont,
               const FallibleTArray<uint8_t> &aCmapTable);
 
     ~gfxSVGGlyphs() {
         mViewer->Destroy();
     }
 
 private: