forked from mirrors/gecko-dev
		
	 5cb2a2829c
			
		
	
	
		5cb2a2829c
		
	
	
	
	
		
			
			Length attribute should allow leading and trailing whitespace per SVG2. Differential Revision: https://phabricator.services.mozilla.com/D23628 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			226 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/. */
 | |
| 
 | |
| #include "SVGLength.h"
 | |
| 
 | |
| #include "mozilla/ArrayUtils.h"
 | |
| #include "mozilla/dom/SVGElement.h"
 | |
| #include "mozilla/dom/SVGSVGElement.h"
 | |
| #include "nsTextFormatter.h"
 | |
| #include "SVGContentUtils.h"
 | |
| #include <limits>
 | |
| #include <algorithm>
 | |
| 
 | |
| using namespace mozilla::dom;
 | |
| using namespace mozilla::dom::SVGLength_Binding;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| // Declare some helpers defined below:
 | |
| static void GetUnitString(nsAString& unit, uint16_t unitType);
 | |
| static uint16_t GetUnitTypeForString(const nsAString& unitStr);
 | |
| 
 | |
| void SVGLength::GetValueAsString(nsAString& aValue) const {
 | |
|   nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
 | |
| 
 | |
|   nsAutoString unitString;
 | |
|   GetUnitString(unitString, mUnit);
 | |
|   aValue.Append(unitString);
 | |
| }
 | |
| 
 | |
