Line data Source code
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 MOZILLA_SVGCONTENTUTILS_H
8 : #define MOZILLA_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 "gfx2DGlue.h"
19 :
20 : class nsIContent;
21 : class nsIDocument;
22 : class nsIFrame;
23 : class nsStyleContext;
24 : class nsStyleCoord;
25 : class nsSVGElement;
26 :
27 : namespace mozilla {
28 : class nsSVGAnimatedTransformList;
29 : class SVGAnimatedPreserveAspectRatio;
30 : class SVGContextPaint;
31 : class SVGPreserveAspectRatio;
32 : namespace dom {
33 : class Element;
34 : class SVGSVGElement;
35 : } // namespace dom
36 :
37 : } // namespace mozilla
38 :
39 : #define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512
40 :
41 : /**
42 : * SVGTransformTypes controls the transforms that PrependLocalTransformsTo
43 : * applies.
44 : *
45 : * If aWhich is eAllTransforms, then all the transforms from the coordinate
46 : * space established by this element for its children to the coordinate
47 : * space established by this element's parent element for this element, are
48 : * included.
49 : *
50 : * If aWhich is eUserSpaceToParent, then only the transforms from this
51 : * element's userspace to the coordinate space established by its parent is
52 : * included. This includes any transforms introduced by the 'transform'
53 : * attribute, transform animations and animateMotion, but not any offsets
54 : * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox'
55 : * attribute. (SVG userspace is defined to be the coordinate space in which
56 : * coordinates on an element apply.)
57 : *
58 : * If aWhich is eChildToUserSpace, then only the transforms from the
59 : * coordinate space established by this element for its childre to this
60 : * elements userspace are included. This includes any offsets due to e.g.
61 : * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but
62 : * does not include any transforms due to the 'transform' attribute.
63 : */
64 : enum SVGTransformTypes {
65 : eAllTransforms,
66 : eUserSpaceToParent,
67 : eChildToUserSpace
68 : };
69 :
70 : inline bool
71 : IsSVGWhitespace(char aChar)
72 : {
73 : return aChar == '\x20' || aChar == '\x9' ||
74 : aChar == '\xD' || aChar == '\xA';
75 : }
76 :
77 : inline bool
78 9356 : IsSVGWhitespace(char16_t aChar)
79 : {
80 7964 : return aChar == char16_t('\x20') || aChar == char16_t('\x9') ||
81 17320 : aChar == char16_t('\xD') || aChar == char16_t('\xA');
82 : }
83 :
84 : /**
85 : * Functions generally used by SVG Content classes. Functions here
86 : * should not generally depend on layout methods/classes e.g. nsSVGUtils
87 : */
88 : class SVGContentUtils
89 : {
90 : public:
91 : typedef mozilla::gfx::Float Float;
92 : typedef mozilla::gfx::Matrix Matrix;
93 : typedef mozilla::gfx::Rect Rect;
94 : typedef mozilla::gfx::StrokeOptions StrokeOptions;
95 : typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
96 : typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio;
97 :
98 : /*
99 : * Get the outer SVG element of an nsIContent
100 : */
101 : static mozilla::dom::SVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement);
102 :
103 : /**
104 : * Activates the animation element aContent as a result of navigation to the
105 : * fragment identifier that identifies aContent. aContent must be an instance
106 : * of nsSVGAnimationElement.
107 : *
108 : * This is just a shim to allow nsSVGAnimationElement::ActivateByHyperlink to
109 : * be called from layout/base without adding to that directory's include paths.
110 : */
111 : static void ActivateByHyperlink(nsIContent *aContent);
112 :
113 : /**
114 : * Moz2D's StrokeOptions requires someone else to own its mDashPattern
115 : * buffer, which is a pain when you want to initialize a StrokeOptions object
116 : * in a helper function and pass it out. This sub-class owns the mDashPattern
117 : * buffer so that consumers of such a helper function don't need to worry
118 : * about creating it, passing it in, or deleting it. (An added benefit is
119 : * that in the typical case when stroke-dasharray is short it will avoid
120 : * allocating.)
121 : */
122 : struct AutoStrokeOptions : public StrokeOptions {
123 84 : AutoStrokeOptions()
124 84 : {
125 84 : MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this");
126 84 : }
127 168 : ~AutoStrokeOptions() {
128 84 : if (mDashPattern && mDashPattern != mSmallArray) {
129 0 : delete [] mDashPattern;
130 : }
131 84 : }
132 : /**
133 : * Creates the buffer to store the stroke-dasharray, assuming out-of-memory
134 : * does not occur. The buffer's address is assigned to mDashPattern and
135 : * returned to the caller as a non-const pointer (so that the caller can
136 : * initialize the values in the buffer, since mDashPattern is const).
137 : */
138 0 : Float* InitDashPattern(size_t aDashCount) {
139 0 : if (aDashCount <= MOZ_ARRAY_LENGTH(mSmallArray)) {
140 0 : mDashPattern = mSmallArray;
141 0 : return mSmallArray;
142 : }
143 0 : Float* nonConstArray = new (mozilla::fallible) Float[aDashCount];
144 0 : mDashPattern = nonConstArray;
145 0 : return nonConstArray;
146 : }
147 0 : void DiscardDashPattern() {
148 0 : if (mDashPattern && mDashPattern != mSmallArray) {
149 0 : delete [] mDashPattern;
150 : }
151 0 : mDashLength = 0;
152 0 : mDashPattern = nullptr;
153 0 : }
154 : private:
155 : // Most dasharrays will fit in this and save us allocating
156 : Float mSmallArray[16];
157 : };
158 :
159 : enum StrokeOptionFlags {
160 : eAllStrokeOptions,
161 : eIgnoreStrokeDashing
162 : };
163 : /**
164 : * Note: the linecap style returned in aStrokeOptions is not valid when
165 : * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing,
166 : * since when aElement has no corners the rendered linecap style depends on
167 : * whether or not the stroke is dashed.
168 : */
169 : static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
170 : nsSVGElement* aElement,
171 : nsStyleContext* aStyleContext,
172 : mozilla::SVGContextPaint* aContextPaint,
173 : StrokeOptionFlags aFlags = eAllStrokeOptions);
174 :
175 : /**
176 : * Returns the current computed value of the CSS property 'stroke-width' for
177 : * the given element. aStyleContext may be provided as an optimization.
178 : * aContextPaint is also optional.
179 : *
180 : * Note that this function does NOT take account of the value of the 'stroke'
181 : * and 'stroke-opacity' properties to, say, return zero if they are "none" or
182 : * "0", respectively.
183 : */
184 : static Float GetStrokeWidth(nsSVGElement* aElement,
185 : nsStyleContext* aStyleContext,
186 : mozilla::SVGContextPaint* aContextPaint);
187 :
188 : /*
189 : * Get the number of CSS px (user units) per em (i.e. the em-height in user
190 : * units) for an nsIContent
191 : *
192 : * XXX document the conditions under which these may fail, and what they
193 : * return in those cases.
194 : */
195 : static float GetFontSize(mozilla::dom::Element *aElement);
196 : static float GetFontSize(nsIFrame *aFrame);
197 : static float GetFontSize(nsStyleContext *aStyleContext);
198 : /*
199 : * Get the number of CSS px (user units) per ex (i.e. the x-height in user
200 : * units) for an nsIContent
201 : *
202 : * XXX document the conditions under which these may fail, and what they
203 : * return in those cases.
204 : */
205 : static float GetFontXHeight(mozilla::dom::Element *aElement);
206 : static float GetFontXHeight(nsIFrame *aFrame);
207 : static float GetFontXHeight(nsStyleContext *aStyleContext);
208 :
209 : /*
210 : * Report a localized error message to the error console.
211 : */
212 : static nsresult ReportToConsole(nsIDocument* doc,
213 : const char* aWarning,
214 : const char16_t **aParams,
215 : uint32_t aParamsLength);
216 :
217 : static Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM);
218 :
219 : /**
220 : * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect
221 : * aRect.
222 : * @param aToBoundsSpace transforms from source space to the space aBounds
223 : * should be computed in. Must be rectilinear.
224 : * @param aToNonScalingStrokeSpace transforms from source
225 : * space to the space in which non-scaling stroke should be applied.
226 : * Must be rectilinear.
227 : */
228 : static void
229 : RectilinearGetStrokeBounds(const Rect& aRect,
230 : const Matrix& aToBoundsSpace,
231 : const Matrix& aToNonScalingStrokeSpace,
232 : float aStrokeWidth,
233 : Rect* aBounds);
234 :
235 : /**
236 : * Check if this is one of the SVG elements that SVG 1.1 Full says
237 : * establishes a viewport: svg, symbol, image or foreignObject.
238 : */
239 : static bool EstablishesViewport(nsIContent *aContent);
240 :
241 : static nsSVGElement*
242 : GetNearestViewportElement(nsIContent *aContent);
243 :
244 : /* enum for specifying coordinate direction for ObjectSpace/UserSpace */
245 : enum ctxDirection { X, Y, XY };
246 :
247 : /**
248 : * Computes sqrt((aWidth^2 + aHeight^2)/2);
249 : */
250 : static double ComputeNormalizedHypotenuse(double aWidth, double aHeight);
251 :
252 : /* Returns the angle halfway between the two specified angles */
253 : static float
254 : AngleBisect(float a1, float a2);
255 :
256 : /* Generate a viewbox to viewport tranformation matrix */
257 :
258 : static Matrix
259 : GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
260 : float aViewboxX, float aViewboxY,
261 : float aViewboxWidth, float aViewboxHeight,
262 : const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio);
263 :
264 : static Matrix
265 : GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
266 : float aViewboxX, float aViewboxY,
267 : float aViewboxWidth, float aViewboxHeight,
268 : const SVGPreserveAspectRatio &aPreserveAspectRatio);
269 :
270 : static mozilla::RangedPtr<const char16_t>
271 : GetStartRangedPtr(const nsAString& aString);
272 :
273 : static mozilla::RangedPtr<const char16_t>
274 : GetEndRangedPtr(const nsAString& aString);
275 :
276 : /**
277 : * True if 'aCh' is a decimal digit.
278 : */
279 21951 : static inline bool IsDigit(char16_t aCh)
280 : {
281 21951 : return aCh >= '0' && aCh <= '9';
282 : }
283 :
284 : /**
285 : * Assuming that 'aCh' is a decimal digit, return its numeric value.
286 : */
287 8797 : static inline uint32_t DecimalDigitValue(char16_t aCh)
288 : {
289 8797 : MOZ_ASSERT(IsDigit(aCh), "Digit expected");
290 8797 : return aCh - '0';
291 : }
292 :
293 : /**
294 : * Parses the sign (+ or -) of a number and moves aIter to the next
295 : * character if a sign is found.
296 : * @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1
297 : * @return false if we hit the end of the string (i.e. if aIter is initially
298 : * at aEnd, or if we reach aEnd right after the sign character).
299 : */
300 : static inline bool
301 3483 : ParseOptionalSign(mozilla::RangedPtr<const char16_t>& aIter,
302 : const mozilla::RangedPtr<const char16_t>& aEnd,
303 : int32_t& aSignMultiplier)
304 : {
305 3483 : if (aIter == aEnd) {
306 0 : return false;
307 : }
308 3483 : aSignMultiplier = *aIter == '-' ? -1 : 1;
309 :
310 3483 : mozilla::RangedPtr<const char16_t> iter(aIter);
311 :
312 3483 : if (*iter == '-' || *iter == '+') {
313 737 : ++iter;
314 737 : if (iter == aEnd) {
315 0 : return false;
316 : }
317 : }
318 3483 : aIter = iter;
319 3483 : return true;
320 : }
321 :
322 : /**
323 : * Parse a number of the form:
324 : * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
325 : * Parsing fails if the number cannot be represented by a floatType.
326 : * If parsing succeeds, aIter is updated so that it points to the character
327 : * after the end of the number, otherwise it is left unchanged
328 : */
329 : template<class floatType>
330 : static bool
331 : ParseNumber(mozilla::RangedPtr<const char16_t>& aIter,
332 : const mozilla::RangedPtr<const char16_t>& aEnd,
333 : floatType& aValue);
334 :
335 : /**
336 : * Parse a number of the form:
337 : * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
338 : * Parsing fails if there is anything left over after the number,
339 : * or the number cannot be represented by a floatType.
340 : */
341 : template<class floatType>
342 : static bool
343 : ParseNumber(const nsAString& aString, floatType& aValue);
344 :
345 : /**
346 : * Parse an integer of the form:
347 : * integer ::= [+-]? [0-9]+
348 : * The returned number is clamped to an int32_t if outside that range.
349 : * If parsing succeeds, aIter is updated so that it points to the character
350 : * after the end of the number, otherwise it is left unchanged
351 : */
352 : static bool ParseInteger(mozilla::RangedPtr<const char16_t>& aIter,
353 : const mozilla::RangedPtr<const char16_t>& aEnd,
354 : int32_t& aValue);
355 :
356 : /**
357 : * Parse an integer of the form:
358 : * integer ::= [+-]? [0-9]+
359 : * The returned number is clamped to an int32_t if outside that range.
360 : * Parsing fails if there is anything left over after the number.
361 : */
362 : static bool ParseInteger(const nsAString& aString, int32_t& aValue);
363 :
364 : /**
365 : * Converts an nsStyleCoord into a userspace value. Handles units
366 : * Factor (straight userspace), Coord (dimensioned), and Percent (of
367 : * aContent's SVG viewport)
368 : */
369 : static float CoordToFloat(nsSVGElement *aContent,
370 : const nsStyleCoord &aCoord);
371 : /**
372 : * Parse the SVG path string
373 : * Returns a path
374 : * string formatted as an SVG path
375 : */
376 : static already_AddRefed<mozilla::gfx::Path>
377 : GetPath(const nsAString& aPathString);
378 :
379 : /**
380 : * Returns true if aContent is one of the elements whose stroke is guaranteed
381 : * to have no corners: circle or ellipse
382 : */
383 : static bool ShapeTypeHasNoCorners(const nsIContent* aContent);
384 : };
385 :
386 : #endif
|