Back to index

lightning-sunbird  0.9+nobinonly
nsSVGCairoGlyphGeometry.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla SVG Cairo Renderer project.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Parts of this file contain code derived from the following files(s)
00022  * of the Mozilla SVG project (these parts are Copyright (C) by their
00023  * respective copyright-holders):
00024  *    layout/svg/renderer/src/gdiplus/nsSVGGDIPlusGlyphGeometry.cpp
00025  *
00026  * Contributor(s):
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsSVGCairoGlyphGeometry.h"
00044 #include "nsISVGRendererGlyphGeometry.h"
00045 #include "nsISVGCairoCanvas.h"
00046 #include "nsIDOMSVGMatrix.h"
00047 #include "nsSVGCairoRegion.h"
00048 #include "nsISVGCairoRegion.h"
00049 #include "nsISVGRendererRegion.h"
00050 #include "nsISVGGlyphGeometrySource.h"
00051 #include "nsPromiseFlatString.h"
00052 #include "nsSVGCairoGlyphMetrics.h"
00053 #include "nsISVGCairoGlyphMetrics.h"
00054 #include "nsPresContext.h"
00055 #include "nsMemory.h"
00056 #include "cairo.h"
00057 
00058 #include "nsISVGGradient.h"
00059 #include "nsSVGCairoGradient.h"
00060 #include "nsIDOMSVGRect.h"
00061 #include "nsSVGTypeCIDs.h"
00062 #include "nsIComponentManager.h"
00063 #include "nsSVGUtils.h"
00064 
00065 extern cairo_surface_t *gSVGCairoDummySurface;
00066 
00071 
00072 
00075 class nsSVGCairoGlyphGeometry : public nsISVGRendererGlyphGeometry
00076 {
00077 protected:
00078   friend nsresult NS_NewSVGCairoGlyphGeometry(nsISVGRendererGlyphGeometry **result,
00079                                                 nsISVGGlyphGeometrySource *src);
00080 
00081   nsSVGCairoGlyphGeometry();
00082   ~nsSVGCairoGlyphGeometry();
00083   nsresult Init(nsISVGGlyphGeometrySource* src);
00084 
00085   nsresult GetGlobalTransform(cairo_t *ctx, nsISVGCairoCanvas* aCanvas);
00086 
00087 public:
00088   // nsISupports interface:
00089   NS_DECL_ISUPPORTS
00090 
00091   // nsISVGRendererGlyphGeometry interface:
00092   NS_DECL_NSISVGRENDERERGLYPHGEOMETRY
00093   
00094 protected:
00095   nsCOMPtr<nsISVGGlyphGeometrySource> mSource;
00096 
00097 private:
00098   nsCOMPtr<nsISVGRendererRegion> mCoveredRegion;
00099 };
00100 
00103 //----------------------------------------------------------------------
00104 // implementation:
00105 
00106 nsSVGCairoGlyphGeometry::nsSVGCairoGlyphGeometry()
00107 {
00108 }
00109 
00110 nsSVGCairoGlyphGeometry::~nsSVGCairoGlyphGeometry()
00111 {
00112 }
00113 
00114 nsresult
00115 nsSVGCairoGlyphGeometry::Init(nsISVGGlyphGeometrySource* src)
00116 {
00117   mSource = src;
00118   return NS_OK;
00119 }
00120 
00121 
00122 nsresult
00123 NS_NewSVGCairoGlyphGeometry(nsISVGRendererGlyphGeometry **result,
00124                               nsISVGGlyphGeometrySource *src)
00125 {
00126   *result = nsnull;
00127   
00128   nsSVGCairoGlyphGeometry* gg = new nsSVGCairoGlyphGeometry();
00129   if (!gg) return NS_ERROR_OUT_OF_MEMORY;
00130 
00131   NS_ADDREF(gg);
00132 
00133   nsresult rv = gg->Init(src);
00134 
00135   if (NS_FAILED(rv)) {
00136     NS_RELEASE(gg);
00137     return rv;
00138   }
00139   
00140   *result = gg;
00141   return rv;
00142 }
00143 
00144 //----------------------------------------------------------------------
00145 // nsISupports methods:
00146 
00147 NS_IMPL_ADDREF(nsSVGCairoGlyphGeometry)
00148 NS_IMPL_RELEASE(nsSVGCairoGlyphGeometry)
00149 
00150 NS_INTERFACE_MAP_BEGIN(nsSVGCairoGlyphGeometry)
00151   NS_INTERFACE_MAP_ENTRY(nsISVGRendererGlyphGeometry)
00152   NS_INTERFACE_MAP_ENTRY(nsISupports)
00153 NS_INTERFACE_MAP_END
00154 
00155 
00156 //----------------------------------------------------------------------
00157 // nsISVGRendererGlyphGeometry methods:
00158 
00159 #define LOOP_CHARS(func) \
00160     if (!cp) { \
00161       func(ctx, NS_ConvertUCS2toUTF8(text).get()); \
00162     } else { \
00163       for (PRUint32 i=0; i<text.Length(); i++) { \
00164         /* character actually on the path? */  \
00165         if (cp[i].draw == PR_FALSE) \
00166           continue;                   \
00167         cairo_matrix_t matrix; \
00168         cairo_get_matrix(ctx, &matrix); \
00169         cairo_move_to(ctx, cp[i].x, cp[i].y); \
00170         cairo_rotate(ctx, cp[i].angle); \
00171         func(ctx, NS_ConvertUCS2toUTF8(Substring(text, i, 1)).get()); \
00172         cairo_set_matrix(ctx, &matrix); \
00173       } \
00174     }
00175 
00177 NS_IMETHODIMP
00178 nsSVGCairoGlyphGeometry::Render(nsISVGRendererCanvas *canvas)
00179 {
00180   nsCOMPtr<nsISVGCairoCanvas> cairoCanvas = do_QueryInterface(canvas);
00181   NS_ASSERTION(cairoCanvas, "wrong svg render context for geometry!");
00182   if (!cairoCanvas) return NS_ERROR_FAILURE;
00183 
00184   nsAutoString text;
00185   mSource->GetCharacterData(text);
00186 
00187   if (!text.Length())
00188     return NS_OK;
00189 
00190   nsSVGCharacterPosition *cp;
00191   
00192   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00193     return NS_ERROR_FAILURE;
00194 
00195   cairo_t *ctx = cairoCanvas->GetContext();
00196 
00197   /* get the metrics */
00198   nsCOMPtr<nsISVGCairoGlyphMetrics> metrics;
00199   {
00200     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00201     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00202     metrics = do_QueryInterface(xpmetrics);
00203     NS_ASSERTION(metrics, "wrong metrics object!");
00204     if (!metrics) {
00205       delete [] cp;
00206       return NS_ERROR_FAILURE;
00207     }
00208   }
00209 
00210   PRUint16 renderMode;
00211   cairo_matrix_t matrix;
00212   canvas->GetRenderMode(&renderMode);
00213   if (renderMode == nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL) {
00214     /* save/pop the state so we don't screw up the xform */
00215     cairo_save(ctx);
00216   }
00217   else {
00218     cairo_get_matrix(ctx, &matrix);
00219   }
00220 
00221   if (NS_FAILED(GetGlobalTransform(ctx, cairoCanvas))) {
00222     if (renderMode == nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL)
00223       cairo_restore(ctx);
00224     return NS_ERROR_FAILURE;
00225   }
00226 
00227   metrics->SelectFont(ctx);
00228 
00229   float x,y;
00230   if (!cp) {
00231     mSource->GetX(&x);
00232     mSource->GetY(&y);
00233     cairo_move_to(ctx, x, y);
00234   }
00235 
00236   if (renderMode != nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL) {
00237     PRUint16 rule;
00238     mSource->GetClipRule(&rule);
00239     if (rule == nsISVGGeometrySource::FILL_RULE_EVENODD)
00240       cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_EVEN_ODD);
00241     else
00242       cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_WINDING);
00243 
00244     LOOP_CHARS(cairo_text_path)
00245 
00246     cairo_set_matrix(ctx, &matrix);
00247 
00248     delete [] cp;
00249 
00250     return NS_OK;
00251   }
00252 
00253   PRBool hasFill = PR_FALSE;
00254   PRUint16 filltype;
00255   mSource->GetFillPaintType(&filltype);
00256   PRUint16 fillServerType = 0;
00257   if (filltype != nsISVGGeometrySource::PAINT_TYPE_NONE) {
00258     hasFill = PR_TRUE;
00259     if (filltype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00260       if (NS_FAILED(mSource->GetFillPaintServerType(&fillServerType)))
00261         hasFill = PR_FALSE;
00262     }
00263   }
00264 
00265   PRBool hasStroke = PR_FALSE;
00266   PRUint16 stroketype;
00267   mSource->GetStrokePaintType(&stroketype);
00268   PRUint16 strokeServerType = 0;
00269   if (stroketype != nsISVGGeometrySource::PAINT_TYPE_NONE) {
00270     hasStroke = PR_TRUE;
00271     if (stroketype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00272       if (NS_FAILED(mSource->GetStrokePaintServerType(&strokeServerType)))
00273         hasStroke = PR_FALSE;
00274     }
00275   }
00276 
00277   if (!hasFill && !hasStroke) {
00278     cairo_restore(ctx);
00279     delete [] cp;
00280     return NS_OK; // nothing to paint
00281   }
00282 
00283   if (hasFill) {
00284       nscolor rgb;
00285       mSource->GetFillPaint(&rgb);
00286       float opacity;
00287       mSource->GetFillOpacity(&opacity);
00288       
00289       cairo_set_source_rgba(ctx,
00290                             NS_GET_R(rgb)/255.0,
00291                             NS_GET_G(rgb)/255.0,
00292                             NS_GET_B(rgb)/255.0,
00293                             opacity);
00294       
00295       nsAutoString text;
00296       mSource->GetCharacterData(text);
00297 
00298       cairo_pattern_t *gradient;
00299       if (filltype == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00300         LOOP_CHARS(cairo_show_text)
00301       } else if (filltype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00302         if (fillServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00303           nsCOMPtr<nsISVGGradient> aGrad;
00304           mSource->GetFillGradient(getter_AddRefs(aGrad));
00305 
00306           cairo_pattern_t *gradient = CairoGradient(ctx, aGrad, mSource, opacity);
00307           if (gradient) {
00308             cairo_set_source(ctx, gradient);
00309             LOOP_CHARS(cairo_show_text)
00310             cairo_pattern_destroy(gradient);
00311           }
00312         }
00313       }
00314   }
00315 
00316   if (hasStroke) {
00317     if (!cp)
00318       cairo_move_to(ctx, x, y);
00319 
00320     nscolor rgb;
00321     mSource->GetStrokePaint(&rgb);
00322     float opacity;
00323     mSource->GetStrokeOpacity(&opacity);
00324     cairo_set_source_rgba(ctx,
00325                           NS_GET_R(rgb)/255.0,
00326                           NS_GET_G(rgb)/255.0,
00327                           NS_GET_B(rgb)/255.0,
00328                           opacity);
00329 
00330     float width;
00331     mSource->GetStrokeWidth(&width);
00332     cairo_set_line_width(ctx, double(width));
00333 
00334     PRUint16 capStyle;
00335     mSource->GetStrokeLinecap(&capStyle);
00336     switch (capStyle) {
00337     case nsISVGGeometrySource::STROKE_LINECAP_BUTT:
00338       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_BUTT);
00339       break;
00340     case nsISVGGeometrySource::STROKE_LINECAP_ROUND:
00341       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND);
00342       break;
00343     case nsISVGGeometrySource::STROKE_LINECAP_SQUARE:
00344       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_SQUARE);
00345       break;
00346     }
00347 
00348     float miterlimit;
00349     mSource->GetStrokeMiterlimit(&miterlimit);
00350     cairo_set_miter_limit(ctx, double(miterlimit));
00351 
00352     PRUint16 joinStyle;
00353     mSource->GetStrokeLinejoin(&joinStyle);
00354     switch(joinStyle) {
00355     case nsISVGGeometrySource::STROKE_LINEJOIN_MITER:
00356       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_MITER);
00357       break;
00358     case nsISVGGeometrySource::STROKE_LINEJOIN_ROUND:
00359       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_ROUND);
00360       break;
00361     case nsISVGGeometrySource::STROKE_LINEJOIN_BEVEL:
00362       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_BEVEL);
00363       break;
00364     }
00365 
00366     float *dashArray, offset;
00367     PRUint32 count;
00368     mSource->GetStrokeDashArray(&dashArray, &count);
00369     if (count > 0) {
00370       double *dashes = new double[count];
00371       for (unsigned i=0; i<count; i++)
00372         dashes[i] = dashArray[i];
00373       mSource->GetStrokeDashoffset(&offset);
00374       cairo_set_dash(ctx, dashes, count, double(offset));
00375       nsMemory::Free(dashArray);
00376       delete [] dashes;
00377     }
00378 
00379     LOOP_CHARS(cairo_text_path)
00380 
00381     if (stroketype == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00382       cairo_stroke(ctx);
00383     } else if (stroketype == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00384       if (strokeServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00385         nsCOMPtr<nsISVGGradient> aGrad;
00386         mSource->GetStrokeGradient(getter_AddRefs(aGrad));
00387 
00388         cairo_pattern_t *gradient = CairoGradient(ctx, aGrad, mSource, opacity);
00389         if (gradient) {
00390           cairo_set_source(ctx, gradient);
00391           cairo_stroke(ctx);
00392           cairo_pattern_destroy(gradient);
00393         }
00394       }
00395     }
00396   }
00397 
00398   delete [] cp;
00399 
00400   cairo_restore(ctx);
00401   
00402   return NS_OK;
00403 }
00404 
00406 NS_IMETHODIMP
00407 nsSVGCairoGlyphGeometry::Update(PRUint32 updatemask, nsISVGRendererRegion **_retval)
00408 {
00409   *_retval = nsnull;
00410 
00411   const unsigned long strokemask =
00412     nsISVGGlyphMetricsSource::UPDATEMASK_FONT           |
00413     nsISVGGlyphMetricsSource::UPDATEMASK_CHARACTER_DATA |
00414     nsISVGGlyphGeometrySource::UPDATEMASK_METRICS       |
00415     nsISVGGlyphGeometrySource::UPDATEMASK_X             |
00416     nsISVGGlyphGeometrySource::UPDATEMASK_Y             |
00417     nsISVGGeometrySource::UPDATEMASK_STROKE_PAINT_TYPE  |
00418     nsISVGGeometrySource::UPDATEMASK_STROKE_WIDTH       |
00419     nsISVGGeometrySource::UPDATEMASK_STROKE_LINECAP     |
00420     nsISVGGeometrySource::UPDATEMASK_STROKE_LINEJOIN    |
00421     nsISVGGeometrySource::UPDATEMASK_STROKE_MITERLIMIT  |
00422     nsISVGGeometrySource::UPDATEMASK_STROKE_DASH_ARRAY  |
00423     nsISVGGeometrySource::UPDATEMASK_STROKE_DASHOFFSET  |
00424     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00425   
00426   const unsigned long regionsmask =
00427     nsISVGGlyphGeometrySource::UPDATEMASK_METRICS |
00428     nsISVGGlyphGeometrySource::UPDATEMASK_X       |
00429     nsISVGGlyphGeometrySource::UPDATEMASK_Y       |
00430     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00431 
00432   nsCOMPtr<nsISVGRendererRegion> before = mCoveredRegion;
00433 
00434   if ((updatemask & regionsmask) || (updatemask & strokemask)) {
00435     nsCOMPtr<nsISVGRendererRegion> after;
00436     GetCoveredRegion(getter_AddRefs(after));
00437 
00438     if (mCoveredRegion) {
00439       if (after)
00440         after->Combine(before, _retval);
00441     } else {
00442       *_retval = after;
00443       NS_IF_ADDREF(*_retval);
00444     }
00445     mCoveredRegion = after;
00446   }
00447 
00448   if (!*_retval) {
00449     *_retval = before;
00450     NS_IF_ADDREF(*_retval);
00451   }
00452 
00453   return NS_OK;
00454 }
00455 
00457 NS_IMETHODIMP
00458 nsSVGCairoGlyphGeometry::GetCoveredRegion(nsISVGRendererRegion **_retval)
00459 {
00460   *_retval = nsnull;
00461 
00462   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00463 
00464   /* get the metrics */
00465   nsCOMPtr<nsISVGCairoGlyphMetrics> metrics;
00466   {
00467     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00468     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00469     metrics = do_QueryInterface(xpmetrics);
00470     NS_ASSERTION(metrics, "wrong metrics object!");
00471     if (!metrics)
00472       return NS_ERROR_FAILURE;
00473   }
00474 
00475   if (NS_FAILED(GetGlobalTransform(ctx, nsnull))) {
00476     cairo_destroy(ctx);
00477     return NS_ERROR_FAILURE;
00478   }
00479 
00480   metrics->SelectFont(ctx);
00481 
00482   nsSVGCharacterPosition *cp;
00483 
00484   if (NS_FAILED(mSource->GetCharacterPosition(&cp))) {
00485     cairo_destroy(ctx);
00486     return NS_ERROR_FAILURE;
00487   }
00488 
00489   float x,y;
00490   if (!cp) {
00491     mSource->GetX(&x);
00492     mSource->GetY(&y);
00493     cairo_move_to(ctx, x, y);
00494   } else {
00495       x = 0.0, y = 0.0;
00496   }
00497 
00498   PRUint16 type;  
00499   mSource->GetFillPaintType(&type);
00500   PRBool hasCoveredFill = type != nsISVGGeometrySource::PAINT_TYPE_NONE;
00501   mSource->GetStrokePaintType(&type);
00502   bool hasCoveredStroke = type != nsISVGGeometrySource::PAINT_TYPE_NONE;
00503 
00504   if (!hasCoveredFill && !hasCoveredStroke) return NS_OK;
00505 
00506   nsAutoString text;
00507   mSource->GetCharacterData(text);
00508   
00509   if (text.Length() == 0) {
00510     double xx = x, yy = y;
00511     cairo_user_to_device(ctx, &xx, &yy);
00512     cairo_destroy(ctx);
00513     delete [] cp;
00514     return NS_NewSVGCairoRectRegion(_retval, xx, yy, 0, 0);
00515   }
00516 
00517   if (!cp) {
00518     if (hasCoveredStroke) {
00519       cairo_text_path(ctx, NS_ConvertUCS2toUTF8(text).get());
00520     } else {
00521       cairo_text_extents_t extent;
00522       cairo_text_extents(ctx,
00523                          NS_ConvertUCS2toUTF8(text).get(),
00524                          &extent);
00525       cairo_rectangle(ctx, x + extent.x_bearing, y + extent.y_bearing,
00526                       extent.width, extent.height);
00527     }
00528   } else {
00529     cairo_matrix_t matrix;
00530     for (PRUint32 i=0; i<text.Length(); i++) {
00531       /* character actually on the path? */
00532       if (cp[i].draw == PR_FALSE)
00533         continue;
00534       cairo_get_matrix(ctx, &matrix);
00535       cairo_move_to(ctx, cp[i].x, cp[i].y);
00536       cairo_rotate(ctx, cp[i].angle);
00537       if (hasCoveredStroke) {
00538         cairo_text_path(ctx, NS_ConvertUCS2toUTF8(Substring(text, i, 1)).get());
00539       } else {
00540         cairo_text_extents_t extent;
00541         cairo_text_extents(ctx,
00542                            NS_ConvertUCS2toUTF8(Substring(text, i, 1)).get(),
00543                            &extent);
00544         cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
00545         cairo_rel_line_to(ctx, extent.width, 0);
00546         cairo_rel_line_to(ctx, 0, extent.height);
00547         cairo_rel_line_to(ctx, -extent.width, 0);
00548         cairo_close_path(ctx);
00549       }
00550       cairo_set_matrix(ctx, &matrix);
00551     }
00552   }
00553 
00554   delete [] cp;
00555 
00556   double xmin, ymin, xmax, ymax;
00557 
00558   if (hasCoveredStroke) {
00559     float width;
00560     mSource->GetStrokeWidth(&width);
00561     cairo_set_line_width(ctx, double(width));
00562     
00563     PRUint16 capStyle;
00564     mSource->GetStrokeLinecap(&capStyle);
00565     switch (capStyle) {
00566     case nsISVGGeometrySource::STROKE_LINECAP_BUTT:
00567       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_BUTT);
00568       break;
00569     case nsISVGGeometrySource::STROKE_LINECAP_ROUND:
00570       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND);
00571       break;
00572     case nsISVGGeometrySource::STROKE_LINECAP_SQUARE:
00573       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_SQUARE);
00574       break;
00575     }
00576         
00577     float miterlimit;
00578     mSource->GetStrokeMiterlimit(&miterlimit);
00579     cairo_set_miter_limit(ctx, double(miterlimit));
00580     
00581     PRUint16 joinStyle;
00582     mSource->GetStrokeLinejoin(&joinStyle);
00583     switch(joinStyle) {
00584     case nsISVGGeometrySource::STROKE_LINEJOIN_MITER:
00585       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_MITER);
00586       break;
00587     case nsISVGGeometrySource::STROKE_LINEJOIN_ROUND:
00588       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_ROUND);
00589       break;
00590     case nsISVGGeometrySource::STROKE_LINEJOIN_BEVEL:
00591       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_BEVEL);
00592       break;
00593     }
00594     
00595     cairo_stroke_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00596     nsSVGUtils::UserToDeviceBBox(ctx, &xmin, &ymin, &xmax, &ymax);
00597   } else {
00598     cairo_identity_matrix(ctx);
00599     cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00600   }
00601 
00602   cairo_destroy(ctx);
00603 
00604   return NS_NewSVGCairoRectRegion(_retval, xmin, ymin, xmax-xmin, ymax-ymin);
00605 }
00606 
00608 NS_IMETHODIMP
00609 nsSVGCairoGlyphGeometry::ContainsPoint(float x, float y, PRBool *_retval)
00610 {
00611   *_retval = PR_FALSE;
00612 
00613   /* get the metrics */
00614   nsCOMPtr<nsISVGCairoGlyphMetrics> metrics;
00615   {
00616     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00617     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00618     metrics = do_QueryInterface(xpmetrics);
00619     NS_ASSERTION(metrics, "wrong metrics object!");
00620     if (!metrics)
00621       return NS_ERROR_FAILURE;
00622   }
00623 
00624   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00625   if (NS_FAILED(GetGlobalTransform(ctx, nsnull))) {
00626     cairo_destroy(ctx);
00627     return NS_ERROR_FAILURE;
00628   }
00629 
00630   metrics->SelectFont(ctx);
00631 
00632   nsAutoString text;
00633   mSource->GetCharacterData(text);
00634 
00635   nsSVGCharacterPosition *cp;
00636 
00637   if (NS_FAILED(mSource->GetCharacterPosition(&cp))) {
00638     cairo_destroy(ctx);
00639     return NS_ERROR_FAILURE;
00640   }
00641 
00642   float xx, yy;
00643   if (!cp) {
00644     mSource->GetX(&xx);
00645     mSource->GetY(&yy);
00646   }
00647 
00648   cairo_matrix_t matrix;
00649 
00650   for (PRUint32 i=0; i<text.Length(); i++) {
00651     /* character actually on the path? */
00652     if (cp && cp[i].draw == PR_FALSE)
00653       continue;
00654 
00655     cairo_get_matrix(ctx, &matrix);
00656 
00657     if (cp) {
00658       cairo_move_to(ctx, cp[i].x, cp[i].y);
00659       cairo_rotate(ctx, cp[i].angle);
00660     } else {
00661       cairo_move_to(ctx, xx, yy);
00662     }
00663 
00664     cairo_text_extents_t extent;
00665     cairo_text_extents(ctx,
00666                        NS_ConvertUCS2toUTF8(Substring(text, i, 1)).get(),
00667                        &extent);
00668     cairo_rel_move_to(ctx, extent.x_bearing, extent.y_bearing);
00669     cairo_rel_line_to(ctx, extent.width, 0);
00670     cairo_rel_line_to(ctx, 0, extent.height);
00671     cairo_rel_line_to(ctx, -extent.width, 0);
00672     cairo_close_path(ctx);
00673 
00674     cairo_set_matrix(ctx, &matrix);
00675 
00676     if (!cp) {
00677       xx += extent.x_advance;
00678       yy += extent.y_advance;
00679     }
00680   }
00681 
00682   delete [] cp;
00683 
00684   cairo_identity_matrix(ctx);
00685   *_retval = cairo_in_fill(ctx, x, y);
00686   cairo_destroy(ctx);
00687 
00688   return NS_OK;
00689 }
00690 
00691 
00692 nsresult
00693 nsSVGCairoGlyphGeometry::GetGlobalTransform(cairo_t *ctx, nsISVGCairoCanvas* aCanvas)
00694 {
00695   nsCOMPtr<nsIDOMSVGMatrix> ctm;
00696   mSource->GetCanvasTM(getter_AddRefs(ctm));
00697   NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
00698   
00699   float m[6];
00700   float val;
00701   ctm->GetA(&val);
00702   m[0] = val;
00703   
00704   ctm->GetB(&val);
00705   m[1] = val;
00706   
00707   ctm->GetC(&val);  
00708   m[2] = val;  
00709   
00710   ctm->GetD(&val);  
00711   m[3] = val;  
00712   
00713   ctm->GetE(&val);
00714   m[4] = val;
00715   
00716   ctm->GetF(&val);
00717   m[5] = val;
00718 
00719   cairo_matrix_t matrix = {m[0], m[1], m[2], m[3], m[4], m[5]};
00720   if (aCanvas) {
00721     aCanvas->AdjustMatrixForInitialTransform(&matrix);
00722   }
00723 
00724   cairo_matrix_t inverse = matrix;
00725   if (cairo_matrix_invert(&inverse)) {
00726     cairo_identity_matrix(ctx);
00727     cairo_new_path(ctx);
00728     return NS_ERROR_FAILURE;
00729   }
00730 
00731   cairo_set_matrix(ctx, &matrix);
00732   return NS_OK;
00733 }
00734 
00735 NS_IMETHODIMP
00736 nsSVGCairoGlyphGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox)
00737 {
00738   *aBoundingBox = nsnull;
00739 
00740   nsCOMPtr<nsIDOMSVGRect> rect = do_CreateInstance(NS_SVGRECT_CONTRACTID);
00741   NS_ASSERTION(rect, "could not create rect");
00742   if (!rect) return NS_ERROR_FAILURE;
00743 
00744   nsAutoString text;
00745   mSource->GetCharacterData(text);
00746   if (!text.Length())
00747     return NS_OK;
00748 
00749   nsSVGCharacterPosition *cp;
00750 
00751   if (NS_FAILED(mSource->GetCharacterPosition(&cp)))
00752     return NS_ERROR_FAILURE;
00753 
00754   double xmin, ymin, xmax, ymax;
00755 
00756   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00757   if (NS_FAILED(GetGlobalTransform(ctx, nsnull))) {
00758     cairo_destroy(ctx);
00759     return NS_ERROR_FAILURE;
00760   }
00761 
00762   /* get the metrics */
00763   nsCOMPtr<nsISVGCairoGlyphMetrics> metrics;
00764   {
00765     nsCOMPtr<nsISVGRendererGlyphMetrics> xpmetrics;
00766     mSource->GetMetrics(getter_AddRefs(xpmetrics));
00767     metrics = do_QueryInterface(xpmetrics);
00768     NS_ASSERTION(metrics, "wrong metrics object!");
00769     if (!metrics)
00770       return NS_ERROR_FAILURE;
00771   }
00772 
00773   metrics->SelectFont(ctx);
00774 
00775   float x,y;
00776   if (!cp) {
00777     mSource->GetX(&x);
00778     mSource->GetY(&y);
00779     cairo_move_to(ctx, x, y);
00780   }
00781 
00782   LOOP_CHARS(cairo_text_path)
00783 
00784   delete [] cp;
00785 
00786   cairo_identity_matrix(ctx);
00787   cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00788 
00789   cairo_destroy(ctx);
00790 
00791   rect->SetX(xmin);
00792   rect->SetY(ymin);
00793   rect->SetWidth(xmax - xmin);
00794   rect->SetHeight(ymax - ymin);
00795 
00796   *aBoundingBox = rect;
00797   NS_ADDREF(*aBoundingBox);
00798   
00799   return NS_OK;
00800 }