| bool SVGLength::SetValueFromString(const nsAString& aString) {
 | |
|   bool success;
 | |
|   auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
 | |
| 
 | |
|   if (!success) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(token);
 | |
|   const RangedPtr<const char16_t> end = SVGContentUtils::GetEndRangedPtr(token);
 | |
| 
 | |
|   float value;
 | |
| 
 | |
|   if (!SVGContentUtils::ParseNumber(iter, end, value)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const nsAString& units = Substring(iter.get(), end.get());
 | |
|   uint16_t unitType = GetUnitTypeForString(units);
 | |
|   if (!IsValidUnitType(unitType)) {
 | |
|     return false;
 | |
|   }
 | |
|   mValue = value;
 | |
|   mUnit = uint8_t(unitType);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| inline static bool IsAbsoluteUnit(uint8_t aUnit) {
 | |
|   return aUnit >= SVGLength_Binding::SVG_LENGTHTYPE_CM &&
 | |
|          aUnit <= SVGLength_Binding::SVG_LENGTHTYPE_PC;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Helper to convert between different CSS absolute units without the need for
 | |
|  * an element, which provides more flexibility at the DOM level (and without
 | |
|  * the need for an intermediary conversion to user units, which avoids
 | |
|  * unnecessary overhead and rounding error).
 | |
|  *
 | |
|  * Example usage: to find out how many centimeters there are per inch:
 | |
|  *
 | |
|  *   GetAbsUnitsPerAbsUnit(SVGLength_Binding::SVG_LENGTHTYPE_CM,
 | |
|  *                         SVGLength_Binding::SVG_LENGTHTYPE_IN)
 | |
|  */
 | |
| inline static float GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
 | |
|   MOZ_ASSERT(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
 | |
|   MOZ_ASSERT(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
 | |
| 
 | |
|   float CSSAbsoluteUnitConversionFactors[5][5] = {
 | |
|       // columns: cm, mm, in, pt, pc
 | |
|       // cm per...:
 | |
|       {1.0f, 0.1f, 2.54f, 0.035277777777777778f, 0.42333333333333333f},
 | |
|       // mm per...:
 | |
|       {10.0f, 1.0f, 25.4f, 0.35277777777777778f, 4.2333333333333333f},
 | |
|       // in per...:
 | |
|       {0.39370078740157481f, 0.039370078740157481f, 1.0f, 0.013888888888888889f,
 | |
|        0.16666666666666667f},
 | |
|       // pt per...:
 | |
|       {28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f},
 | |
|       // pc per...:
 | |
|       {2.3622047244094489f, 0.23622047244094489f, 6.0f, 0.083333333333333333f,
 | |
|        1.0f}};
 | |
| 
 | |
|   // First absolute unit is SVG_LENGTHTYPE_CM = 6
 | |
|   return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
 | |
| }
 | |
| 
 | |
| float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
 | |
|                                          const SVGElement* aElement,
 | |
|                                          uint8_t aAxis) const {
 | |
|   if (aUnit == mUnit) {
 | |
|     return mValue;
 | |
|   }
 | |
|   if ((aUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
 | |
|        mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) ||
 | |
|       (aUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX &&
 | |
|        mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)) {
 | |
|     return mValue;
 | |
|   }
 | |
|   if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
 | |
|     return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
 | |
|   }
 | |
| 
 | |
|   // Otherwise we do a two step conversion via user units. This can only
 | |
|   // succeed if aElement is non-null (although that's not sufficient to
 | |
|   // guarantee success).
 | |
| 
 | |
|   float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
 | |
|   float userUnitsPerNewUnit =
 | |
|       SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
 | |
| 
 | |
|   NS_ASSERTION(
 | |
|       userUnitsPerCurrentUnit >= 0 || !IsFinite(userUnitsPerCurrentUnit),
 | |
|       "bad userUnitsPerCurrentUnit");
 | |
|   NS_ASSERTION(userUnitsPerNewUnit >= 0 || !IsFinite(userUnitsPerNewUnit),
 | |
|                "bad userUnitsPerNewUnit");
 | |
| 
 | |
|   float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
 | |
| 
 | |
|   // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
 | |
|   // be zero.
 | |
|   if (IsFinite(value)) {
 | |
|     return value;
 | |
|   }
 | |
|   return std::numeric_limits<float>::quiet_NaN();
 | |
| }
 | |
| 
 | |
| #define INCHES_PER_MM_FLOAT float(0.0393700787)
 | |
| #define INCHES_PER_CM_FLOAT float(0.393700787)
 | |
| 
 | |
| float SVGLength::GetUserUnitsPerUnit(const SVGElement* aElement,
 | |
|                                      uint8_t aAxis) const {
 | |
|   switch (mUnit) {
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_NUMBER:
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_PX:
 | |
|       return 1.0f;
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_MM:
 | |
|       return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch();
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_CM:
 | |
|       return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch();
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_IN:
 | |
|       return GetUserUnitsPerInch();
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_PT:
 | |
|       return (1.0f / POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_PC:
 | |
|       return (12.0f / POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE:
 | |
|       return GetUserUnitsPerPercent(aElement, aAxis);
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_EMS:
 | |
|       return SVGContentUtils::GetFontSize(const_cast<SVGElement*>(aElement));
 | |
|     case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
 | |
|       return SVGContentUtils::GetFontXHeight(const_cast<SVGElement*>(aElement));
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("Unknown unit type");
 | |
|       return std::numeric_limits<float>::quiet_NaN();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| float SVGLength::GetUserUnitsPerPercent(const SVGElement* aElement,
 | |
|                                         uint8_t aAxis) {
 | |
|   if (aElement) {
 | |
|     dom::SVGViewportElement* viewportElement = aElement->GetCtx();
 | |
|     if (viewportElement) {
 | |
|       return std::max(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
 | |
|     }
 | |
|   }
 | |
|   return std::numeric_limits<float>::quiet_NaN();
 | |
| }
 | |
| 
 | |
| // Helpers:
 | |
| 
 | |
| // These items must be at the same index as the SVGLength constants!
 | |
| static const nsStaticAtom* const unitMap[] = {
 | |
|     nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
 | |
|     nullptr, /* SVG_LENGTHTYPE_NUMBER */
 | |
|     nsGkAtoms::percentage,
 | |
|     nsGkAtoms::em,
 | |
|     nsGkAtoms::ex,
 | |
|     nsGkAtoms::px,
 | |
|     nsGkAtoms::cm,
 | |
|     nsGkAtoms::mm,
 | |
|     nsGkAtoms::in,
 | |
|     nsGkAtoms::pt,
 | |
|     nsGkAtoms::pc};
 | |
| 
 | |
| static void GetUnitString(nsAString& unit, uint16_t unitType) {
 | |
|   if (SVGLength::IsValidUnitType(unitType)) {
 | |
|     if (unitMap[unitType]) {
 | |
|       unitMap[unitType]->ToString(unit);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
|   MOZ_ASSERT_UNREACHABLE(
 | |
|       "Unknown unit type! Someone's using an SVGLength "
 | |
|       "with an invalid unit?");
 | |
| }
 | |
| 
 | |
| static uint16_t GetUnitTypeForString(const nsAString& unitStr) {
 | |
|   if (unitStr.IsEmpty()) return SVGLength_Binding::SVG_LENGTHTYPE_NUMBER;
 | |
| 
 | |
|   nsAtom* unitAtom = NS_GetStaticAtom(unitStr);
 | |
| 
 | |
|   if (unitAtom) {
 | |
|     for (uint32_t i = 1; i < ArrayLength(unitMap); i++) {
 | |
|       if (unitMap[i] == unitAtom) {
 | |
|         return i;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 |