/// <summary> /// Clones an already existing <see cref="CssValue"/> /// </summary> internal CssValue(CssValue sv) { type = sv.Type; value = sv.value; unit = sv.Unit; flags = sv.Flags; }
internal CssValue(ECssValueTypes type, object value, ECssUnit unit) : this(type) { this.unit = unit; this.value = value; if (type == ECssValueTypes.KEYWORD)// Try and catch some common IMPORTANT keywords { /* If our keyword can be resolved to another ECssValueType then its an global keyword */ if (!Lookup.TryEnum(value as string, out ECssValueTypes outType)) { type = outType; } } else if (type == ECssValueTypes.DIMENSION || type == ECssValueTypes.RESOLUTION) { /* Make sure to correct the value and distinguish whether we are a Dimension or a Resolution */ switch (unit) { case ECssUnit.DPI: case ECssUnit.DPCM: case ECssUnit.DPPX: type = ECssValueTypes.RESOLUTION; break; default: type = ECssValueTypes.DIMENSION; break; } } }
/// <summary> /// Resolves a <paramref name="value"/> from <paramref name="unitFrom"/> to <paramref name="unitTo"/> /// </summary> /// <param name="unitFrom">The unit the value is specified in</param> /// <param name="unitTo">The unit to convert to</param> /// <param name="value">Value to convert</param> public double Resolve(double value, ECssUnit unitFrom, ECssUnit unitTo) { /* First we convert the unit value to the canonical unit (pixels for physical values) */ double canonical = value * SCALING_TABLE[(int)unitFrom]; /* Next we divide the canonical value to get the value in (unitTo) units */ return(canonical / SCALING_TABLE[(int)unitTo]); }
/// <summary> /// Notifys all dimension-properties which use the specified unit that its scale has changed and they need to update /// </summary> /// <param name="Unit"></param> public void Notify_Unit_Scale_Change(ECssUnit Unit) { if (Unit == ECssUnit.None) { return; } foreach (ICssProperty Property in Cascaded.Get_Set_Properties()) { Property.Handle_Unit_Change(Unit); } }
/// <summary> /// Resolves a <paramref name="Value"/> from <paramref name="Unit"/> to its canonical form. /// </summary> /// <param name="Unit">Unit the value is specified in</param> /// <param name="Value">Value to convert</param> public double Resolve(double Value, ECssUnit Unit) { /* First we convert the unit value to the canonical unit (pixels for physical values) */ return(Value * SCALING_TABLE[(int)Unit]); }
/// <summary> /// Retreives the base scaling factor for the given unit relative to its canonical unit /// </summary> /// <param name="document"></param> /// <param name="Unit"></param> /// <returns></returns> private static double Get_Scale(Document document, ECssUnit Unit, bool anchor_to_dpi = false) { switch (Unit) { /* Physical Units */ /* Docs: https://www.w3.org/TR/css3-values/#physical-units */ case ECssUnit.PX: { return(1.0); } case ECssUnit.CM: { if (anchor_to_dpi) { return(document.defaultView.screen.dpi / 2.54); } return(CM_TO_PX); } case ECssUnit.MM: { if (anchor_to_dpi) { return((document.defaultView.screen.dpi / 2.54) / 10.0); } return(MM_TO_PX); } case ECssUnit.Q: { if (anchor_to_dpi) { return((document.defaultView.screen.dpi / 2.54) / 40.0); } return(Q_TO_PX); } case ECssUnit.IN: { if (anchor_to_dpi) { return(1.0 / document.defaultView.screen.dpi); } return(INCH_TO_PX); } case ECssUnit.PC: { if (anchor_to_dpi) { return((1.0 / document.defaultView.screen.dpi) / 6.0); } return(PC_TO_PX); } case ECssUnit.PT: { if (anchor_to_dpi) { return((1.0 / document.defaultView.screen.dpi) / 72.0); } return(PT_TO_PX); } /* <Resolution> Units */ /* Docs: https://www.w3.org/TR/css3-values/#resolution-value */ case ECssUnit.DPI: { if (anchor_to_dpi) { return(1.0 / document.defaultView.screen.dpi); } return(1 / 96); } case ECssUnit.DPCM: { if (anchor_to_dpi) { return(document.defaultView.screen.dpi / 2.54); } return(PT_TO_PX); } case ECssUnit.DPPX: { return(1); } /* <Time> Units */ /* Docs: https://www.w3.org/TR/css3-values/#time */ case ECssUnit.S: { return(1.0); } case ECssUnit.MS: { return(1 / 1000); } /* <Frequency> Units */ /* Docs: https://www.w3.org/TR/css3-values/#frequency */ case ECssUnit.HZ: { return(1.0); } case ECssUnit.KHZ: { return(1 / 1000); } /* Angle Units */ /* Docs: https://www.w3.org/TR/css3-values/#angles */ /* Canonical unit: degrees */ case ECssUnit.DEG: // Translate degrees to radians { return(1.0); } case ECssUnit.GRAD: { return(400 / 360); } case ECssUnit.RAD: { return(180.0 / Math.PI); } case ECssUnit.TURN: { return(360.0); } /* Font Units */ /* Docs: https://www.w3.org/TR/css-values-3/#font-relative-lengths */ case ECssUnit.REM: { return(document.body.Style.FontSize); } case ECssUnit.VMAX: { return(Math.Max(document.defaultView.visualViewport.Width, document.defaultView.visualViewport.Height)); } case ECssUnit.VMIN: { return(Math.Min(document.defaultView.visualViewport.Width, document.defaultView.visualViewport.Height)); } case ECssUnit.VW: { return(document.defaultView.visualViewport.Width); } case ECssUnit.VH: { return(document.defaultView.visualViewport.Height); } default: { throw new NotImplementedException($"CSS Unit type '{Enum.GetName(typeof(ECssUnit), Unit)}' has not been implemented!"); } } }
public static double Get_Font_Unit_Scale(Element Owner, ICssProperty Property, ECssUnit Unit) { switch (Unit) { case ECssUnit.CH: throw new NotImplementedException($"CSS Unit type '{Enum.GetName(typeof(ECssUnit), Unit)}' has not been implemented!"); case ECssUnit.EM: { /* * CSS Specs: * When used in the value of the font-size property on the element they refer to, * these units refer to the computed font metrics of the parent element * (or the computed font metrics corresponding to the initial values of the font property, if the element has no parent). * When used outside the context of an element (such as in media queries), * these units refer to the computed font metrics corresponding to the initial values of the font property. */ if (Property.CssName == "font-size" && ReferenceEquals(Property.Owner, Owner)) { // We are being called from the font-size property if (Property.Owner.parentElement is object) { // Basically just try and inherit our parents unit scale return(Get_Font_Unit_Scale(Owner.parentElement, Property, Unit)); } else // No parent { /* * CSS Specs: * When used in the value of the font-size property on the element they refer to, * these units refer to the computed font metrics of the parent element * (or the computed font metrics corresponding to the initial values of the font property, if the element has no parent) */ // I can only assume this means we default to the property declerations default/initial value var def = CssDefinitions.StyleDefinitions[Property.CssName]; if (def != null) { return(def.Initial.Resolve() ?? throw new CssException($"Failed to resolve the default value specified in the '{Property.CssName}' property decleration to a number!")); } } } #if DISABLE_FONT_SYSTEM return(1); #else if (Owner.Style.Font != null) { return(Owner.Style.Font.EmSize); } else { return(Owner.Style.FontSize); } #endif } case ECssUnit.EX: { /* * CSS Specs: * The 'ex' unit is defined by the element's first available font. * The exception is when 'ex' occurs in the value of the 'font-size' property, * in which case it refers to the 'ex' of the parent element. */ if (Property.CssName.Equals("font-size") && Property.Owner == Owner) { // We are being called from the font-size property if (Owner.parentElement != null) { // Basically just try and inherit our parents unit scale return(Get_Font_Unit_Scale(Owner.parentElement, Property, Unit)); } else // No parent { /* * CSS Specs: * When used in the value of the font-size property on the element they refer to, * these units refer to the computed font metrics of the parent element * (or the computed font metrics corresponding to the initial values of the font property, if the element has no parent) */ // I can only assume this means we default to the property declerations default/initial value var def = CssDefinitions.StyleDefinitions[Property.CssName]; if (def != null) { return(def.Initial.Resolve() ?? throw new CssException($"Failed to resolve the default value specified in the '{Property.CssName}' property decleration to a number!")); } } } #if DISABLE_FONT_SYSTEM #else if (Owner.Style.Font != null) { // XXX: implement logic to measure the 'x' height for our font. SEE: https://www.w3.org/TR/css-values-3/#font-relative-lengths throw new NotImplementedException(); } #endif /* * CSS Specs: * In the cases where it is impossible or impractical to determine the x-height, a value of 0.5em should be used. */ return(Get_Font_Unit_Scale(Owner, Property, ECssUnit.EM) * 0.5); } default: throw new NotImplementedException($"CSS Unit type '{Enum.GetName(typeof(ECssUnit), Unit)}' has not been implemented!"); } }
public static CssValue Consume_CssValue(DataConsumer <CssToken> Stream) { if (Stream is null) { throw new CssParserException(CssErrors.STREAM_IS_NULL); } Contract.EndContractBlock(); CssToken Token = Stream.Next; switch (Token.Type) { case ECssTokenType.Dimension: { var tok = Stream.Consume() as DimensionToken; ECssUnit unit = ECssUnit.PX; if (!string.IsNullOrEmpty(tok.Unit)) { ECssUnit unitLookup = Lookup.Enum <ECssUnit>(tok.Unit); unit = unitLookup; } return(new CssValue(ECssValueTypes.DIMENSION, tok.Number, unit)); } case ECssTokenType.Number: { var tok = Stream.Consume() as NumberToken; return(new CssValue(ECssValueTypes.NUMBER, tok.Number)); } case ECssTokenType.Percentage: { var tok = Stream.Consume() as PercentageToken; return(new CssValue(ECssValueTypes.PERCENT, tok.Number)); } case ECssTokenType.String: { var tok = Stream.Consume() as StringToken; return(new CssValue(ECssValueTypes.STRING, tok.Value)); } case ECssTokenType.Ident: // Keyword { var tok = Stream.Consume() as IdentToken; return(new CssValue(ECssValueTypes.KEYWORD, tok.Value)); } case ECssTokenType.FunctionName: { CssFunction func = Consume_Function(Stream); return(new CssValue(func)); } case ECssTokenType.Function: { var func = Stream.Consume() as CssFunction; return(new CssValue(func)); } case ECssTokenType.Url: { /* XXX: Finish this */ throw new NotSupportedException("URL values are not supported as of yet!"); } case ECssTokenType.EOF: { return(CssValue.Null); } default: { throw new CssParserException(String.Format(CultureInfo.InvariantCulture, CssErrors.UNHANDLED_TOKEN_FOR_CSS_VALUE, Token.Type), Stream); } } }
/// <summary>Create an absolute length value if not null, or return the given default value</summary> public static CssValue From(double?value, ECssUnit Unit, CssValue defaultValue) => (!value.HasValue ? defaultValue : new CssValue(ECssValueTypes.DIMENSION, value.Value, Unit));
/// <summary>Create an absolute length value</summary> public static CssValue From(double value, ECssUnit Unit) => new CssValue(ECssValueTypes.DIMENSION, value, Unit);
private static ECssValueFlags Get_Inherent_Value_Type_Flags(ECssValueTypes Type, ECssUnit Unit, object Value) { ECssValueFlags Flags = ECssValueFlags.None; /*if (Value is Array) * { * Flags |= ECssValueFlags.Collection; * }*/ switch (Type) { // NOTE: Auto values get calculated in vastly different ways, sometimes this doesn't even indicate that a value depends on the value of other properties, //so it should NOT get the 'Depends' flag case ECssValueTypes.INHERIT: // Inherited values function like redirects which compute to the current value of the matching property for the owning element's parent case ECssValueTypes.PERCENT: // Percentage values represent a percentage of another property's value { Flags |= ECssValueFlags.Depends; break; } case ECssValueTypes.NULL: case ECssValueTypes.NONE: case ECssValueTypes.INITIAL: case ECssValueTypes.INTEGER: case ECssValueTypes.NUMBER: case ECssValueTypes.STRING: case ECssValueTypes.KEYWORD: case ECssValueTypes.COLOR: case ECssValueTypes.IMAGE: case ECssValueTypes.POSITION: // The position has already been resolved here. case ECssValueTypes.FUNCTION: // The function args have already been resolved here. { Flags |= ECssValueFlags.Absolute; break; } case ECssValueTypes.UNSET: case ECssValueTypes.AUTO: case ECssValueTypes.DIMENSION: case ECssValueTypes.RATIO: case ECssValueTypes.RESOLUTION: { /* XXX: * These values when used on properties CAN be dependant but arent always so idk maybe its best to leave them as absolute? * Maybe we can come up with some sort of intermediate state that causes them to resolve their flag to absolute/dependent AFTER they get assigned to a property? */ Flags |= ECssValueFlags.Absolute; break; } case ECssValueTypes.COLLECTION: // A collections flags are the combined flags of all it's sub-values { if (Value is Array array) { // Multi object foreach (object o in array) { if (o is CssValue cssValue) { Flags |= Get_Inherent_Value_Type_Flags(cssValue.Type, cssValue.Unit, cssValue.value); } else { throw new CssException($"All {nameof(CssValue)} collection members must be {nameof(CssValue)}s"); } } return(Flags); } else if (Value is CssValue cssValue) { // Single object Flags |= Get_Inherent_Value_Type_Flags(cssValue.Type, cssValue.Unit, cssValue.value); } else { throw new CssException($"All {nameof(CssValue)} collection members must be {nameof(CssValue)}s"); } break; } default: { throw new NotImplementedException($"Flag handling for the '{Enum.GetName(typeof(ECssValueTypes), Type)}' css-value type has not been implemented!"); } } return(Flags); }