/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_SVGCONTENTUTILS_H #define MOZILLA_SVGCONTENTUTILS_H // include math.h to pick up definition of M_ maths defines e.g. M_PI #include #include "mozilla/gfx/2D.h" // for StrokeOptions #include "mozilla/gfx/Matrix.h" #include "mozilla/RangedPtr.h" #include "nsError.h" #include "nsStringFwd.h" #include "gfx2DGlue.h" class nsIContent; class nsIDocument; class nsIFrame; class nsStyleContext; class nsStyleCoord; class nsSVGElement; namespace mozilla { class nsSVGAnimatedTransformList; class SVGAnimatedPreserveAspectRatio; class SVGContextPaint; class SVGPreserveAspectRatio; namespace dom { class Element; class SVGSVGElement; } // namespace dom namespace gfx { class Matrix; } // namespace gfx } // namespace mozilla #define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512 /** * SVGTransformTypes controls the transforms that PrependLocalTransformsTo * applies. * * If aWhich is eAllTransforms, then all the transforms from the coordinate * space established by this element for its children to the coordinate * space established by this element's parent element for this element, are * included. * * If aWhich is eUserSpaceToParent, then only the transforms from this * element's userspace to the coordinate space established by its parent is * included. This includes any transforms introduced by the 'transform' * attribute, transform animations and animateMotion, but not any offsets * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox' * attribute. (SVG userspace is defined to be the coordinate space in which * coordinates on an element apply.) * * If aWhich is eChildToUserSpace, then only the transforms from the * coordinate space established by this element for its childre to this * elements userspace are included. This includes any offsets due to e.g. * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but * does not include any transforms due to the 'transform' attribute. */ enum SVGTransformTypes { eAllTransforms, eUserSpaceToParent, eChildToUserSpace }; inline bool IsSVGWhitespace(char aChar) { return aChar == '\x20' || aChar == '\x9' || aChar == '\xD' || aChar == '\xA'; } inline bool IsSVGWhitespace(char16_t aChar) { return aChar == char16_t('\x20') || aChar == char16_t('\x9') || aChar == char16_t('\xD') || aChar == char16_t('\xA'); } /** * Functions generally used by SVG Content classes. Functions here * should not generally depend on layout methods/classes e.g. nsSVGUtils */ class SVGContentUtils { public: typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Matrix Matrix; typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::StrokeOptions StrokeOptions; typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio; typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio; /* * Get the outer SVG element of an nsIContent */ static mozilla::dom::SVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement); /** * Activates the animation element aContent as a result of navigation to the * fragment identifier that identifies aContent. aContent must be an instance * of nsSVGAnimationElement. * * This is just a shim to allow nsSVGAnimationElement::ActivateByHyperlink to * be called from layout/base without adding to that directory's include paths. */ static void ActivateByHyperlink(nsIContent *aContent); /** * Moz2D's StrokeOptions requires someone else to own its mDashPattern * buffer, which is a pain when you want to initialize a StrokeOptions object * in a helper function and pass it out. This sub-class owns the mDashPattern * buffer so that consumers of such a helper function don't need to worry * about creating it, passing it in, or deleting it. (An added benefit is * that in the typical case when stroke-dasharray is short it will avoid * allocating.) */ struct AutoStrokeOptions : public StrokeOptions { AutoStrokeOptions() { MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this"); } ~AutoStrokeOptions() { if (mDashPattern && mDashPattern != mSmallArray) { delete [] mDashPattern; } } /** * Creates the buffer to store the stroke-dasharray, assuming out-of-memory * does not occur. The buffer's address is assigned to mDashPattern and * returned to the caller as a non-const pointer (so that the caller can * initialize the values in the buffer, since mDashPattern is const). */ Float* InitDashPattern(size_t aDashCount) { if (aDashCount <= MOZ_ARRAY_LENGTH(mSmallArray)) { mDashPattern = mSmallArray; return mSmallArray; } Float* nonConstArray = new (mozilla::fallible) Float[aDashCount]; mDashPattern = nonConstArray; return nonConstArray; } void DiscardDashPattern() { if (mDashPattern && mDashPattern != mSmallArray) { delete [] mDashPattern; } mDashLength = 0; mDashPattern = nullptr; } private: // Most dasharrays will fit in this and save us allocating Float mSmallArray[16]; }; enum StrokeOptionFlags { eAllStrokeOptions, eIgnoreStrokeDashing }; /** * Note: the linecap style returned in aStrokeOptions is not valid when * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing, * since when aElement has no corners the rendered linecap style depends on * whether or not the stroke is dashed. */ static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, nsSVGElement* aElement, nsStyleContext* aStyleContext, mozilla::SVGContextPaint* aContextPaint, StrokeOptionFlags aFlags = eAllStrokeOptions); /** * Returns the current computed value of the CSS property 'stroke-width' for * the given element. aStyleContext may be provided as an optimization. * aContextPaint is also optional. * * Note that this function does NOT take account of the value of the 'stroke' * and 'stroke-opacity' properties to, say, return zero if they are "none" or * "0", respectively. */ static Float GetStrokeWidth(nsSVGElement* aElement, nsStyleContext* aStyleContext, mozilla::SVGContextPaint* aContextPaint); /* * Get the number of CSS px (user units) per em (i.e. the em-height in user * units) for an nsIContent * * XXX document the conditions under which these may fail, and what they * return in those cases. */ static float GetFontSize(mozilla::dom::Element *aElement); static float GetFontSize(nsIFrame *aFrame); static float GetFontSize(nsStyleContext *aStyleContext); /* * Get the number of CSS px (user units) per ex (i.e. the x-height in user * units) for an nsIContent * * XXX document the conditions under which these may fail, and what they * return in those cases. */ static float GetFontXHeight(mozilla::dom::Element *aElement); static float GetFontXHeight(nsIFrame *aFrame); static float GetFontXHeight(nsStyleContext *aStyleContext); /* * Report a localized error message to the error console. */ static nsresult ReportToConsole(nsIDocument* doc, const char* aWarning, const char16_t **aParams, uint32_t aParamsLength); static Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM); /** * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect * aRect. * @param aToBoundsSpace transforms from source space to the space aBounds * should be computed in. Must be rectilinear. * @param aToNonScalingStrokeSpace transforms from source * space to the space in which non-scaling stroke should be applied. * Must be rectilinear. */ static void RectilinearGetStrokeBounds(const Rect& aRect, const Matrix& aToBoundsSpace, const Matrix& aToNonScalingStrokeSpace, float aStrokeWidth, Rect* aBounds); /** * Check if this is one of the SVG elements that SVG 1.1 Full says * establishes a viewport: svg, symbol, image or foreignObject. */ static bool EstablishesViewport(nsIContent *aContent); static nsSVGElement* GetNearestViewportElement(nsIContent *aContent); /* enum for specifying coordinate direction for ObjectSpace/UserSpace */ enum ctxDirection { X, Y, XY }; /** * Computes sqrt((aWidth^2 + aHeight^2)/2); */ static double ComputeNormalizedHypotenuse(double aWidth, double aHeight); /* Returns the angle halfway between the two specified angles */ static float AngleBisect(float a1, float a2); /* Generate a viewbox to viewport tranformation matrix */ static Matrix GetViewBoxTransform(float aViewportWidth, float aViewportHeight, float aViewboxX, float aViewboxY, float aViewboxWidth, float aViewboxHeight, const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio); static Matrix GetViewBoxTransform(float aViewportWidth, float aViewportHeight, float aViewboxX, float aViewboxY, float aViewboxWidth, float aViewboxHeight, const SVGPreserveAspectRatio &aPreserveAspectRatio); static mozilla::RangedPtr GetStartRangedPtr(const nsAString& aString); static mozilla::RangedPtr GetEndRangedPtr(const nsAString& aString); /** * True if 'aCh' is a decimal digit. */ static inline bool IsDigit(char16_t aCh) { return aCh >= '0' && aCh <= '9'; } /** * Assuming that 'aCh' is a decimal digit, return its numeric value. */ static inline uint32_t DecimalDigitValue(char16_t aCh) { MOZ_ASSERT(IsDigit(aCh), "Digit expected"); return aCh - '0'; } /** * Parses the sign (+ or -) of a number and moves aIter to the next * character if a sign is found. * @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1 * @return false if we hit the end of the string (i.e. if aIter is initially * at aEnd, or if we reach aEnd right after the sign character). */ static inline bool ParseOptionalSign(mozilla::RangedPtr& aIter, const mozilla::RangedPtr& aEnd, int32_t& aSignMultiplier) { if (aIter == aEnd) { return false; } aSignMultiplier = *aIter == '-' ? -1 : 1; mozilla::RangedPtr iter(aIter); if (*iter == '-' || *iter == '+') { ++iter; if (iter == aEnd) { return false; } } aIter = iter; return true; } /** * Parse a number of the form: * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)? * Parsing fails if the number cannot be represented by a floatType. * If parsing succeeds, aIter is updated so that it points to the character * after the end of the number, otherwise it is left unchanged */ template static bool ParseNumber(mozilla::RangedPtr& aIter, const mozilla::RangedPtr& aEnd, floatType& aValue); /** * Parse a number of the form: * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)? * Parsing fails if there is anything left over after the number, * or the number cannot be represented by a floatType. */ template static bool ParseNumber(const nsAString& aString, floatType& aValue); /** * Parse an integer of the form: * integer ::= [+-]? [0-9]+ * The returned number is clamped to an int32_t if outside that range. * If parsing succeeds, aIter is updated so that it points to the character * after the end of the number, otherwise it is left unchanged */ static bool ParseInteger(mozilla::RangedPtr& aIter, const mozilla::RangedPtr& aEnd, int32_t& aValue); /** * Parse an integer of the form: * integer ::= [+-]? [0-9]+ * The returned number is clamped to an int32_t if outside that range. * Parsing fails if there is anything left over after the number. */ static bool ParseInteger(const nsAString& aString, int32_t& aValue); /** * Converts an nsStyleCoord into a userspace value. Handles units * Factor (straight userspace), Coord (dimensioned), and Percent (of * aContent's SVG viewport) */ static float CoordToFloat(nsSVGElement *aContent, const nsStyleCoord &aCoord); /** * Parse the SVG path string * Returns a path * string formatted as an SVG path */ static already_AddRefed GetPath(const nsAString& aPathString); /** * Returns true if aContent is one of the elements whose stroke is guaranteed * to have no corners: circle or ellipse */ static bool ShapeTypeHasNoCorners(const nsIContent* aContent); /** * Prepends an element's local transforms to the transform matrix. * This is a helper for nsSVGElement::PrependLocalTransformsTo. * Any callers probably really want to call that method instead of this one. */ static gfxMatrix PrependLocalTransformsTo( const gfxMatrix &aMatrix, SVGTransformTypes aWhich, const Matrix* aAnimateMotionTransform, const mozilla::nsSVGAnimatedTransformList* aTransforms); }; #endif