tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGLength.cpp (11202B)


      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 #include "SVGLength.h"
      8 
      9 #include <algorithm>
     10 #include <limits>
     11 
     12 #include "SVGContentUtils.h"
     13 #include "mozilla/dom/SVGElement.h"
     14 #include "nsCSSValue.h"
     15 #include "nsTextFormatter.h"
     16 
     17 using namespace mozilla::dom;
     18 using namespace mozilla::dom::SVGLength_Binding;
     19 
     20 namespace mozilla {
     21 
     22 // These types are numbered so that different length categories are in
     23 // contiguous ranges - See `SVGLength::Is[..]Unit()`.
     24 const unsigned short SVG_LENGTHTYPE_Q = 11;
     25 const unsigned short SVG_LENGTHTYPE_CH = 12;
     26 const unsigned short SVG_LENGTHTYPE_REM = 13;
     27 const unsigned short SVG_LENGTHTYPE_IC = 14;
     28 const unsigned short SVG_LENGTHTYPE_CAP = 15;
     29 const unsigned short SVG_LENGTHTYPE_LH = 16;
     30 const unsigned short SVG_LENGTHTYPE_RLH = 17;
     31 const unsigned short SVG_LENGTHTYPE_REX = 18;
     32 const unsigned short SVG_LENGTHTYPE_RCH = 19;
     33 const unsigned short SVG_LENGTHTYPE_RIC = 20;
     34 const unsigned short SVG_LENGTHTYPE_RCAP = 21;
     35 const unsigned short SVG_LENGTHTYPE_VW = 22;
     36 const unsigned short SVG_LENGTHTYPE_VH = 23;
     37 const unsigned short SVG_LENGTHTYPE_VMIN = 24;
     38 const unsigned short SVG_LENGTHTYPE_VMAX = 25;
     39 
     40 void SVGLength::GetValueAsString(nsAString& aValue) const {
     41  nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
     42 
     43  nsAutoString unitString;
     44  GetUnitString(unitString, mUnit);
     45  aValue.Append(unitString);
     46 }
     47 
     48 bool SVGLength::SetValueFromString(const nsAString& aString) {
     49  bool success;
     50  auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
     51 
     52  if (!success) {
     53    return false;
     54  }
     55 
     56  nsAString::const_iterator iter, end;
     57  aString.BeginReading(iter);
     58  aString.EndReading(end);
     59 
     60  float value;
     61 
     62  if (!SVGContentUtils::ParseNumber(iter, end, value)) {
     63    return false;
     64  }
     65 
     66  const nsAString& units = Substring(iter, end);
     67  uint16_t unitType = GetUnitTypeForString(units);
     68  if (unitType == SVG_LENGTHTYPE_UNKNOWN) {
     69    return false;
     70  }
     71  mValue = value;
     72  mUnit = uint8_t(unitType);
     73  return true;
     74 }
     75 
     76 /*static*/
     77 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
     78  return aUnit == SVG_LENGTHTYPE_NUMBER ||
     79         (aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_Q);
     80 }
     81 
     82 /*static*/
     83 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit) {
     84  return aUnit == SVG_LENGTHTYPE_EMS || aUnit == SVG_LENGTHTYPE_EXS ||
     85         (aUnit >= SVG_LENGTHTYPE_CH && aUnit <= SVG_LENGTHTYPE_RCAP);
     86 }
     87 
     88 /**
     89 * Helper to convert between different CSS absolute units without the need for
     90 * an element, which provides more flexibility at the DOM level (and without
     91 * the need for an intermediary conversion to user units, which avoids
     92 * unnecessary overhead and rounding error).
     93 *
     94 * Example usage: to find out how many centimeters there are per inch:
     95 *
     96 *   GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
     97 */
     98 /*static*/
     99 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
    100  MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
    101  MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
    102 
    103  static const float CSSAbsoluteUnitConversionFactors[7][7] = {
    104      // columns: px, cm, mm, in, pt, pc, q
    105      // px per...:
    106      {1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f,
    107       0.94488188988f},
    108      // cm per...:
    109      {0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
    110       0.42333333333333333f, 0.025f},
    111      // mm per...:
    112      {0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
    113       4.2333333333333333f, 0.25f},
    114      // in per...:
    115      {0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
    116       0.013888888888888889f, 0.16666666666666667f, 0.02204860853f},
    117      // pt per...:
    118      {0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f,
    119       0.70866141732f},
    120      // pc per...:
    121      {0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
    122       0.083333333333333333f, 1.0f, 16.9333333333f},
    123      // q per...:
    124      {1.0583333332f, 40.0f, 4.0f, 45.354336f, 1.41111111111f, 16.9333333333f,
    125       1.0f}};
    126 
    127  auto ToIndex = [](uint8_t aUnit) {
    128    return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
    129  };
    130 
    131  return CSSAbsoluteUnitConversionFactors[ToIndex(aUnits)][ToIndex(aPerUnit)];
    132 }
    133 
    134 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
    135                                         const SVGElement* aElement,
    136                                         uint8_t aAxis) const {
    137  if (aUnit == mUnit) {
    138    return mValue;
    139  }
    140  if ((aUnit == SVG_LENGTHTYPE_NUMBER && mUnit == SVG_LENGTHTYPE_PX) ||
    141      (aUnit == SVG_LENGTHTYPE_PX && mUnit == SVG_LENGTHTYPE_NUMBER)) {
    142    return mValue;
    143  }
    144  if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
    145    return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
    146  }
    147 
    148  // Otherwise we do a two step conversion via user units. This can only
    149  // succeed if aElement is non-null (although that's not sufficient to
    150  // guarantee success).
    151 
    152  SVGElementMetrics userSpaceMetrics(aElement);
    153 
    154  float userUnitsPerCurrentUnit = GetPixelsPerUnit(userSpaceMetrics, aAxis);
    155  float userUnitsPerNewUnit =
    156      SVGLength(0.0f, aUnit).GetPixelsPerUnit(userSpaceMetrics, aAxis);
    157 
    158  float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
    159 
    160  // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
    161  // be zero.
    162  if (std::isfinite(value)) {
    163    return value;
    164  }
    165  return std::numeric_limits<float>::quiet_NaN();
    166 }
    167 
    168 // Helpers:
    169 
    170 enum class ZoomType { Self, SelfFromRoot, None };
    171 
    172 /*static*/
    173 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
    174                                  uint8_t aUnitType, uint8_t aAxis,
    175                                  bool aApplyZoom) {
    176  auto zoomType = ZoomType::Self;
    177  float value = [&]() -> float {
    178    switch (aUnitType) {
    179      case SVG_LENGTHTYPE_NUMBER:
    180      case SVG_LENGTHTYPE_PX:
    181        return 1.0f;
    182      case SVG_LENGTHTYPE_PERCENTAGE:
    183        zoomType = ZoomType::None;
    184        return aMetrics.GetAxisLength(aAxis) / 100.0f;
    185      case SVG_LENGTHTYPE_EMS:
    186        zoomType = ZoomType::None;
    187        return aMetrics.GetEmLength(UserSpaceMetrics::Type::This);
    188      case SVG_LENGTHTYPE_EXS:
    189        zoomType = ZoomType::None;
    190        return aMetrics.GetExLength(UserSpaceMetrics::Type::This);
    191      case SVG_LENGTHTYPE_CH:
    192        zoomType = ZoomType::None;
    193        return aMetrics.GetChSize(UserSpaceMetrics::Type::This);
    194      case SVG_LENGTHTYPE_REM:
    195        zoomType = ZoomType::SelfFromRoot;
    196        return aMetrics.GetEmLength(UserSpaceMetrics::Type::Root);
    197      case SVG_LENGTHTYPE_IC:
    198        zoomType = ZoomType::None;
    199        return aMetrics.GetIcWidth(UserSpaceMetrics::Type::This);
    200      case SVG_LENGTHTYPE_CAP:
    201        zoomType = ZoomType::None;
    202        return aMetrics.GetCapHeight(UserSpaceMetrics::Type::This);
    203      case SVG_LENGTHTYPE_VW:
    204        return aMetrics.GetCSSViewportSize().width / 100.f;
    205      case SVG_LENGTHTYPE_VH:
    206        return aMetrics.GetCSSViewportSize().height / 100.f;
    207      case SVG_LENGTHTYPE_VMIN: {
    208        auto sz = aMetrics.GetCSSViewportSize();
    209        return std::min(sz.width, sz.height) / 100.f;
    210      }
    211      case SVG_LENGTHTYPE_VMAX: {
    212        auto sz = aMetrics.GetCSSViewportSize();
    213        return std::max(sz.width, sz.height) / 100.f;
    214      }
    215      case SVG_LENGTHTYPE_LH:
    216        zoomType = ZoomType::None;
    217        return aMetrics.GetLineHeight(UserSpaceMetrics::Type::This);
    218      case SVG_LENGTHTYPE_RLH:
    219        zoomType = ZoomType::SelfFromRoot;
    220        return aMetrics.GetLineHeight(UserSpaceMetrics::Type::Root);
    221      case SVG_LENGTHTYPE_REX:
    222        zoomType = ZoomType::SelfFromRoot;
    223        return aMetrics.GetExLength(UserSpaceMetrics::Type::Root);
    224      case SVG_LENGTHTYPE_RCH:
    225        zoomType = ZoomType::SelfFromRoot;
    226        return aMetrics.GetChSize(UserSpaceMetrics::Type::Root);
    227      case SVG_LENGTHTYPE_RIC:
    228        zoomType = ZoomType::SelfFromRoot;
    229        return aMetrics.GetIcWidth(UserSpaceMetrics::Type::Root);
    230      case SVG_LENGTHTYPE_RCAP:
    231        zoomType = ZoomType::SelfFromRoot;
    232        return aMetrics.GetCapHeight(UserSpaceMetrics::Type::Root);
    233      default:
    234        MOZ_ASSERT(IsAbsoluteUnit(aUnitType));
    235        return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType);
    236    }
    237  }();
    238  if (aApplyZoom) {
    239    switch (zoomType) {
    240      case ZoomType::None:
    241        break;
    242      case ZoomType::Self:
    243        value *= aMetrics.GetZoom();
    244        break;
    245      case ZoomType::SelfFromRoot:
    246        value *= aMetrics.GetZoom() / aMetrics.GetRootZoom();
    247        break;
    248    }
    249  }
    250  return value;
    251 }
    252 
    253 /*static*/
    254 float SVGLength::GetPixelsPerCSSUnit(const UserSpaceMetrics& aMetrics,
    255                                     nsCSSUnit aCSSUnit, uint8_t aAxis,
    256                                     bool aApplyZoom) {
    257  uint8_t unitType;
    258  switch (aCSSUnit) {
    259 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue)
    260 #define SVG_LENGTH_UNIT(id, name, cssValue) \
    261  case cssValue:                            \
    262    unitType = id;                          \
    263    break;
    264 #include "mozilla/dom/SVGLengthUnits.h"
    265 #undef SVG_LENGTH_UNIT
    266 #undef SVG_LENGTH_EMPTY_UNIT
    267    default:
    268      MOZ_ASSERT_UNREACHABLE("Unknown CSS unit to SVG mapping");
    269      unitType = SVG_LENGTHTYPE_UNKNOWN;
    270      break;
    271  }
    272  return GetPixelsPerUnit(aMetrics, unitType, aAxis, aApplyZoom);
    273 }
    274 
    275 /* static */
    276 nsCSSUnit SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit) {
    277  switch (aSpecifiedUnit) {
    278 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
    279  case id:                                  \
    280    return cssValue;
    281 #define SVG_LENGTH_UNIT(id, name, cssValue) SVG_LENGTH_EMPTY_UNIT(id, cssValue)
    282 #include "mozilla/dom/SVGLengthUnits.h"
    283 #undef SVG_LENGTH_UNIT
    284 #undef SVG_LENGTH_EMPTY_UNIT
    285    default:
    286      MOZ_ASSERT_UNREACHABLE("Unknown unit type");
    287      return nsCSSUnit::eCSSUnit_Pixel;
    288  }
    289 }
    290 
    291 /* static */
    292 void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
    293  switch (aUnitType) {
    294 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
    295  case id:                                  \
    296    aUnit.Truncate();                       \
    297    return;
    298 #define SVG_LENGTH_UNIT(id, name, cssValue) \
    299  case id:                                  \
    300    aUnit.AssignLiteral(name);              \
    301    return;
    302 #include "mozilla/dom/SVGLengthUnits.h"
    303 #undef SVG_LENGTH_UNIT
    304 #undef SVG_LENGTH_EMPTY_UNIT
    305  }
    306  MOZ_ASSERT_UNREACHABLE(
    307      "Unknown unit type! Someone's using an SVGLength "
    308      "with an invalid unit?");
    309 }
    310 
    311 /* static */
    312 uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) {
    313 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
    314  if (aUnit.IsEmpty()) {                    \
    315    return id;                              \
    316  }
    317 #define SVG_LENGTH_UNIT(id, name, cssValue) \
    318  if (aUnit.LowerCaseEqualsLiteral(name)) { \
    319    return id;                              \
    320  }
    321 #include "mozilla/dom/SVGLengthUnits.h"
    322 #undef SVG_LENGTH_UNIT
    323 #undef SVG_LENGTH_EMPTY_UNIT
    324 
    325  return SVG_LENGTHTYPE_UNKNOWN;
    326 }
    327 
    328 }  // namespace mozilla