Back to index

lightning-sunbird  0.9+nobinonly
nsSVGUtils.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 project.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2005
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either of the GNU General Public License Version 2 or later (the "GPL"),
00025  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "nsSVGLength.h"
00038 #include "nsIDOMDocument.h"
00039 #include "nsIDOMSVGElement.h"
00040 #include "nsIDOMSVGSVGElement.h"
00041 #include "nsStyleCoord.h"
00042 #include "nsPresContext.h"
00043 #include "nsSVGCoordCtxProvider.h"
00044 #include "nsIContent.h"
00045 #include "nsIDocument.h"
00046 #include "nsIFrame.h"
00047 #include "nsLayoutAtoms.h"
00048 #include "nsIURI.h"
00049 #include "nsStyleStruct.h"
00050 #include "nsIPresShell.h"
00051 #include "nsSVGUtils.h"
00052 #include "nsISVGGeometrySource.h"
00053 #include "nsNetUtil.h"
00054 #include "nsContentDLF.h"
00055 #include "nsContentUtils.h"
00056 #include "nsISVGRenderer.h"
00057 #include "nsIDOMSVGRect.h"
00058 #include "nsFrameList.h"
00059 #include "nsISVGChildFrame.h"
00060 #include "nsIDOMSVGMatrix.h"
00061 #include "cairo.h"
00062 
00063 #if defined(MOZ_SVG_RENDERER_GDIPLUS)
00064 #include <windows.h>
00065 #endif
00066 
00067 static PRBool gSVGEnabled;
00068 static PRBool gSVGRendererAvailable = PR_FALSE;
00069 static const char SVG_PREF_STR[] = "svg.enabled";
00070 
00071 PR_STATIC_CALLBACK(int)
00072 SVGPrefChanged(const char *aPref, void *aClosure)
00073 {
00074   PRBool prefVal = nsContentUtils::GetBoolPref(SVG_PREF_STR);
00075   if (prefVal == gSVGEnabled)
00076     return 0;
00077 
00078   gSVGEnabled = prefVal;
00079   if (gSVGRendererAvailable) {
00080     if (gSVGEnabled)
00081       nsContentDLF::RegisterSVG();
00082     else
00083       nsContentDLF::UnregisterSVG();
00084   }
00085 
00086   return 0;
00087 }
00088 
00089 PRBool
00090 nsSVGUtils::SVGEnabled()
00091 {
00092   static PRBool sInitialized = PR_FALSE;
00093 
00094   if (!sInitialized) {
00095     gSVGRendererAvailable = PR_TRUE;
00096 
00097 #if defined(MOZ_SVG_RENDERER_GDIPLUS)
00098     HMODULE gdiplus, gkgdiplus;
00099 
00100     if ((gdiplus = LoadLibrary("gdiplus.dll")) == NULL)
00101       gSVGRendererAvailable = PR_FALSE;
00102   #endif
00103 
00104     /* check and register ourselves with the pref */
00105     gSVGEnabled = nsContentUtils::GetBoolPref(SVG_PREF_STR);
00106     nsContentUtils::RegisterPrefCallback(SVG_PREF_STR, SVGPrefChanged, nsnull);
00107 
00108     sInitialized = PR_TRUE;
00109   }
00110 
00111   return gSVGEnabled && gSVGRendererAvailable;
00112 }
00113 
00114 float
00115 nsSVGUtils::CoordToFloat(nsPresContext *aPresContext, nsIContent *aContent,
00116                       const nsStyleCoord &aCoord)
00117 {
00118   float val = 0.0f;
00119 
00120   switch (aCoord.GetUnit()) {
00121   case eStyleUnit_Factor:
00122     // user units
00123     val = aCoord.GetFactorValue();
00124     break;
00125 
00126   case eStyleUnit_Coord:
00127     val = aCoord.GetCoordValue() / aPresContext->ScaledPixelsToTwips();
00128     break;
00129 
00130   case eStyleUnit_Percent: {
00131       nsCOMPtr<nsIDOMSVGElement> element = do_QueryInterface(aContent);
00132       if (!element)
00133         break;
00134       nsCOMPtr<nsIDOMSVGSVGElement> owner;
00135       element->GetOwnerSVGElement(getter_AddRefs(owner));
00136       nsCOMPtr<nsSVGCoordCtxProvider> ctx = do_QueryInterface(owner);
00137     
00138       nsCOMPtr<nsISVGLength> length;
00139       NS_NewSVGLength(getter_AddRefs(length), aCoord.GetPercentValue() * 100.0f,
00140                       nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
00141     
00142       if (!ctx || !length)
00143         break;
00144 
00145       length->SetContext(nsRefPtr<nsSVGCoordCtx>(ctx->GetContextUnspecified()));
00146       length->GetValue(&val);
00147       break;
00148     }
00149   default:
00150     break;
00151   }
00152 
00153   return val;
00154 }
00155 
00156 float
00157 nsSVGUtils::UserSpace(nsIContent *content,
00158                       nsIDOMSVGLength *length,
00159                       ctxDirection direction)
00160 {
00161   PRUint16 units;
00162   float value;
00163 
00164   length->GetUnitType(&units);
00165   length->GetValueInSpecifiedUnits(&value);
00166 
00167   nsCOMPtr<nsISVGLength> val;
00168   NS_NewSVGLength(getter_AddRefs(val), value, units);
00169  
00170   nsCOMPtr<nsIDOMSVGElement> element = do_QueryInterface(content);
00171   nsCOMPtr<nsIDOMSVGSVGElement> svg;
00172   element->GetOwnerSVGElement(getter_AddRefs(svg));
00173   nsCOMPtr<nsSVGCoordCtxProvider> ctx = do_QueryInterface(svg);
00174 
00175   if (ctx) {
00176     switch (direction) {
00177     case X:
00178       val->SetContext(nsRefPtr<nsSVGCoordCtx>(ctx->GetContextX()));
00179       break;
00180     case Y:
00181       val->SetContext(nsRefPtr<nsSVGCoordCtx>(ctx->GetContextY()));
00182       break;
00183     case XY:
00184       val->SetContext(nsRefPtr<nsSVGCoordCtx>(ctx->GetContextUnspecified()));
00185       break;
00186     }
00187   }
00188 
00189   val->GetValue(&value);
00190   return value;
00191 }
00192 
00193 nsresult nsSVGUtils::GetReferencedFrame(nsIFrame **aRefFrame, nsCAutoString& uriSpec, nsIContent *aContent, 
00194                                         nsIPresShell *aPresShell)
00195 {
00196   nsresult rv = NS_OK;
00197   *aRefFrame = nsnull;
00198 
00199   // Get the ID from the spec (no ID = an error)
00200   PRInt32 pos = uriSpec.FindChar('#');
00201   if (pos == -1) {
00202     NS_ASSERTION(pos != -1, "URI Spec not a reference");
00203     return NS_ERROR_FAILURE;
00204   }
00205 
00206   // Get the current document
00207   nsIDocument *myDoc = aContent->GetCurrentDoc();
00208   if (!myDoc) {
00209     NS_WARNING("Content doesn't reference a Document!");
00210     return NS_ERROR_FAILURE;
00211   }
00212 
00213   // Get our URI
00214   nsCOMPtr<nsIURI> myURI = myDoc->GetDocumentURI();
00215 
00216 #ifdef DEBUG_scooter
00217     // Get the uri Spec
00218     nsCAutoString dSpec;
00219     myURI->GetSpec(dSpec);
00220     printf("Document URI = %s, target URI = %s\n",dSpec.get(), uriSpec.get());
00221 #endif
00222 
00223   // Create a URI out of the target
00224   nsCAutoString aURISName;
00225   uriSpec.Left(aURISName, pos);
00226   nsCOMPtr<nsIURI> targetURI;
00227   NS_NewURI(getter_AddRefs(targetURI), aURISName, nsnull, myDoc->GetBaseURI());
00228   PRBool match;
00229   myURI->Equals(targetURI, &match);
00230   if (!match) {
00231     // Oops -- we don't support off-document references
00232     return NS_ERROR_FAILURE;
00233   }
00234 
00235   // At this point, we know we have a target within our document, but
00236   // it may not point to anything
00237   // Strip off the hash and get the name
00238   nsCAutoString aURICName;
00239   uriSpec.Right(aURICName, uriSpec.Length()-(pos + 1));
00240 
00241   // Get a unicode string
00242   nsAutoString  aURIName;
00243   CopyUTF8toUTF16(aURICName, aURIName);
00244 
00245   // Get the domDocument
00246   nsCOMPtr<nsIDOMDocument>domDoc = do_QueryInterface(myDoc);
00247   NS_ASSERTION(domDoc, "Content doesn't reference a dom Document");
00248   if (domDoc == nsnull) {
00249     return NS_ERROR_FAILURE;
00250   }
00251 
00252   // Get the element
00253   nsCOMPtr<nsIDOMElement> element;
00254   rv = domDoc->GetElementById(aURIName, getter_AddRefs(element));
00255   if (!NS_SUCCEEDED(rv) || element == nsnull) {
00256     return NS_ERROR_FAILURE;  
00257   }
00258 
00259   // Get the Primary Frame
00260   nsCOMPtr<nsIContent> aGContent = do_QueryInterface(element);
00261   NS_ASSERTION(aPresShell, "Get referenced SVG frame -- no pres shell provided");
00262   if (!aPresShell)
00263     return NS_ERROR_FAILURE;
00264 
00265   rv = aPresShell->GetPrimaryFrameFor(aGContent, aRefFrame);
00266   NS_ASSERTION(*aRefFrame, "Get referenced SVG frame -- can't find primary frame");
00267   if (!(*aRefFrame)) return NS_ERROR_FAILURE;
00268   return rv;
00269 }
00270 
00271 nsresult nsSVGUtils::GetPaintType(PRUint16 *aPaintType, const nsStyleSVGPaint& aPaint, 
00272                                   nsIContent *aContent, nsIPresShell *aPresShell )
00273 {
00274   *aPaintType = aPaint.mType;
00275   // If the type is a Paint Server, determine what kind
00276   if (*aPaintType == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00277     nsIURI *aServer = aPaint.mPaint.mPaintServer;
00278     if (aServer == nsnull)
00279       return NS_ERROR_FAILURE;
00280 
00281     // Get the uri Spec
00282     nsCAutoString uriSpec;
00283     aServer->GetSpec(uriSpec);
00284 
00285     // Get the frame
00286     nsIFrame *aFrame = nsnull;
00287     nsresult rv;
00288     rv = nsSVGUtils::GetReferencedFrame(&aFrame, uriSpec, aContent, aPresShell);
00289     if (!NS_SUCCEEDED(rv) || !aFrame)
00290       return NS_ERROR_FAILURE;
00291 
00292     // Finally, figure out what type it is
00293     if (aFrame->GetType() == nsLayoutAtoms::svgLinearGradientFrame ||
00294         aFrame->GetType() == nsLayoutAtoms::svgRadialGradientFrame)
00295       *aPaintType = nsISVGGeometrySource::PAINT_TYPE_GRADIENT;
00296     else if (aFrame->GetType() == nsLayoutAtoms::svgPatternFrame)
00297       *aPaintType = nsISVGGeometrySource::PAINT_TYPE_PATTERN;
00298     else
00299       return NS_ERROR_FAILURE;
00300   }
00301   return NS_OK;
00302 }
00303 
00304 nsresult
00305 nsSVGUtils::GetBBox(nsFrameList *aFrames, nsIDOMSVGRect **_retval)
00306 {
00307   *_retval = nsnull;
00308 
00309   float minx, miny, maxx, maxy;
00310   minx = miny = FLT_MAX;
00311   maxx = maxy = -1.0 * FLT_MAX;
00312 
00313   nsCOMPtr<nsIDOMSVGRect> unionRect;
00314 
00315   nsIFrame* kid = aFrames->FirstChild();
00316   while (kid) {
00317     nsISVGChildFrame* SVGFrame=0;
00318     kid->QueryInterface(NS_GET_IID(nsISVGChildFrame), (void**)&SVGFrame);
00319     if (SVGFrame) {
00320       nsCOMPtr<nsIDOMSVGRect> box;
00321       SVGFrame->GetBBox(getter_AddRefs(box));
00322 
00323       if (box) {
00324         float bminx, bminy, bmaxx, bmaxy, width, height;
00325         box->GetX(&bminx);
00326         box->GetY(&bminy);
00327         box->GetWidth(&width);
00328         box->GetHeight(&height);
00329         bmaxx = bminx+width;
00330         bmaxy = bminy+height;
00331 
00332         if (!unionRect)
00333           unionRect = box;
00334         minx = PR_MIN(minx, bminx);
00335         miny = PR_MIN(miny, bminy);
00336         maxx = PR_MAX(maxx, bmaxx);
00337         maxy = PR_MAX(maxy, bmaxy);
00338       }
00339     }
00340     kid = kid->GetNextSibling();
00341   }
00342 
00343   if (unionRect) {
00344     unionRect->SetX(minx);
00345     unionRect->SetY(miny);
00346     unionRect->SetWidth(maxx - minx);
00347     unionRect->SetHeight(maxy - miny);
00348     *_retval = unionRect;
00349     NS_ADDREF(*_retval);
00350     return NS_OK;
00351   }
00352 
00353   return NS_ERROR_FAILURE;
00354 }
00355 
00356 static cairo_surface_t *
00357 GetCairoComputationalSurface()
00358 {
00359   static cairo_surface_t *sCairoComputationalSurface = nsnull;
00360 
00361   if (!sCairoComputationalSurface)
00362     sCairoComputationalSurface =
00363       cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
00364 
00365   return sCairoComputationalSurface;
00366 }
00367 
00368 static cairo_matrix_t
00369 ConvertSVGMatrixToCairo(nsIDOMSVGMatrix *aMatrix)
00370 {
00371   float A, B, C, D, E, F;
00372   aMatrix->GetA(&A);
00373   aMatrix->GetB(&B);
00374   aMatrix->GetC(&C);
00375   aMatrix->GetD(&D);
00376   aMatrix->GetE(&E);
00377   aMatrix->GetF(&F);
00378   cairo_matrix_t m = { A, B, C, D, E, F };
00379   return m;
00380 }
00381 
00382 PRBool
00383 nsSVGUtils::HitTestRect(nsIDOMSVGMatrix *aMatrix,
00384                         float aRX, float aRY, float aRWidth, float aRHeight,
00385                         float aX, float aY)
00386 {
00387   PRBool result = PR_TRUE;
00388 
00389   if (aMatrix) {
00390     cairo_matrix_t matrix = ConvertSVGMatrixToCairo(aMatrix);
00391     cairo_t *ctx = cairo_create(GetCairoComputationalSurface());
00392     cairo_set_tolerance(ctx, 1.0);
00393 
00394     cairo_set_matrix(ctx, &matrix);
00395     cairo_new_path(ctx);
00396     cairo_rectangle(ctx, aRX, aRY, aRWidth, aRHeight);
00397     cairo_identity_matrix(ctx);
00398 
00399     if (!cairo_in_fill(ctx, aX, aY))
00400       result = PR_FALSE;
00401 
00402     cairo_destroy(ctx);
00403   }
00404 
00405   return result;
00406 }
00407 
00408 void
00409 nsSVGUtils::UserToDeviceBBox(cairo_t *ctx,
00410                              double *xmin, double *ymin,
00411                              double *xmax, double *ymax)
00412 {
00413   double x[3], y[3];
00414   x[0] = *xmin;  y[0] = *ymax;
00415   x[1] = *xmax;  y[1] = *ymax;
00416   x[2] = *xmax;  y[2] = *ymin;
00417 
00418   cairo_user_to_device(ctx, xmin, ymin);
00419   *xmax = *xmin;
00420   *ymax = *ymin;
00421   for (int i = 0; i < 3; i++) {
00422     cairo_user_to_device(ctx, &x[i], &y[i]);
00423     *xmin = PR_MIN(*xmin, x[i]);
00424     *xmax = PR_MAX(*xmax, x[i]);
00425     *ymin = PR_MIN(*ymin, y[i]);
00426     *ymax = PR_MAX(*ymax, y[i]);
00427   }
00428 }