Back to index

lightning-sunbird  0.9+nobinonly
nsSVGLength.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
00018  * Crocodile Clips Ltd..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *   Jonathan Watt <jonathan.watt@strath.ac.uk>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsSVGLength.h"
00041 #include "nsIDOMSVGMatrix.h"
00042 #include "nsSVGAtoms.h"
00043 #include "nsSVGValue.h"
00044 #include "nsTextFormatter.h"
00045 #include "prdtoa.h"
00046 #include "nsCRT.h"
00047 #include "nsSVGCoordCtx.h"
00048 #include "nsIDOMSVGNumber.h"
00049 #include "nsISVGValueUtils.h"
00050 #include "nsWeakReference.h"
00051 #include "nsContentUtils.h"
00052 
00054 // nsSVGLength class
00055 
00056 class nsSVGLength : public nsISVGLength,
00057                     public nsSVGValue,
00058                     public nsISVGValueObserver,
00059                     public nsSupportsWeakReference
00060 {
00061 protected:
00062   friend nsresult NS_NewSVGLength(nsISVGLength** result,
00063                                   float value,
00064                                   PRUint16 unit);
00065 
00066   friend nsresult NS_NewSVGLength(nsISVGLength** result,
00067                                   const nsAString &value);
00068   
00069   nsSVGLength(float value, PRUint16 unit);
00070   nsSVGLength();
00071   virtual ~nsSVGLength();
00072 
00073 public:
00074   // nsISupports interface:
00075   NS_DECL_ISUPPORTS
00076 
00077   // nsIDOMSVGLength interface:
00078   NS_DECL_NSIDOMSVGLENGTH
00079 
00080   // nsISVGLength interface:
00081   NS_IMETHOD SetContext(nsSVGCoordCtx* context);
00082   
00083   // nsISVGValue interface:
00084   NS_IMETHOD SetValueString(const nsAString& aValue);
00085   NS_IMETHOD GetValueString(nsAString& aValue);
00086   
00087   // nsISVGValueObserver interface:
00088   NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
00089                                      modificationType aModType);
00090   NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
00091                                      modificationType aModType);
00092 
00093   // nsISupportsWeakReference
00094   // implementation inherited from nsSupportsWeakReference
00095   
00096 protected:
00097   // implementation helpers:
00098   float mmPerPixel();
00099   float AxisLength();
00100   PRBool IsValidUnitType(PRUint16 unit);
00101   void MaybeAddAsObserver();
00102   void MaybeRemoveAsObserver();
00103   
00104   float mValueInSpecifiedUnits;
00105   PRUint16 mSpecifiedUnitType;
00106   nsRefPtr<nsSVGCoordCtx> mContext;
00107 };
00108 
00109 
00110 //----------------------------------------------------------------------
00111 // Implementation
00112 
00113 nsresult
00114 NS_NewSVGLength(nsISVGLength** result,
00115                 float value,
00116                 PRUint16 unit)
00117 {
00118   *result = new nsSVGLength(value, unit);
00119   if (!*result)
00120     return NS_ERROR_OUT_OF_MEMORY;
00121   NS_ADDREF(*result);
00122   return NS_OK;
00123 }
00124 
00125 nsresult
00126 NS_NewSVGLength(nsISVGLength** result,
00127                 const nsAString &value)
00128 {
00129   *result = nsnull;
00130   nsSVGLength *pl = new nsSVGLength();
00131   if (!pl)
00132     return NS_ERROR_OUT_OF_MEMORY;
00133   NS_ADDREF(pl);
00134   nsresult rv = pl->SetValueAsString(value);
00135   if (NS_FAILED(rv)) {
00136     NS_RELEASE(pl);
00137     return rv;
00138   }
00139   *result = pl;
00140   return NS_OK;
00141 }  
00142 
00143 
00144 nsSVGLength::nsSVGLength(float value,
00145                          PRUint16 unit)
00146     : mValueInSpecifiedUnits(value),
00147       mSpecifiedUnitType(unit)
00148 {
00149   // we don't have a context yet, so we don't call MaybeAddAsObserver()
00150 }
00151 
00152 nsSVGLength::nsSVGLength()
00153 {
00154 }
00155 
00156 nsSVGLength::~nsSVGLength()
00157 {
00158   MaybeRemoveAsObserver();
00159 }
00160 
00161 //----------------------------------------------------------------------
00162 // nsISupports methods:
00163 
00164 NS_IMPL_ADDREF(nsSVGLength)
00165 NS_IMPL_RELEASE(nsSVGLength)
00166 
00167 NS_INTERFACE_MAP_BEGIN(nsSVGLength)
00168   NS_INTERFACE_MAP_ENTRY(nsISVGValue)
00169   NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
00170   NS_INTERFACE_MAP_ENTRY(nsISVGLength)
00171   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
00172   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00173   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(SVGLength)
00174   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
00175 NS_INTERFACE_MAP_END
00176 
00177 //----------------------------------------------------------------------
00178 // nsISVGValue methods:
00179 
00180 NS_IMETHODIMP
00181 nsSVGLength::SetValueString(const nsAString& aValue)
00182 {
00183   return SetValueAsString(aValue);
00184 }
00185 
00186 NS_IMETHODIMP
00187 nsSVGLength::GetValueString(nsAString& aValue)
00188 {
00189   return GetValueAsString(aValue);
00190 }
00191 
00192 //----------------------------------------------------------------------
00193 // nsISVGValueObserver methods
00194 
00195 NS_IMETHODIMP
00196 nsSVGLength::WillModifySVGObservable(nsISVGValue* observable,
00197                                      modificationType aModType)
00198 {
00199   WillModify(aModType);
00200   return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsSVGLength::DidModifySVGObservable(nsISVGValue* observable,
00205                                     modificationType aModType)
00206 {
00207   DidModify(aModType);
00208   return NS_OK;
00209 }
00210 
00211 //----------------------------------------------------------------------
00212 // nsIDOMSVGLength methods:
00213 
00214 /* readonly attribute unsigned short unitType; */
00215 NS_IMETHODIMP
00216 nsSVGLength::GetUnitType(PRUint16 *aUnitType)
00217 {
00218   *aUnitType = mSpecifiedUnitType;
00219   return NS_OK;
00220 }
00221 
00222 /* attribute float value; */
00223 NS_IMETHODIMP
00224 nsSVGLength::GetValue(float *aValue)
00225 {
00226   switch (mSpecifiedUnitType) {
00227     case SVG_LENGTHTYPE_NUMBER:
00228     case SVG_LENGTHTYPE_PX:
00229       *aValue = mValueInSpecifiedUnits;
00230       break;
00231     case SVG_LENGTHTYPE_MM:
00232       *aValue = mValueInSpecifiedUnits / mmPerPixel();
00233       break;
00234     case SVG_LENGTHTYPE_CM:
00235       *aValue = mValueInSpecifiedUnits * 10.0f / mmPerPixel();
00236       break;
00237     case SVG_LENGTHTYPE_IN:
00238       *aValue = mValueInSpecifiedUnits * 25.4f / mmPerPixel();
00239       break;
00240     case SVG_LENGTHTYPE_PT:
00241       *aValue = mValueInSpecifiedUnits * 25.4f / 72.0f / mmPerPixel();
00242       break;
00243     case SVG_LENGTHTYPE_PC:
00244       *aValue = mValueInSpecifiedUnits * 25.4f * 12.0f / 72.0f / mmPerPixel();
00245       break;
00246     case SVG_LENGTHTYPE_PERCENTAGE:
00247       *aValue = mValueInSpecifiedUnits * AxisLength() / 100.0f;
00248       break;
00249     case SVG_LENGTHTYPE_EMS:
00250     case SVG_LENGTHTYPE_EXS:
00251       NS_NOTYETIMPLEMENTED("SVG_LENGTHTYPE_EXS");
00252       *aValue = 0;
00253       return NS_ERROR_NOT_IMPLEMENTED;
00254     default:
00255       NS_NOTREACHED("Unknown unit type");
00256       *aValue = 0;
00257       return NS_ERROR_UNEXPECTED;
00258   }
00259   return NS_OK;
00260 }
00261 
00262 NS_IMETHODIMP
00263 nsSVGLength::SetValue(float aValue)
00264 {
00265   nsresult rv = NS_OK;
00266 
00267   WillModify();
00268 
00269   switch (mSpecifiedUnitType) {
00270     case SVG_LENGTHTYPE_NUMBER:
00271     case SVG_LENGTHTYPE_PX:
00272       mValueInSpecifiedUnits = aValue;
00273       break;
00274     case SVG_LENGTHTYPE_MM:
00275       mValueInSpecifiedUnits = aValue * mmPerPixel();
00276       break;
00277     case SVG_LENGTHTYPE_CM:
00278       mValueInSpecifiedUnits = aValue * mmPerPixel() / 10.0f;
00279       break;
00280     case SVG_LENGTHTYPE_IN:
00281       mValueInSpecifiedUnits = aValue * mmPerPixel() / 25.4f;
00282       break;
00283     case SVG_LENGTHTYPE_PT:
00284       mValueInSpecifiedUnits = aValue * mmPerPixel() * 72.0f / 25.4f;
00285       break;
00286     case SVG_LENGTHTYPE_PC:
00287       mValueInSpecifiedUnits = aValue * mmPerPixel() * 72.0f / 24.4f / 12.0f;
00288       break;
00289     case SVG_LENGTHTYPE_PERCENTAGE:
00290       mValueInSpecifiedUnits = aValue * 100.0f / AxisLength();
00291       break;
00292     case SVG_LENGTHTYPE_EMS:
00293     case SVG_LENGTHTYPE_EXS:
00294       NS_NOTYETIMPLEMENTED("SVG_LENGTHTYPE_EXS");
00295       mValueInSpecifiedUnits = 0;
00296       rv = NS_ERROR_NOT_IMPLEMENTED;
00297       break;
00298     default:
00299       NS_NOTREACHED("Unknown unit type");
00300       mValueInSpecifiedUnits = 0;
00301       rv = NS_ERROR_UNEXPECTED;
00302       break;
00303   }
00304 
00305   DidModify();
00306   return rv;
00307 }
00308 
00309 /* attribute float valueInSpecifiedUnits; */
00310 NS_IMETHODIMP
00311 nsSVGLength::GetValueInSpecifiedUnits(float *aValueInSpecifiedUnits)
00312 {
00313   *aValueInSpecifiedUnits = mValueInSpecifiedUnits;
00314   return NS_OK;
00315 }
00316 NS_IMETHODIMP
00317 nsSVGLength::SetValueInSpecifiedUnits(float aValueInSpecifiedUnits)
00318 {
00319   WillModify();
00320   mValueInSpecifiedUnits = aValueInSpecifiedUnits;
00321   DidModify();
00322   return NS_OK;
00323 }
00324 
00325 /* attribute DOMString valueAsString; */
00326 NS_IMETHODIMP
00327 nsSVGLength::GetValueAsString(nsAString & aValueAsString)
00328 {
00329   PRUnichar buf[24];
00330   nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(PRUnichar),
00331                             NS_LITERAL_STRING("%g").get(),
00332                             (double)mValueInSpecifiedUnits);
00333   aValueAsString.Assign(buf);
00334 
00335   nsIAtom* UnitAtom = nsnull;
00336 
00337   switch (mSpecifiedUnitType) {
00338     case SVG_LENGTHTYPE_NUMBER:
00339       return NS_OK;
00340     case SVG_LENGTHTYPE_PX:
00341       UnitAtom = nsSVGAtoms::px;
00342       break;
00343     case SVG_LENGTHTYPE_MM:
00344       UnitAtom = nsSVGAtoms::mm;
00345       break;
00346     case SVG_LENGTHTYPE_CM:
00347       UnitAtom = nsSVGAtoms::cm;
00348       break;
00349     case SVG_LENGTHTYPE_IN:
00350       UnitAtom = nsSVGAtoms::in;
00351       break;
00352     case SVG_LENGTHTYPE_PT:
00353       UnitAtom = nsSVGAtoms::pt;
00354       break;
00355     case SVG_LENGTHTYPE_PC:
00356       UnitAtom = nsSVGAtoms::pc;
00357       break;
00358     case SVG_LENGTHTYPE_EMS:
00359       UnitAtom = nsSVGAtoms::ems;
00360       break;
00361     case SVG_LENGTHTYPE_EXS:
00362       UnitAtom = nsSVGAtoms::exs;
00363       break;
00364     case SVG_LENGTHTYPE_PERCENTAGE:
00365       UnitAtom = nsSVGAtoms::percentage;
00366       break;
00367     default:
00368       NS_NOTREACHED("Unknown unit");
00369       return NS_ERROR_UNEXPECTED;
00370   }
00371 
00372   nsAutoString unitString;
00373   UnitAtom->ToString(unitString);
00374   aValueAsString.Append(unitString);
00375 
00376   return NS_OK;
00377 }
00378 
00379 NS_IMETHODIMP
00380 nsSVGLength::SetValueAsString(const nsAString & aValueAsString)
00381 {
00382   nsresult rv = NS_OK;
00383   
00384   char *str = ToNewCString(aValueAsString);
00385 
00386   char* number = str;
00387   while (*number && isspace(*number))
00388     ++number;
00389 
00390   if (*number) {
00391     char *rest;
00392     double value = PR_strtod(number, &rest);
00393     if (rest!=number) {
00394       const char* unitStr = nsCRT::strtok(rest, "\x20\x9\xD\xA", &rest);
00395       PRUint16 unitType = SVG_LENGTHTYPE_UNKNOWN;
00396       if (!unitStr || *unitStr=='\0') {
00397         unitType = SVG_LENGTHTYPE_NUMBER;
00398       }
00399       else {
00400         nsCOMPtr<nsIAtom> unitAtom = do_GetAtom(unitStr);
00401         if (unitAtom == nsSVGAtoms::px)
00402           unitType = SVG_LENGTHTYPE_PX;
00403         else if (unitAtom == nsSVGAtoms::mm)
00404           unitType = SVG_LENGTHTYPE_MM;
00405         else if (unitAtom == nsSVGAtoms::cm)
00406           unitType = SVG_LENGTHTYPE_CM;
00407         else if (unitAtom == nsSVGAtoms::in)
00408           unitType = SVG_LENGTHTYPE_IN;
00409         else if (unitAtom == nsSVGAtoms::pt)
00410           unitType = SVG_LENGTHTYPE_PT;
00411         else if (unitAtom == nsSVGAtoms::pc)
00412           unitType = SVG_LENGTHTYPE_PC;
00413         else if (unitAtom == nsSVGAtoms::ems)
00414           unitType = SVG_LENGTHTYPE_EMS;
00415         else if (unitAtom == nsSVGAtoms::exs)
00416           unitType = SVG_LENGTHTYPE_EXS;
00417         else if (unitAtom == nsSVGAtoms::percentage)
00418           unitType = SVG_LENGTHTYPE_PERCENTAGE;
00419       }
00420       if (IsValidUnitType(unitType)){
00421         WillModify();
00422         mValueInSpecifiedUnits = (float)value;
00423         mSpecifiedUnitType     = unitType;
00424         DidModify();
00425       }
00426       else { // parse error
00427         // not a valid unit type
00428         rv = NS_ERROR_FAILURE;
00429         NS_ERROR("invalid length type");
00430       }
00431     }
00432     else { // parse error
00433       // no number
00434       rv = NS_ERROR_FAILURE;
00435     }
00436   }
00437   
00438   nsMemory::Free(str);
00439     
00440   return rv;
00441 }
00442 
00443 /* void newValueSpecifiedUnits (in unsigned short unitType, in float valueInSpecifiedUnits); */
00444 NS_IMETHODIMP
00445 nsSVGLength::NewValueSpecifiedUnits(PRUint16 unitType, float valueInSpecifiedUnits)
00446 {
00447   if (!IsValidUnitType(unitType))
00448     return NS_ERROR_FAILURE;
00449 
00450   PRBool observer_change = (unitType != mSpecifiedUnitType);
00451 
00452   WillModify();
00453   if (observer_change)
00454     MaybeRemoveAsObserver();
00455   mValueInSpecifiedUnits = valueInSpecifiedUnits;
00456   mSpecifiedUnitType     = unitType;
00457   if (observer_change)
00458     MaybeAddAsObserver();
00459   DidModify();
00460   
00461   return NS_OK;
00462 }
00463 
00464 /* void convertToSpecifiedUnits (in unsigned short unitType); */
00465 NS_IMETHODIMP
00466 nsSVGLength::ConvertToSpecifiedUnits(PRUint16 unitType)
00467 {
00468   if (!IsValidUnitType(unitType))
00469     return NS_ERROR_FAILURE;
00470 
00471   PRBool observer_change = (unitType != mSpecifiedUnitType);
00472 
00473   WillModify();
00474   if (observer_change)
00475     MaybeRemoveAsObserver();
00476   float valueInUserUnits;
00477   GetValue(&valueInUserUnits);
00478   mSpecifiedUnitType = unitType;
00479   SetValue(valueInUserUnits);
00480   if (observer_change)
00481     MaybeAddAsObserver();
00482   DidModify();
00483   
00484   return NS_OK;
00485 }
00486 
00487 /* float getTransformedValue (in nsIDOMSVGMatrix matrix); */
00488 NS_IMETHODIMP
00489 nsSVGLength::GetTransformedValue(nsIDOMSVGMatrix *matrix,
00490                                  float *_retval)
00491 {
00492 // XXX we don't have enough information here. is it the length part of
00493 // a coordinate pair (in which case it should transform like a point)
00494 // or is it used like a vector-component (in which case it doesn't
00495 // translate)
00496 
00497   // maybe we should remove this method since it isn't part of the spec?
00498   // if not, null check when implementing - this method can be used by scripts!
00499   // if (!matrix)
00500   //   return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
00501 
00502   NS_NOTYETIMPLEMENTED("nsSVGLength::GetTransformedValue");
00503   return NS_ERROR_NOT_IMPLEMENTED;
00504 }
00505 
00506 
00507 //----------------------------------------------------------------------
00508 // nsISVGLength methods:
00509 NS_IMETHODIMP
00510 nsSVGLength::SetContext(nsSVGCoordCtx* context)
00511 {
00512   /* Unless our unit type is SVG_LENGTHTYPE_NUMBER or SVG_LENGTHTYPE_PX, our
00513      user unit value is determined by our context and we must notify our
00514      observers that we have changed. */
00515 
00516   if (mSpecifiedUnitType != SVG_LENGTHTYPE_NUMBER &&
00517       mSpecifiedUnitType != SVG_LENGTHTYPE_PX) {
00518     WillModify(mod_context);
00519     MaybeRemoveAsObserver();
00520   }
00521 
00522   mContext = context;
00523 
00524   if (mSpecifiedUnitType != SVG_LENGTHTYPE_NUMBER &&
00525       mSpecifiedUnitType != SVG_LENGTHTYPE_PX) {
00526     MaybeAddAsObserver();
00527     DidModify(mod_context);
00528   }
00529   return NS_OK;
00530 }
00531 
00532 
00533 //----------------------------------------------------------------------
00534 // Implementation helpers:
00535 
00536 float nsSVGLength::mmPerPixel()
00537 {
00538   if (!mContext) {
00539     NS_WARNING("no context in mmPerPixel()");
00540     return 1.0f;
00541   }
00542   
00543   float mmPerPx = mContext->GetMillimeterPerPixel();
00544 
00545   if (mmPerPx == 0.0f) {
00546     NS_ASSERTION(mmPerPx != 0.0f, "invalid mm/pixels");
00547     mmPerPx = 1e-4f; // some small value
00548   }
00549   
00550   return mmPerPx;
00551 }
00552 
00553 float nsSVGLength::AxisLength()
00554 {
00555   if (!mContext) {
00556     NS_WARNING("no context in AxisLength()");
00557     return 1.0f;
00558   }
00559 
00560   nsCOMPtr<nsIDOMSVGNumber> num = mContext->GetLength();
00561   NS_ASSERTION(num != nsnull, "null interface");
00562   float d;
00563   num->GetValue(&d);
00564 
00565   if (d == 0.0f) {
00566     NS_WARNING("zero axis length");
00567     d = 1e-20f;
00568   }
00569 
00570   return d;
00571 }
00572 
00573 PRBool nsSVGLength::IsValidUnitType(PRUint16 unit)
00574 {
00575   if (unit > SVG_LENGTHTYPE_UNKNOWN && unit <= SVG_LENGTHTYPE_PC)
00576     return PR_TRUE;
00577 
00578   return PR_FALSE;
00579 }
00580 
00581 void nsSVGLength::MaybeAddAsObserver()
00582 {
00583   if ((mSpecifiedUnitType==SVG_LENGTHTYPE_PERCENTAGE) &&
00584       mContext) {
00585     nsCOMPtr<nsIDOMSVGNumber> num = mContext->GetLength();
00586     NS_ADD_SVGVALUE_OBSERVER(num);
00587   }
00588 }
00589 
00590 void nsSVGLength::MaybeRemoveAsObserver()
00591 {
00592   if ((mSpecifiedUnitType==SVG_LENGTHTYPE_PERCENTAGE) &&
00593       mContext) {
00594     nsCOMPtr<nsIDOMSVGNumber> num = mContext->GetLength();
00595     NS_REMOVE_SVGVALUE_OBSERVER(num);
00596   }
00597 }
00598