1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef DOM_SVG_SVGCONTENTUTILS_H_ 8 #define DOM_SVG_SVGCONTENTUTILS_H_ 9 10 // include math.h to pick up definition of M_ maths defines e.g. M_PI 11 #include <math.h> 12 13 #include "mozilla/gfx/2D.h" // for StrokeOptions 14 #include "mozilla/gfx/Matrix.h" 15 #include "mozilla/RangedPtr.h" 16 #include "nsError.h" 17 #include "nsStringFwd.h" 18 #include "nsTArray.h" 19 #include "gfx2DGlue.h" 20 #include "nsDependentSubstring.h" 21 22 class nsIContent; 23 24 class nsIFrame; 25 class nsPresContext; 26 27 namespace mozilla { 28 class ComputedStyle; 29 class SVGAnimatedTransformList; 30 class SVGAnimatedPreserveAspectRatio; 31 class SVGContextPaint; 32 class SVGPreserveAspectRatio; 33 union StyleLengthPercentageUnion; 34 namespace dom { 35 class Document; 36 class Element; 37 class SVGElement; 38 class SVGSVGElement; 39 class SVGViewportElement; 40 } // namespace dom 41 42 #define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512 43 44 /** 45 * SVGTransformTypes controls the transforms that PrependLocalTransformsTo 46 * applies. 47 * 48 * If aWhich is eAllTransforms, then all the transforms from the coordinate 49 * space established by this element for its children to the coordinate 50 * space established by this element's parent element for this element, are 51 * included. 52 * 53 * If aWhich is eUserSpaceToParent, then only the transforms from this 54 * element's userspace to the coordinate space established by its parent is 55 * included. This includes any transforms introduced by the 'transform' 56 * attribute, transform animations and animateMotion, but not any offsets 57 * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' 58 * attribute. (SVG userspace is defined to be the coordinate space in which 59 * coordinates on an element apply.) 60 * 61 * If aWhich is eChildToUserSpace, then only the transforms from the 62 * coordinate space established by this element for its childre to this 63 * elements userspace are included. This includes any offsets due to e.g. 64 * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but 65 * does not include any transforms due to the 'transform' attribute. 66 */ 67 enum SVGTransformTypes { 68 eAllTransforms, 69 eUserSpaceToParent, 70 eChildToUserSpace 71 }; 72 73 /** 74 * Functions generally used by SVG Content classes. Functions here 75 * should not generally depend on layout methods/classes e.g. SVGUtils 76 */ 77 class SVGContentUtils { 78 public: 79 using Float = gfx::Float; 80 using Matrix = gfx::Matrix; 81 using Rect = gfx::Rect; 82 using StrokeOptions = gfx::StrokeOptions; 83 84 /* 85 * Get the outer SVG element of an nsIContent 86 */ 87 static dom::SVGSVGElement* GetOuterSVGElement(dom::SVGElement* aSVGElement); 88 89 /** 90 * Moz2D's StrokeOptions requires someone else to own its mDashPattern 91 * buffer, which is a pain when you want to initialize a StrokeOptions object 92 * in a helper function and pass it out. This sub-class owns the mDashPattern 93 * buffer so that consumers of such a helper function don't need to worry 94 * about creating it, passing it in, or deleting it. (An added benefit is 95 * that in the typical case when stroke-dasharray is short it will avoid 96 * allocating.) 97 */ 98 struct AutoStrokeOptions : public StrokeOptions { AutoStrokeOptionsAutoStrokeOptions99 AutoStrokeOptions() { 100 MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this"); 101 } ~AutoStrokeOptionsAutoStrokeOptions102 ~AutoStrokeOptions() { 103 if (mDashPattern && mDashPattern != mSmallArray) { 104 delete[] mDashPattern; 105 } 106 } 107 /** 108 * Creates the buffer to store the stroke-dasharray, assuming out-of-memory 109 * does not occur. The buffer's address is assigned to mDashPattern and 110 * returned to the caller as a non-const pointer (so that the caller can 111 * initialize the values in the buffer, since mDashPattern is const). 112 */ InitDashPatternAutoStrokeOptions113 Float* InitDashPattern(size_t aDashCount) { 114 if (aDashCount <= MOZ_ARRAY_LENGTH(mSmallArray)) { 115 mDashPattern = mSmallArray; 116 return mSmallArray; 117 } 118 Float* nonConstArray = new (mozilla::fallible) Float[aDashCount]; 119 mDashPattern = nonConstArray; 120 return nonConstArray; 121 } DiscardDashPatternAutoStrokeOptions122 void DiscardDashPattern() { 123 if (mDashPattern && mDashPattern != mSmallArray) { 124 delete[] mDashPattern; 125 } 126 mDashLength = 0; 127 mDashPattern = nullptr; 128 } 129 130 private: 131 // Most dasharrays will fit in this and save us allocating 132 Float mSmallArray[16]; 133 }; 134 135 enum StrokeOptionFlags { eAllStrokeOptions, eIgnoreStrokeDashing }; 136 /** 137 * Note: the linecap style returned in aStrokeOptions is not valid when 138 * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing, 139 * since when aElement has no corners the rendered linecap style depends on 140 * whether or not the stroke is dashed. 141 */ 142 static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, 143 dom::SVGElement* aElement, 144 const ComputedStyle* aComputedStyle, 145 mozilla::SVGContextPaint* aContextPaint, 146 StrokeOptionFlags aFlags = eAllStrokeOptions); 147 148 /** 149 * Returns the current computed value of the CSS property 'stroke-width' for 150 * the given element. aComputedStyle may be provided as an optimization. 151 * aContextPaint is also optional. 152 * 153 * Note that this function does NOT take account of the value of the 'stroke' 154 * and 'stroke-opacity' properties to, say, return zero if they are "none" or 155 * "0", respectively. 156 */ 157 static Float GetStrokeWidth(dom::SVGElement* aElement, 158 const ComputedStyle* aComputedStyle, 159 mozilla::SVGContextPaint* aContextPaint); 160 161 /* 162 * Get the number of CSS px (user units) per em (i.e. the em-height in user 163 * units) for an nsIContent 164 * 165 * XXX document the conditions under which these may fail, and what they 166 * return in those cases. 167 */ 168 static float GetFontSize(mozilla::dom::Element* aElement); 169 static float GetFontSize(nsIFrame* aFrame); 170 static float GetFontSize(ComputedStyle*, nsPresContext*); 171 /* 172 * Get the number of CSS px (user units) per ex (i.e. the x-height in user 173 * units) for an nsIContent 174 * 175 * XXX document the conditions under which these may fail, and what they 176 * return in those cases. 177 */ 178 static float GetFontXHeight(mozilla::dom::Element* aElement); 179 static float GetFontXHeight(nsIFrame* aFrame); 180 static float GetFontXHeight(ComputedStyle*, nsPresContext*); 181 182 /* 183 * Report a localized error message to the error console. 184 */ 185 static nsresult ReportToConsole(dom::Document* doc, const char* aWarning, 186 const nsTArray<nsString>& aParams); 187 188 static Matrix GetCTM(dom::SVGElement* aElement, bool aScreenCTM); 189 190 /** 191 * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect 192 * aRect. 193 * @param aToBoundsSpace transforms from source space to the space aBounds 194 * should be computed in. Must be rectilinear. 195 * @param aToNonScalingStrokeSpace transforms from source 196 * space to the space in which non-scaling stroke should be applied. 197 * Must be rectilinear. 198 */ 199 static void RectilinearGetStrokeBounds(const Rect& aRect, 200 const Matrix& aToBoundsSpace, 201 const Matrix& aToNonScalingStrokeSpace, 202 float aStrokeWidth, Rect* aBounds); 203 204 /** 205 * Check if this is one of the SVG elements that SVG 1.1 Full says 206 * establishes a viewport: svg, symbol, image or foreignObject. 207 */ 208 static bool EstablishesViewport(nsIContent* aContent); 209 210 static mozilla::dom::SVGViewportElement* GetNearestViewportElement( 211 const nsIContent* aContent); 212 213 /* enum for specifying coordinate direction for ObjectSpace/UserSpace */ 214 enum ctxDirection { X, Y, XY }; 215 216 /** 217 * Computes sqrt((aWidth^2 + aHeight^2)/2); 218 */ 219 static double ComputeNormalizedHypotenuse(double aWidth, double aHeight); 220 221 /* Returns the angle halfway between the two specified angles */ 222 static float AngleBisect(float a1, float a2); 223 224 /* Generate a viewbox to viewport transformation matrix */ 225 226 static Matrix GetViewBoxTransform( 227 float aViewportWidth, float aViewportHeight, float aViewboxX, 228 float aViewboxY, float aViewboxWidth, float aViewboxHeight, 229 const SVGAnimatedPreserveAspectRatio& aPreserveAspectRatio); 230 231 static Matrix GetViewBoxTransform( 232 float aViewportWidth, float aViewportHeight, float aViewboxX, 233 float aViewboxY, float aViewboxWidth, float aViewboxHeight, 234 const SVGPreserveAspectRatio& aPreserveAspectRatio); 235 236 static mozilla::RangedPtr<const char16_t> GetStartRangedPtr( 237 const nsAString& aString); 238 239 static mozilla::RangedPtr<const char16_t> GetEndRangedPtr( 240 const nsAString& aString); 241 242 /** 243 * Parses the sign (+ or -) of a number and moves aIter to the next 244 * character if a sign is found. 245 * @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1 246 * @return false if we hit the end of the string (i.e. if aIter is initially 247 * at aEnd, or if we reach aEnd right after the sign character). 248 */ ParseOptionalSign(mozilla::RangedPtr<const char16_t> & aIter,const mozilla::RangedPtr<const char16_t> & aEnd,int32_t & aSignMultiplier)249 static inline bool ParseOptionalSign( 250 mozilla::RangedPtr<const char16_t>& aIter, 251 const mozilla::RangedPtr<const char16_t>& aEnd, 252 int32_t& aSignMultiplier) { 253 if (aIter == aEnd) { 254 return false; 255 } 256 aSignMultiplier = *aIter == '-' ? -1 : 1; 257 258 mozilla::RangedPtr<const char16_t> iter(aIter); 259 260 if (*iter == '-' || *iter == '+') { 261 ++iter; 262 if (iter == aEnd) { 263 return false; 264 } 265 } 266 aIter = iter; 267 return true; 268 } 269 270 /** 271 * Parse a number of the form: 272 * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] 273 * integer)? Parsing fails if the number cannot be represented by a floatType. 274 * If parsing succeeds, aIter is updated so that it points to the character 275 * after the end of the number, otherwise it is left unchanged 276 */ 277 template <class floatType> 278 static bool ParseNumber(mozilla::RangedPtr<const char16_t>& aIter, 279 const mozilla::RangedPtr<const char16_t>& aEnd, 280 floatType& aValue); 281 282 /** 283 * Parse a number of the form: 284 * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] 285 * integer)? Parsing fails if there is anything left over after the number, or 286 * the number cannot be represented by a floatType. 287 */ 288 template <class floatType> 289 static bool ParseNumber(const nsAString& aString, floatType& aValue); 290 291 /** 292 * Parse an integer of the form: 293 * integer ::= [+-]? [0-9]+ 294 * The returned number is clamped to an int32_t if outside that range. 295 * If parsing succeeds, aIter is updated so that it points to the character 296 * after the end of the number, otherwise it is left unchanged 297 */ 298 static bool ParseInteger(mozilla::RangedPtr<const char16_t>& aIter, 299 const mozilla::RangedPtr<const char16_t>& aEnd, 300 int32_t& aValue); 301 302 /** 303 * Parse an integer of the form: 304 * integer ::= [+-]? [0-9]+ 305 * The returned number is clamped to an int32_t if outside that range. 306 * Parsing fails if there is anything left over after the number. 307 */ 308 static bool ParseInteger(const nsAString& aString, int32_t& aValue); 309 310 // XXX This should rather use LengthPercentage instead of 311 // StyleLengthPercentageUnion, but that's a type alias defined in 312 // ServoStyleConsts.h, and we don't want to avoid including that large header 313 // with all its dependencies. If a forwarding header were generated by 314 // cbindgen, we could include that. 315 // https://github.com/eqrion/cbindgen/issues/617 addresses this. 316 /** 317 * Converts a LengthPercentage into a userspace value, resolving percentage 318 * values relative to aContent's SVG viewport. 319 */ 320 static float CoordToFloat(dom::SVGElement* aContent, 321 const StyleLengthPercentageUnion&, 322 uint8_t aCtxType = SVGContentUtils::XY); 323 /** 324 * Parse the SVG path string 325 * Returns a path 326 * string formatted as an SVG path 327 */ 328 static already_AddRefed<mozilla::gfx::Path> GetPath( 329 const nsAString& aPathString); 330 331 /** 332 * Returns true if aContent is one of the elements whose stroke is guaranteed 333 * to have no corners: circle or ellipse 334 */ 335 static bool ShapeTypeHasNoCorners(const nsIContent* aContent); 336 337 /** 338 * Return one token in aString, aString may have leading and trailing 339 * whitespace; aSuccess will be set to false if there is no token or more than 340 * one token, otherwise it's set to true. 341 */ 342 static nsDependentSubstring GetAndEnsureOneToken(const nsAString& aString, 343 bool& aSuccess); 344 }; 345 346 } // namespace mozilla 347 348 #endif // DOM_SVG_SVGCONTENTUTILS_H_ 349