summaryrefslogtreecommitdiffstats
path: root/dom/svg/SVGLength.h
blob: ab0cef140a83e94e5463b434e76eca809225621d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* -*- 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 DOM_SVG_SVGLENGTH_H_
#define DOM_SVG_SVGLENGTH_H_

#include "nsDebug.h"
#include "nsMathUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/dom/SVGLengthBinding.h"

namespace mozilla {

namespace dom {
class SVGElement;
}

/**
 * This SVGLength class is currently used for SVGLength *list* attributes only.
 * The class that is currently used for <length> attributes is
 * SVGAnimatedLength.
 *
 * The member mUnit should always be valid, but the member mValue may be
 * numeric_limits<float>::quiet_NaN() under one circumstances (see the comment
 * in SetValueAndUnit below). Even if mValue is valid, some methods may return
 * numeric_limits<float>::quiet_NaN() if they involve a unit conversion that
 * fails - see comments below.
 *
 * The DOM wrapper class for this class is DOMSVGLength.
 */
class SVGLength {
 public:
  SVGLength()
      : mValue(0.0f),
        mUnit(dom::SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN)  // caught by
                                                               // IsValid()
  {}

  SVGLength(float aValue, uint8_t aUnit) : mValue(aValue), mUnit(aUnit) {
    NS_ASSERTION(IsValid(), "Constructed an invalid length");
  }

  bool operator==(const SVGLength& rhs) const {
    return mValue == rhs.mValue && mUnit == rhs.mUnit;
  }

  void GetValueAsString(nsAString& aValue) const;

  /**
   * This method returns true, unless there was a parse failure, in which
   * case it returns false (and the length is left unchanged).
   */
  bool SetValueFromString(const nsAString& aString);

  /**
   * This will usually return a valid, finite number. There is one exception
   * though - see the comment in SetValueAndUnit().
   */
  float GetValueInCurrentUnits() const { return mValue; }

  uint8_t GetUnit() const { return mUnit; }

  void SetValueInCurrentUnits(float aValue) {
    mValue = aValue;
    NS_ASSERTION(IsValid(), "Set invalid SVGLength");
  }

  void SetValueAndUnit(float aValue, uint8_t aUnit) {
    mValue = aValue;
    mUnit = aUnit;

    // IsValid() should always be true, with one exception: if
    // SVGLengthListSMILType has to convert between unit types and the unit
    // conversion is undefined, it will end up passing in and setting
    // numeric_limits<float>::quiet_NaN(). Because of that we only check the
    // unit here, and allow mValue to be invalid. The painting code has to be
    // able to handle NaN anyway, since conversion to user units may fail in
    // general.

    NS_ASSERTION(IsValidUnitType(mUnit), "Set invalid SVGLength");
  }

  /**
   * If it's not possible to convert this length's value to user units, then
   * this method will return numeric_limits<float>::quiet_NaN().
   */
  float GetValueInUserUnits(const dom::SVGElement* aElement,
                            uint8_t aAxis) const {
    return mValue * GetUserUnitsPerUnit(aElement, aAxis);
  }

  /**
   * Get this length's value in the units specified.
   *
   * This method returns numeric_limits<float>::quiet_NaN() if it is not
   * possible to convert the value to the specified unit.
   */
  float GetValueInSpecifiedUnit(uint8_t aUnit, const dom::SVGElement* aElement,
                                uint8_t aAxis) const;

  bool IsPercentage() const {
    return mUnit == dom::SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE;
  }

  static bool IsValidUnitType(uint16_t aUnitType) {
    return aUnitType > dom::SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN &&
           aUnitType <= dom::SVGLength_Binding::SVG_LENGTHTYPE_PC;
  }

  static void GetUnitString(nsAString& aUnit, uint16_t aUnitType);

  static uint16_t GetUnitTypeForString(const nsAString& aUnit);

  /**
   * Returns the number of user units per current unit.
   *
   * This method returns numeric_limits<float>::quiet_NaN() if the conversion
   * factor between the length's current unit and user units is undefined (see
   * the comments for GetUserUnitsPerInch and GetUserUnitsPerPercent).
   */
  float GetUserUnitsPerUnit(const dom::SVGElement* aElement,
                            uint8_t aAxis) const;

 private:
#ifdef DEBUG
  bool IsValid() const {
    return std::isfinite(mValue) && IsValidUnitType(mUnit);
  }
#endif

  /**
   * The conversion factor between user units (CSS px) and CSS inches is
   * constant: 96 px per inch.
   */
  static float GetUserUnitsPerInch() { return 96.0; }

  /**
   * The conversion factor between user units and percentage units depends on
   * aElement being non-null, and on aElement having a viewport element
   * ancestor with only appropriate SVG elements between aElement and that
   * ancestor. If that's not the case, then the conversion factor is undefined.
   *
   * This function returns a non-negative value if the conversion factor is
   * defined, otherwise it returns numeric_limits<float>::quiet_NaN().
   */
  static float GetUserUnitsPerPercent(const dom::SVGElement* aElement,
                                      uint8_t aAxis);

  float mValue;
  uint8_t mUnit;
};

}  // namespace mozilla

#endif  // DOM_SVG_SVGLENGTH_H_