using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Reflection; using System.ComponentModel; using Svg.DataTypes; using System.Text.RegularExpressions; using System.Linq; namespace Svg { public partial class SvgElement { private bool _dirty; /// /// Gets or sets a value indicating whether this element's 'Path' is dirty. /// /// /// true if the path is dirty; otherwise, false. /// protected virtual bool IsPathDirty { get { return this._dirty; } set { this._dirty = value; } } /// /// Force recreation of the paths for the element and it's children. /// public void InvalidateChildPaths() { this.IsPathDirty = true; foreach (SvgElement element in this.Children) { element.InvalidateChildPaths(); } } protected static float FixOpacityValue(float value) { const float max = 1.0f; const float min = 0.0f; return Math.Min(Math.Max(value, min), max); } /// /// Gets or sets the fill of this element. /// [SvgAttribute("fill", true)] public virtual SvgPaintServer Fill { get { return ((SvgPaintServer)this.Attributes["fill"] ?? SvgColourServer.NotSet); } set { this.Attributes["fill"] = value; } } /// /// Gets or sets the to be used when rendering a stroke around this element. /// [SvgAttribute("stroke", true)] public virtual SvgPaintServer Stroke { get { return (SvgPaintServer)this.Attributes["stroke"]; } set { this.Attributes["stroke"] = value; } } [SvgAttribute("fill-rule", true)] public virtual SvgFillRule FillRule { get { return (SvgFillRule)(this.Attributes["fill-rule"] ?? SvgFillRule.NonZero); } set { this.Attributes["fill-rule"] = value; } } /// /// Gets or sets the opacity of this element's . /// [SvgAttribute("fill-opacity", true)] public virtual float FillOpacity { get { return (float)(this.Attributes["fill-opacity"] ?? 1.0f); } set { this.Attributes["fill-opacity"] = FixOpacityValue(value); } } /// /// Gets or sets the width of the stroke (if the property has a valid value specified. /// [SvgAttribute("stroke-width", true)] public virtual SvgUnit StrokeWidth { get { return (SvgUnit)(this.Attributes["stroke-width"] ?? new SvgUnit(1.0f)); } set { this.Attributes["stroke-width"] = value; } } [SvgAttribute("stroke-linecap", true)] public virtual SvgStrokeLineCap StrokeLineCap { get { return (SvgStrokeLineCap)(this.Attributes["stroke-linecap"] ?? SvgStrokeLineCap.Butt); } set { this.Attributes["stroke-linecap"] = value; } } [SvgAttribute("stroke-linejoin", true)] public virtual SvgStrokeLineJoin StrokeLineJoin { get { return (SvgStrokeLineJoin)(this.Attributes["stroke-linejoin"] ?? SvgStrokeLineJoin.Miter); } set { this.Attributes["stroke-linejoin"] = value; } } [SvgAttribute("stroke-miterlimit", true)] public virtual float StrokeMiterLimit { get { return (float)(this.Attributes["stroke-miterlimit"] ?? 4f); } set { this.Attributes["stroke-miterlimit"] = value; } } [SvgAttribute("stroke-dasharray", true)] public virtual SvgUnitCollection StrokeDashArray { get { return this.Attributes["stroke-dasharray"] as SvgUnitCollection; } set { this.Attributes["stroke-dasharray"] = value; } } [SvgAttribute("stroke-dashoffset", true)] public virtual SvgUnit StrokeDashOffset { get { return (SvgUnit)(this.Attributes["stroke-dashoffset"] ?? SvgUnit.Empty); } set { this.Attributes["stroke-dashoffset"] = value; } } /// /// Gets or sets the opacity of the stroke, if the property has been specified. 1.0 is fully opaque; 0.0 is transparent. /// [SvgAttribute("stroke-opacity", true)] public virtual float StrokeOpacity { get { return (float)(this.Attributes["stroke-opacity"] ?? 1.0f); } set { this.Attributes["stroke-opacity"] = FixOpacityValue(value); } } /// /// Gets or sets the colour of the gradient stop. /// /// Apparently this can be set on non-sensical elements. Don't ask; just check the tests. [SvgAttribute("stop-color", true)] [TypeConverter(typeof(SvgPaintServerFactory))] public virtual SvgPaintServer StopColor { get { return this.Attributes["stop-color"] as SvgPaintServer; } set { this.Attributes["stop-color"] = value; } } /// /// Gets or sets the opacity of the element. 1.0 is fully opaque; 0.0 is transparent. /// [SvgAttribute("opacity", true)] public virtual float Opacity { get { return (float)(this.Attributes["opacity"] ?? 1.0f); } set { this.Attributes["opacity"] = FixOpacityValue(value); } } /// /// Refers to the AnitAlias rendering of shapes. /// [SvgAttribute("shape-rendering")] public virtual SvgShapeRendering ShapeRendering { get { return this.Attributes.GetInheritedAttribute("shape-rendering"); } set { this.Attributes["shape-rendering"] = value; } } /// /// Gets or sets the text anchor. /// [SvgAttribute("text-anchor", true)] public virtual SvgTextAnchor TextAnchor { get { return this.Attributes.GetInheritedAttribute("text-anchor"); } set { this.Attributes["text-anchor"] = value; this.IsPathDirty = true; } } /// /// Specifies dominant-baseline positioning of text. /// [SvgAttribute("baseline-shift", true)] public virtual string BaselineShift { get { return this.Attributes.GetInheritedAttribute("baseline-shift"); } set { this.Attributes["baseline-shift"] = value; this.IsPathDirty = true; } } /// /// Indicates which font family is to be used to render the text. /// [SvgAttribute("font-family", true)] public virtual string FontFamily { get { return this.Attributes["font-family"] as string; } set { this.Attributes["font-family"] = value; this.IsPathDirty = true; } } /// /// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment. /// [SvgAttribute("font-size", true)] public virtual SvgUnit FontSize { get { return (SvgUnit)(this.Attributes["font-size"] ?? SvgUnit.Empty); } set { this.Attributes["font-size"] = value; this.IsPathDirty = true; } } /// /// Refers to the style of the font. /// [SvgAttribute("font-style", true)] public virtual SvgFontStyle FontStyle { get { return (SvgFontStyle)(this.Attributes["font-style"] ?? SvgFontStyle.All); } set { this.Attributes["font-style"] = value; this.IsPathDirty = true; } } /// /// Refers to the varient of the font. /// [SvgAttribute("font-variant", true)] public virtual SvgFontVariant FontVariant { get { return (SvgFontVariant)(this.Attributes["font-variant"] ?? SvgFontVariant.Inherit); } set { this.Attributes["font-variant"] = value; this.IsPathDirty = true; } } /// /// Refers to the boldness of the font. /// [SvgAttribute("text-decoration", true)] public virtual SvgTextDecoration TextDecoration { get { return (SvgTextDecoration)(this.Attributes["text-decoration"] ?? SvgTextDecoration.Inherit); } set { this.Attributes["text-decoration"] = value; this.IsPathDirty = true; } } /// /// Refers to the boldness of the font. /// [SvgAttribute("font-weight", true)] public virtual SvgFontWeight FontWeight { get { return (SvgFontWeight)(this.Attributes["font-weight"] ?? SvgFontWeight.Inherit); } set { this.Attributes["font-weight"] = value; this.IsPathDirty = true; } } private enum FontParseState { fontStyle, fontVariant, fontWeight, fontSize, fontFamilyNext, fontFamilyCurr } /// /// Set all font information. /// [SvgAttribute("font", true)] public virtual string Font { get { return ((this.Attributes["font"] ?? string.Empty) as string); } set { var state = FontParseState.fontStyle; var parts = value.Split(' '); SvgFontStyle fontStyle; SvgFontVariant fontVariant; SvgFontWeight fontWeight; SvgUnit fontSize; bool success; string[] sizes; string part; for (int i = 0; i < parts.Length; i++) { part = parts[i]; success = false; while (!success) { switch (state) { case FontParseState.fontStyle: success = Enums.TryParse(part, out fontStyle); if (success) this.FontStyle = fontStyle; state++; break; case FontParseState.fontVariant: success = Enums.TryParse(part, out fontVariant); if (success) this.FontVariant = fontVariant; state++; break; case FontParseState.fontWeight: success = Enums.TryParse(part, out fontWeight); if (success) this.FontWeight = fontWeight; state++; break; case FontParseState.fontSize: sizes = part.Split('/'); try { fontSize = (SvgUnit)(new SvgUnitConverter().ConvertFromInvariantString(sizes[0])); success = true; this.FontSize = fontSize; } catch { } state++; break; case FontParseState.fontFamilyNext: state++; success = true; break; } } switch (state) { case FontParseState.fontFamilyNext: this.FontFamily = string.Join(" ", parts, i + 1, parts.Length - (i + 1)); i = int.MaxValue - 2; break; case FontParseState.fontFamilyCurr: this.FontFamily = string.Join(" ", parts, i, parts.Length - (i)); i = int.MaxValue - 2; break; } } this.Attributes["font"] = value; this.IsPathDirty = true; } } /// /// Get the font information based on data stored with the text object or inherited from the parent. /// /// internal IFontDefn GetFont(ISvgRenderer renderer) { // Get the font-size float fontSize; var fontSizeUnit = this.FontSize; if (fontSizeUnit == SvgUnit.None || fontSizeUnit == SvgUnit.Empty) { fontSize = 1.0f; } else { fontSize = fontSizeUnit.ToDeviceValue(renderer, UnitRenderingType.Vertical, this); } var family = ValidateFontFamily(this.FontFamily, this.OwnerDocument); var sFaces = family as IEnumerable; if (sFaces == null) { var fontStyle = System.Drawing.FontStyle.Regular; // Get the font-weight switch (this.FontWeight) { //Note: Bold is not listed because it is = W700. case SvgFontWeight.Bolder: case SvgFontWeight.W600: case SvgFontWeight.W700: case SvgFontWeight.W800: case SvgFontWeight.W900: fontStyle |= System.Drawing.FontStyle.Bold; break; } // Get the font-style switch (this.FontStyle) { case SvgFontStyle.Italic: case SvgFontStyle.Oblique: fontStyle |= System.Drawing.FontStyle.Italic; break; } // Get the text-decoration switch (this.TextDecoration) { case SvgTextDecoration.LineThrough: fontStyle |= System.Drawing.FontStyle.Strikeout; break; case SvgTextDecoration.Underline: fontStyle |= System.Drawing.FontStyle.Underline; break; } var ff = family as FontFamily; if (!ff.IsStyleAvailable(fontStyle)) { // Do Something } // Get the font-family return new GdiFontDefn(new System.Drawing.Font(ff, fontSize, fontStyle, System.Drawing.GraphicsUnit.Pixel)); } else { var font = sFaces.First().Parent as SvgFont; if (font == null) { var uri = sFaces.First().Descendants().OfType().First().ReferencedElement; font = OwnerDocument.IdManager.GetElementById(uri) as SvgFont; } return new SvgFontDefn(font, fontSize, OwnerDocument.Ppi); } } public static System.Drawing.Text.PrivateFontCollection PrivateFonts = new System.Drawing.Text.PrivateFontCollection(); public static object ValidateFontFamily(string fontFamilyList, SvgDocument doc) { // Split font family list on "," and then trim start and end spaces and quotes. var fontParts = (fontFamilyList ?? string.Empty).Split(new[] { ',' }).Select(fontName => fontName.Trim(new[] { '"', ' ', '\'' })); var families = System.Drawing.FontFamily.Families; Func getFamily; FontFamily family; IEnumerable sFaces; // Find a the first font that exists in the list of installed font families. //styles from IE get sent through as lowercase. foreach (var f in fontParts) { if (doc != null && doc.FontDefns().TryGetValue(f, out sFaces)) return sFaces; getFamily = new Func(ff => string.Equals(ff.Name, f, StringComparison.OrdinalIgnoreCase)); family = families.FirstOrDefault(getFamily); if (family != null) return family; family = PrivateFonts.Families.FirstOrDefault(getFamily); if (family != null) return family; switch (f.ToLower()) { case "serif": return System.Drawing.FontFamily.GenericSerif; case "sans-serif": return System.Drawing.FontFamily.GenericSansSerif; case "monospace": return System.Drawing.FontFamily.GenericMonospace; } } // No valid font family found from the list requested. return System.Drawing.FontFamily.GenericSansSerif; } } }