Commit 7c70bd11 authored by Eric Domke's avatar Eric Domke
Browse files

Text on a Path & SVG Fonts

- Extraction interface for SvgRenderer
- Initial support for Text on a Path
- Initial support for Svg Fonts
- Support for symbol element
- Minor bug fixes with image pattern rendering
- Additional support for Text whitespace modes
parent 1a00f391
......@@ -11,16 +11,16 @@ namespace Svg
/// <summary>
/// Convenience wrapper around a graphics object
/// </summary>
public sealed class SvgRenderer : IDisposable
public sealed class SvgRenderer : IDisposable, IGraphicsProvider, ISvgRenderer
{
private Graphics _innerGraphics;
private Stack<ISvgBoundable> _boundables = new Stack<ISvgBoundable>();
public void Boundable(ISvgBoundable boundable)
public void SetBoundable(ISvgBoundable boundable)
{
_boundables.Push(boundable);
}
public ISvgBoundable Boundable()
public ISvgBoundable GetBoundable()
{
return _boundables.Peek();
}
......@@ -29,107 +29,57 @@ namespace Svg
return _boundables.Pop();
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgRenderer"/> class.
/// </summary>
private SvgRenderer()
{
}
public Region Clip
public float DpiY
{
get { return this._innerGraphics.Clip; }
set { this._innerGraphics.Clip = value; }
get { return _innerGraphics.DpiY; }
}
/// <summary>
/// Creates a new <see cref="SvgRenderer"/> from the specified <see cref="Image"/>.
/// Initializes a new instance of the <see cref="ISvgRenderer"/> class.
/// </summary>
/// <param name="image"><see cref="Image"/> from which to create the new <see cref="SvgRenderer"/>.</param>
public static SvgRenderer FromImage(Image image)
{
SvgRenderer renderer = new SvgRenderer();
renderer._innerGraphics = Graphics.FromImage(image);
return renderer;
}
/// <summary>
/// Creates a new <see cref="SvgRenderer"/> from the specified <see cref="Graphics"/>.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> to create the renderer from.</param>
public static SvgRenderer FromGraphics(Graphics graphics)
{
SvgRenderer renderer = new SvgRenderer();
renderer._innerGraphics = graphics;
return renderer;
}
public static SvgRenderer FromNull()
{
SvgRenderer renderer = new SvgRenderer();
var img = new Bitmap(1, 1);
renderer._innerGraphics = Graphics.FromImage(img);
return renderer;
}
public void DrawImageUnscaled(Image image, Point location)
private SvgRenderer(Graphics graphics)
{
this._innerGraphics.DrawImageUnscaled(image, location);
this._innerGraphics = graphics;
}
public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit graphicsUnit)
{
_innerGraphics.DrawImage(image, destRect, srcRect, graphicsUnit);
}
public void AddClip(Region region)
public void DrawImageUnscaled(Image image, Point location)
{
this._innerGraphics.SetClip(region, CombineMode.Intersect);
this._innerGraphics.DrawImageUnscaled(image, location);
}
public void SetClip(Region region)
public void DrawPath(Pen pen, GraphicsPath path)
{
this._innerGraphics.SetClip(region, CombineMode.Replace);
this._innerGraphics.DrawPath(pen, path);
}
public void FillPath(Brush brush, GraphicsPath path)
{
this._innerGraphics.FillPath(brush, path);
}
public void DrawPath(Pen pen, GraphicsPath path)
public Region GetClip()
{
this._innerGraphics.DrawPath(pen, path);
return this._innerGraphics.Clip;
}
public void RotateTransform(float fAngle, MatrixOrder order)
public void RotateTransform(float fAngle, MatrixOrder order = MatrixOrder.Append)
{
this._innerGraphics.RotateTransform(fAngle, order);
}
public void RotateTransform(float fAngle)
public void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append)
{
this.RotateTransform(fAngle, MatrixOrder.Append);
this._innerGraphics.ScaleTransform(sx, sy, order);
}
public void TranslateTransform(float dx, float dy, MatrixOrder order)
public void SetClip(Region region, CombineMode combineMode = CombineMode.Replace)
{
this._innerGraphics.TranslateTransform(dx, dy, order);
this._innerGraphics.SetClip(region, combineMode);
}
public void TranslateTransform(float dx, float dy)
public void TranslateTransform(float dx, float dy, MatrixOrder order = MatrixOrder.Append)
{
this.TranslateTransform(dx, dy, MatrixOrder.Append);
this._innerGraphics.TranslateTransform(dx, dy, order);
}
public void ScaleTransform(float sx, float sy, MatrixOrder order)
{
this._innerGraphics.ScaleTransform(sx, sy, order);
}
public void ScaleTransform(float sx, float sy)
{
this.ScaleTransform(sx, sy, MatrixOrder.Append);
}
public SmoothingMode SmoothingMode
{
......@@ -137,61 +87,64 @@ namespace Svg
set { this._innerGraphics.SmoothingMode = value; }
}
public PixelOffsetMode PixelOffsetMode
public Matrix Transform
{
get { return this._innerGraphics.PixelOffsetMode; }
set { this._innerGraphics.PixelOffsetMode = value; }
get { return this._innerGraphics.Transform; }
set { this._innerGraphics.Transform = value; }
}
public CompositingQuality CompositingQuality
public void Dispose()
{
get { return this._innerGraphics.CompositingQuality; }
set { this._innerGraphics.CompositingQuality = value; }
this._innerGraphics.Dispose();
}
public TextRenderingHint TextRenderingHint
public float FontBaselineOffset(IFontDefn font)
{
get { return this._innerGraphics.TextRenderingHint; }
set { this._innerGraphics.TextRenderingHint = value; }
return font.Ascent(this);
}
public int TextContrast
public IList<RectangleF> MeasureCharacters(string text, IFontDefn font)
{
get { return this._innerGraphics.TextContrast; }
set { this._innerGraphics.TextContrast = value; }
return font.MeasureCharacters(this, text);
}
public Matrix Transform
public SizeF MeasureString(string text, IFontDefn font)
{
get { return this._innerGraphics.Transform; }
set { this._innerGraphics.Transform = value; }
return font.MeasureString(this, text);
}
public void Save()
Graphics IGraphicsProvider.GetGraphics()
{
this._innerGraphics.Save();
return _innerGraphics;
}
public void Dispose()
/// <summary>
/// Creates a new <see cref="ISvgRenderer"/> from the specified <see cref="Image"/>.
/// </summary>
/// <param name="image"><see cref="Image"/> from which to create the new <see cref="ISvgRenderer"/>.</param>
public static ISvgRenderer FromImage(Image image)
{
this._innerGraphics.Dispose();
var g = Graphics.FromImage(image);
g.TextRenderingHint = TextRenderingHint.AntiAlias;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.TextContrast = 1;
return new SvgRenderer(g);
}
public SizeF MeasureString(string text, Font font)
/// <summary>
/// Creates a new <see cref="ISvgRenderer"/> from the specified <see cref="Graphics"/>.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> to create the renderer from.</param>
public static ISvgRenderer FromGraphics(Graphics graphics)
{
var ff = font.FontFamily;
//Baseline calculation to match http://bobpowell.net/formattingtext.aspx
float ascent = ff.GetCellAscent(font.Style);
float baselineOffset = font.SizeInPoints / ff.GetEmHeight(font.Style) * ascent;
float baselineOffsetPixels = this._innerGraphics.DpiY / 72f * baselineOffset;
StringFormat format = StringFormat.GenericTypographic;
format.SetMeasurableCharacterRanges(new CharacterRange[]{new CharacterRange(0, text.Length)});
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
Region[] r = this._innerGraphics.MeasureCharacterRanges(text, font, new Rectangle(0, 0, 1000, 1000), format);
RectangleF rect = r[0].GetBounds(this._innerGraphics);
return new SvgRenderer(graphics);
}
return new SizeF(rect.Width, baselineOffsetPixels);
public static ISvgRenderer FromNull()
{
var img = new Bitmap(1, 1);
return SvgRenderer.FromImage(img);
}
}
}
\ No newline at end of file
......@@ -101,8 +101,15 @@
<Compile Include="Clipping and Masking\SvgMask.cs" />
<Compile Include="DataTypes\ISvgSupportsCoordinateUnits.cs" />
<Compile Include="DataTypes\SvgTextDecoration.cs" />
<Compile Include="DataTypes\SvgTextLengthAdjust.cs" />
<Compile Include="DataTypes\SvgTextPathMethod.cs" />
<Compile Include="DataTypes\SvgTextPathSpacing.cs" />
<Compile Include="DataTypes\XmlSpaceHandling.cs" />
<Compile Include="Document Structure\SvgSymbol.cs" />
<Compile Include="Filter Effects\ImageBuffer.cs" />
<Compile Include="Painting\GenericBoundable.cs" />
<Compile Include="Rendering\IGraphicsProvider.cs" />
<Compile Include="Rendering\ISvgRenderer.cs" />
<Compile Include="SvgNodeReader.cs" />
<Compile Include="Css\CssQuery.cs" />
<Compile Include="Css\SvgElementOps.cs" />
......@@ -235,7 +242,7 @@
<Compile Include="SvgUnknownElement.cs" />
<Compile Include="SvgElementAttribute.cs" />
<Compile Include="SvgExtentions.cs" />
<Compile Include="SvgRenderer.cs" />
<Compile Include="Rendering\SvgRenderer.cs" />
<Compile Include="Painting\SvgColourConverter.cs" />
<Compile Include="Painting\SvgGradientSpreadMethod.cs" />
<Compile Include="SvgDtdResolver.cs" />
......@@ -276,10 +283,24 @@
<Compile Include="DataTypes\SvgUnit.cs" />
<Compile Include="DataTypes\SvgUnitConverter.cs" />
<Compile Include="SvgTextReader.cs" />
<Compile Include="Text\SvgFontFaceUri.cs" />
<Compile Include="Text\FontFamily.cs" />
<Compile Include="Text\GdiFontDefn.cs" />
<Compile Include="Text\IFontDefn.cs" />
<Compile Include="Text\SvgFont.cs" />
<Compile Include="Text\SvgFontDefn.cs" />
<Compile Include="Text\SvgFontFace.cs" />
<Compile Include="Text\SvgFontFaceSrc.cs" />
<Compile Include="Text\SvgGlyph.cs" />
<Compile Include="Text\SvgKern.cs" />
<Compile Include="Text\SvgMissingGlyph.cs" />
<Compile Include="Text\SvgText.cs" />
<Compile Include="Text\SvgTextBase.cs" />
<Compile Include="Text\SvgTextAnchor.cs" />
<Compile Include="Text\SvgTextPath.cs" />
<Compile Include="Text\SvgTextSpan.cs" />
<Compile Include="Text\SvgTextRef.cs" />
<Compile Include="Text\PathStatistics.cs" />
<Compile Include="Transforms\ISvgTransformable.cs" />
<Compile Include="Transforms\SvgMatrix.cs" />
<Compile Include="Transforms\SvgRotate.cs" />
......
......@@ -15,13 +15,14 @@ namespace Svg
/// <summary>
/// Gets a <see cref="string"/> containing the XLink namespace (http://www.w3.org/1999/xlink).
/// </summary>
public const string SVG_NAMESPACE = "http://www.w3.org/2000/svg";
public const string SvgNamespace = "http://www.w3.org/2000/svg";
public const string XLinkPrefix = "xlink";
public const string XLinkNamespace = "http://www.w3.org/1999/xlink";
public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
public static readonly List<KeyValuePair<string, string>> Namespaces = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("", SVG_NAMESPACE),
new KeyValuePair<string, string>("", SvgNamespace),
new KeyValuePair<string, string>(XLinkPrefix, XLinkNamespace)
};
private string _name;
......@@ -55,7 +56,7 @@ namespace Svg
{
get
{
if (_namespace == SVG_NAMESPACE)
if (_namespace == SvgNamespace)
return _name;
return Namespaces.First(x => x.Value == _namespace).Key + ":" + _name;
}
......@@ -93,7 +94,7 @@ namespace Svg
internal SvgAttributeAttribute(string name)
{
this._name = name;
this._namespace = SVG_NAMESPACE;
this._namespace = SvgNamespace;
}
/// <summary>
......
......@@ -90,7 +90,8 @@ namespace Svg
(value is SvgTextAnchor && (SvgTextAnchor)value == SvgTextAnchor.inherit) ||
(value is SvgFontVariant && (SvgFontVariant)value == SvgFontVariant.inherit) ||
(value is SvgTextDecoration && (SvgTextDecoration)value == SvgTextDecoration.inherit) ||
(value == "inherit")
(value is XmlSpaceHandling && (XmlSpaceHandling)value == XmlSpaceHandling.inherit) ||
(value is string && (string)value == "inherit")
);
}
......
......@@ -22,6 +22,18 @@ namespace Svg
public static readonly int PointsPerInch = 96;
private SvgElementIdManager _idManager;
private Dictionary<string, IEnumerable<SvgFontFace>> _fontDefns = null;
internal Dictionary<string, IEnumerable<SvgFontFace>> FontDefns()
{
if (_fontDefns == null)
{
_fontDefns = (from f in Descendants().OfType<SvgFontFace>()
group f by f.FontFamily into family
select family).ToDictionary(f => f.Key, f => (IEnumerable<SvgFontFace>)f);
}
return _fontDefns;
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgDocument"/> class.
/// </summary>
......@@ -379,18 +391,18 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>.
/// Renders the <see cref="SvgDocument"/> to the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to render the document with.</param>
/// <param name="renderer">The <see cref="ISvgRenderer"/> to render the document with.</param>
/// <exception cref="ArgumentNullException">The <paramref name="renderer"/> parameter cannot be <c>null</c>.</exception>
public void Draw(SvgRenderer renderer)
public void Draw(ISvgRenderer renderer)
{
if (renderer == null)
{
throw new ArgumentNullException("renderer");
}
renderer.Boundable(this);
renderer.SetBoundable(this);
this.Render(renderer);
}
......@@ -407,7 +419,7 @@ namespace Svg
}
var renderer = SvgRenderer.FromGraphics(graphics);
renderer.Boundable(this);
renderer.SetBoundable(this);
this.Render(renderer);
}
......@@ -447,12 +459,8 @@ namespace Svg
{
using (var renderer = SvgRenderer.FromImage(bitmap))
{
renderer.Boundable(new GenericBoundable(0, 0, bitmap.Width, bitmap.Height));
renderer.TextRenderingHint = TextRenderingHint.AntiAlias;
renderer.TextContrast = 1;
renderer.PixelOffsetMode = PixelOffsetMode.Half;
renderer.SetBoundable(new GenericBoundable(0, 0, bitmap.Width, bitmap.Height));
this.Render(renderer);
renderer.Save();
}
}
catch
......
......@@ -255,13 +255,13 @@ namespace Svg
}
/// <summary>
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// Applies the required transforms to <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
protected internal virtual bool PushTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
protected internal virtual bool PushTransforms(ISvgRenderer renderer)
{
_graphicsMatrix = renderer.Transform;
_graphicsClip = renderer.Clip;
_graphicsClip = renderer.GetClip();
// Return if there are no transforms
if (this.Transforms == null || this.Transforms.Count == 0)
......@@ -283,10 +283,10 @@ namespace Svg
}
/// <summary>
/// Removes any previously applied transforms from the specified <see cref="SvgRenderer"/>.
/// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> that should have transforms removed.</param>
protected internal virtual void PopTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
protected internal virtual void PopTransforms(ISvgRenderer renderer)
{
renderer.Transform = _graphicsMatrix;
_graphicsMatrix = null;
......@@ -295,19 +295,19 @@ namespace Svg
}
/// <summary>
/// Applies the required transforms to <see cref="SvgRenderer"/>.
/// Applies the required transforms to <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to be transformed.</param>
void ISvgTransformable.PushTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to be transformed.</param>
void ISvgTransformable.PushTransforms(ISvgRenderer renderer)
{
this.PushTransforms(renderer);
}
/// <summary>
/// Removes any previously applied transforms from the specified <see cref="SvgRenderer"/>.
/// Removes any previously applied transforms from the specified <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> that should have transforms removed.</param>
void ISvgTransformable.PopTransforms(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> that should have transforms removed.</param>
void ISvgTransformable.PopTransforms(ISvgRenderer renderer)
{
this.PopTransforms(renderer);
}
......@@ -344,6 +344,17 @@ namespace Svg
}
}
/// <summary>
/// Gets or sets the text anchor.
/// </summary>
/// <value>The text anchor.</value>
[SvgAttribute("space", SvgAttributeAttribute.XmlNamespace)]
public virtual XmlSpaceHandling SpaceHandling
{
get { return (this.Attributes["space"] == null) ? XmlSpaceHandling.inherit : (XmlSpaceHandling)this.Attributes["space"]; }
set { this.Attributes["space"] = value; }
}
public void SetAndForceUniqueID(string value, bool autoForceUniqueID = true, Action<SvgElement, string, string> logElementOldIDNewID = null)
{
// Don't do anything if it hasn't changed
......@@ -469,10 +480,10 @@ namespace Svg
/// <summary>
/// Renders this element to the <see cref="SvgRenderer"/>.
/// Renders this element to the <see cref="ISvgRenderer"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> that the element should use to render itself.</param>
public void RenderElement(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> that the element should use to render itself.</param>
public void RenderElement(ISvgRenderer renderer)
{
this.Render(renderer);
}
......@@ -632,10 +643,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
protected virtual void Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
protected virtual void Render(ISvgRenderer renderer)
{
this.PushTransforms(renderer);
this.RenderChildren(renderer);
......@@ -645,8 +656,8 @@ namespace Svg
/// <summary>
/// Renders the children of this <see cref="SvgElement"/>.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> to render the child <see cref="SvgElement"/>s to.</param>
protected virtual void RenderChildren(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> to render the child <see cref="SvgElement"/>s to.</param>
protected virtual void RenderChildren(ISvgRenderer renderer)
{
foreach (SvgElement element in this.Children)
{
......@@ -655,10 +666,10 @@ namespace Svg
}
/// <summary>
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="SvgRenderer"/> object.
/// Renders the <see cref="SvgElement"/> and contents to the specified <see cref="ISvgRenderer"/> object.
/// </summary>
/// <param name="renderer">The <see cref="SvgRenderer"/> object to render to.</param>
void ISvgElement.Render(SvgRenderer renderer)
/// <param name="renderer">The <see cref="ISvgRenderer"/> object to render to.</param>
void ISvgElement.Render(ISvgRenderer renderer)
{
this.Render(renderer);
}
......@@ -698,7 +709,7 @@ namespace Svg
/// </summary>
/// <param name="elem"></param>
/// <param name="path"></param>
protected GraphicsPath GetPaths(SvgElement elem, SvgRenderer renderer)
protected GraphicsPath GetPaths(SvgElement elem, ISvgRenderer renderer)
{
var ret = new GraphicsPath();
......@@ -1117,6 +1128,6 @@ namespace Svg
SvgElementCollection Children { get; }
IList<ISvgNode> Nodes { get; }
void Render(SvgRenderer renderer);
void Render(ISvgRenderer renderer);
}
}
\ No newline at end of file
......@@ -15,7 +15,6 @@ namespace Svg
internal class SvgElementFactory
{
private static List<ElementInfo> availableElements;
private const string svgNS = "http://www.w3.org/2000/svg";
private static Parser cssParser = new Parser();
/// <summary>
......@@ -84,7 +83,7 @@ namespace Svg
//Trace.TraceInformation("Begin CreateElement: {0}", elementName);
if (elementNS == svgNS)
if (elementNS == SvgAttributeAttribute.SvgNamespace || string.IsNullOrEmpty(elementNS))
{
if (elementName == "svg")
{
......
......@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
namespace Svg
{
......@@ -39,6 +41,29 @@ namespace Svg
public virtual SvgElement GetElementById(Uri uri)
{
if (!uri.IsAbsoluteUri && this._document.BaseUri != null && !uri.ToString().StartsWith("#"))
{
var fullUri = new Uri(this._document.BaseUri, uri);
var hash = fullUri.OriginalString.Substring(fullUri.OriginalString.LastIndexOf('#'));
SvgDocument doc;
switch (fullUri.Scheme.ToLowerInvariant())
{
case "file":
doc = SvgDocument.Open<SvgDocument>(fullUri.LocalPath.Substring(0, fullUri.LocalPath.Length - hash.Length));
return doc.IdManager.GetElementById(hash);
case "http":
case "https":
var httpRequest = WebRequest.Create(uri);
using (WebResponse webResponse = httpRequest.GetResponse())
{
doc = SvgDocument.Open<SvgDocument>(webResponse.GetResponseStream());
return doc.IdManager.GetElementById(hash);
}
default:
throw new NotSupportedException();
}
}
return this.GetElementById(uri.ToString());
}
......
......@@ -123,7 +123,6 @@ namespace Svg
{
const string entityText = "<!ENTITY";
string[] entities = this.Value.Split(new string[]{entityText}, StringSplitOptions.None);
string[] parts = null;
string name = null;
string value = null;
int quoteIndex;
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Svg.Text
{
/// <summary>
/// http://stackoverflow.com/questions/3633000/net-enumerate-winforms-font-styles
/// </summary>
public class FontFamily
{
#region InstalledFont Parameters
string _fontName = string.Empty;
string _fontSubFamily = string.Empty;
string _fontPath = string.Empty;
#endregion
#region InstalledFont Constructor
private FontFamily(string fontName, string fontSubFamily, string fontPath)
{
_fontName = fontName;
_fontSubFamily = fontSubFamily;
_fontPath = fontPath;
}
#endregion
#region InstalledFont Properties
public string FontName { get { return _fontName; } set { _fontName = value; } }
public string FontSubFamily { get { return _fontSubFamily; } set { _fontSubFamily = value; } }
public string FontPath { get { return _fontPath; } set { _fontPath = value; } }
#endregion
static public FontFamily FromPath(string fontFilePath)
{
string FontName = string.Empty;
string FontSubFamily = string.Empty;
string encStr = "UTF-8";
string strRet = string.Empty;
using (FileStream fs = new FileStream(fontFilePath, FileMode.Open, FileAccess.Read))
{
TT_OFFSET_TABLE ttOffsetTable = new TT_OFFSET_TABLE()
{
uMajorVersion = ReadUShort(fs),
uMinorVersion = ReadUShort(fs),
uNumOfTables = ReadUShort(fs),
uSearchRange = ReadUShort(fs),
uEntrySelector = ReadUShort(fs),
uRangeShift = ReadUShort(fs),
};
TT_TABLE_DIRECTORY tblDir = new TT_TABLE_DIRECTORY();
bool found = false;
for (int i = 0; i <= ttOffsetTable.uNumOfTables; i++)
{
tblDir = new TT_TABLE_DIRECTORY();
tblDir.Initialize();
fs.Read(tblDir.szTag, 0, tblDir.szTag.Length);
tblDir.uCheckSum = ReadULong(fs);
tblDir.uOffset = ReadULong(fs);
tblDir.uLength = ReadULong(fs);
Encoding enc = Encoding.GetEncoding(encStr);
string s = enc.GetString(tblDir.szTag);
if (s.CompareTo("name") == 0)
{
found = true;
break;
}
}
if (!found) return null;
fs.Seek(tblDir.uOffset, SeekOrigin.Begin);
TT_NAME_TABLE_HEADER ttNTHeader = new TT_NAME_TABLE_HEADER
{
uFSelector = ReadUShort(fs),
uNRCount = ReadUShort(fs),
uStorageOffset = ReadUShort(fs)
};
TT_NAME_RECORD ttRecord = new TT_NAME_RECORD();
for (int j = 0; j <= ttNTHeader.uNRCount; j++)
{
ttRecord = new TT_NAME_RECORD()
{
uPlatformID = ReadUShort(fs),
uEncodingID = ReadUShort(fs),
uLanguageID = ReadUShort(fs),
uNameID = ReadUShort(fs),
uStringLength = ReadUShort(fs),
uStringOffset = ReadUShort(fs)
};
if (ttRecord.uNameID > 2) { break; }
long nPos = fs.Position;
fs.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, SeekOrigin.Begin);
byte[] buf = new byte[ttRecord.uStringLength];
fs.Read(buf, 0, ttRecord.uStringLength);
Encoding enc;
if (ttRecord.uEncodingID == 3 || ttRecord.uEncodingID == 1)
{
enc = Encoding.BigEndianUnicode;
}
else
{
enc = Encoding.UTF8;
}
strRet = enc.GetString(buf);
if (ttRecord.uNameID == 1) { FontName = strRet; }
if (ttRecord.uNameID == 2) { FontSubFamily = strRet; }
fs.Seek(nPos, SeekOrigin.Begin);
}
return new FontFamily(FontName, FontSubFamily, fontFilePath);
}
}
public struct TT_OFFSET_TABLE
{
public ushort uMajorVersion;
public ushort uMinorVersion;
public ushort uNumOfTables;
public ushort uSearchRange;
public ushort uEntrySelector;
public ushort uRangeShift;
}
public struct TT_TABLE_DIRECTORY
{
public byte[] szTag;
public UInt32 uCheckSum;
public UInt32 uOffset;
public UInt32 uLength;
public void Initialize()
{
szTag = new byte[4];
}
}
public struct TT_NAME_TABLE_HEADER
{
public ushort uFSelector;
public ushort uNRCount;
public ushort uStorageOffset;
}
public struct TT_NAME_RECORD
{
public ushort uPlatformID;
public ushort uEncodingID;
public ushort uLanguageID;
public ushort uNameID;
public ushort uStringLength;
public ushort uStringOffset;
}
static private UInt16 ReadChar(FileStream fs, int characters)
{
string[] s = new string[characters];
byte[] buf = new byte[Convert.ToByte(s.Length)];
buf = ReadAndSwap(fs, buf.Length);
return BitConverter.ToUInt16(buf, 0);
}
static private UInt16 ReadByte(FileStream fs)
{
byte[] buf = new byte[11];
buf = ReadAndSwap(fs, buf.Length);
return BitConverter.ToUInt16(buf, 0);
}
static private UInt16 ReadUShort(FileStream fs)
{
byte[] buf = new byte[2];
buf = ReadAndSwap(fs, buf.Length);
return BitConverter.ToUInt16(buf, 0);
}
static private UInt32 ReadULong(FileStream fs)
{
byte[] buf = new byte[4];
buf = ReadAndSwap(fs, buf.Length);
return BitConverter.ToUInt32(buf, 0);
}
static private byte[] ReadAndSwap(FileStream fs, int size)
{
byte[] buf = new byte[size];
fs.Read(buf, 0, buf.Length);
Array.Reverse(buf);
return buf;
}
}
class Program
{
//static void Main(string[] args)
//{
// System.Drawing.FontFamily fam;
// var allInstalledFonts = from e in Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", false).GetValueNames()
// select Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts").GetValue(e);
// var ttfFonts = from e in allInstalledFonts.Where(e => (e.ToString().EndsWith(".ttf") || e.ToString().EndsWith(".otf"))) select e;
// var ttfFontsPaths = from e in ttfFonts.Select(e => (Path.GetPathRoot(e.ToString()) == "") ? Environment.GetFolderPath(Environment.SpecialFolder.Fonts) + "\\" + e.ToString() : e.ToString()) select e;
// var fonts = from e in ttfFontsPaths.Select(e => GetFontDetails(e.ToString())) select e;
// foreach (InstalledFont f in fonts)
// {
// if (f != null)
// Console.WriteLine("Name: " + f.FontName + ", SubFamily: " + f.FontSubFamily + ", Path: " + f.FontPath);
// }
// Console.ReadLine();
//}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Svg
{
public class GdiFontDefn : IFontDefn
{
private Font _font;
public float Size
{
get { return _font.Size; }
}
public float SizeInPoints
{
get { return _font.SizeInPoints; }
}
public GdiFontDefn(Font font)
{
_font = font;
}
public void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location)
{
path.AddString(text, _font.FontFamily, (int)_font.Style, _font.Size, location, StringFormat.GenericTypographic);
}
//Baseline calculation to match http://bobpowell.net/formattingtext.aspx
public float Ascent(ISvgRenderer renderer)
{
var ff = _font.FontFamily;
float ascent = ff.GetCellAscent(_font.Style);
float baselineOffset = _font.SizeInPoints / ff.GetEmHeight(_font.Style) * ascent;
return renderer.DpiY / 72f * baselineOffset;
}
public IList<RectangleF> MeasureCharacters(ISvgRenderer renderer, string text)
{
var g = GetGraphics(renderer);
var regions = new List<RectangleF>();
StringFormat format;
for (int s = 0; s <= (text.Length - 1) / 32; s++)
{
format = StringFormat.GenericTypographic;
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
format.SetMeasurableCharacterRanges((from r in Enumerable.Range(32 * s, Math.Min(32, text.Length - 32 * s))
select new CharacterRange(r, 1)).ToArray());
regions.AddRange(from r in g.MeasureCharacterRanges(text, _font, new Rectangle(0, 0, 1000, 1000), format)
select r.GetBounds(g));
}
return regions;
}
public SizeF MeasureString(ISvgRenderer renderer, string text)
{
var g = GetGraphics(renderer);
StringFormat format = StringFormat.GenericTypographic;
format.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, text.Length) });
format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
Region[] r = g.MeasureCharacterRanges(text, _font, new Rectangle(0, 0, 1000, 1000), format);
RectangleF rect = r[0].GetBounds(g);
return new SizeF(rect.Width, Ascent(renderer));
}
private static Graphics _graphics;
private static Graphics GetGraphics(object renderer)
{
var provider = renderer as IGraphicsProvider;
if (provider == null)
{
if (_graphics == null)
{
var bmp = new Bitmap(1, 1);
_graphics = Graphics.FromImage(bmp);
}
return _graphics;
}
else
{
return provider.GetGraphics();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Svg
{
public interface IFontDefn
{
float Size { get; }
float SizeInPoints { get; }
void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location);
float Ascent(ISvgRenderer renderer);
IList<RectangleF> MeasureCharacters(ISvgRenderer renderer, string text);
SizeF MeasureString(ISvgRenderer renderer, string text);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;
namespace Svg
{
public class PathStatistics
{
private const double GqBreak_TwoPoint = 0.57735026918962573;
private const double GqBreak_ThreePoint = 0.7745966692414834;
private const double GqBreak_FourPoint_01 = 0.33998104358485631;
private const double GqBreak_FourPoint_02 = 0.86113631159405257;
private const double GqWeight_FourPoint_01 = 0.65214515486254621;
private const double GqWeight_FourPoint_02 = 0.34785484513745385;
private PathData _data;
private double _totalLength;
private List<ISegment> _segments = new List<ISegment>();
public double TotalLength { get { return _totalLength; } }
public PathStatistics(PathData data)
{
_data = data;
int i = 1;
_totalLength = 0;
ISegment newSegment;
while (i < _data.Points.Length)
{
switch (_data.Types[i])
{
case 1:
newSegment = new LineSegment(_data.Points[i - 1], _data.Points[i]);
i++;
break;
case 3:
newSegment = new CubicBezierSegment(_data.Points[i - 1], _data.Points[i], _data.Points[i + 1], _data.Points[i + 2]);
i+= 3;
break;
default:
throw new NotSupportedException();
}
newSegment.StartOffset = _totalLength;
_segments.Add(newSegment);
_totalLength += newSegment.Length;
}
}
public void LocationAngleAtOffset(double offset, out PointF point, out float angle)
{
_segments[BinarySearchForSegment(offset, 0, _segments.Count - 1)].LocationAngleAtOffset(offset, out point, out angle);
}
public bool OffsetOnPath(double offset)
{
var seg = _segments[BinarySearchForSegment(offset, 0, _segments.Count - 1)];
offset -= seg.StartOffset;
return (offset >= 0 && offset <= seg.Length);
}
private int BinarySearchForSegment(double offset, int first, int last)
{
if (last == first)
{
return first;
}
else if ((last - first) == 1)
{
return (offset >= _segments[last].StartOffset ? last : first);
}
else
{
var mid = (last + first) / 2;
if (offset < _segments[mid].StartOffset)
{
return BinarySearchForSegment(offset, first, mid);
}
else
{
return BinarySearchForSegment(offset, mid, last);
}
}
}
private interface ISegment
{
double StartOffset { get; set; }
double Length { get; }
void LocationAngleAtOffset(double offset, out PointF point, out float rotation);
}
private class LineSegment : ISegment
{
private double _length;
private double _rotation;
private PointF _start;
private PointF _end;
public double StartOffset { get; set; }
public double Length { get { return _length; } }
public LineSegment(PointF start, PointF end)
{
_start = start;
_end = end;
_length = Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
_rotation = Math.Atan2(end.Y - start.Y, end.X - start.X) * 180 / Math.PI;
}
public void LocationAngleAtOffset(double offset, out PointF point, out float rotation)
{
offset -= StartOffset;
if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException();
point = new PointF((float)(_start.X + (offset / _length) * (_end.X - _start.X)),
(float)(_start.Y + (offset / _length) * (_end.Y - _start.Y)));
rotation = (float)_rotation;
}
}
private class CubicBezierSegment : ISegment
{
private PointF _p0;
private PointF _p1;
private PointF _p2;
private PointF _p3;
private double _length;
private Func<double, double> _integral;
private SortedList<double, double> _lengths = new SortedList<double, double>();
public double StartOffset { get; set; }
public double Length { get { return _length; } }
public CubicBezierSegment(PointF p0, PointF p1, PointF p2, PointF p3)
{
_p0 = p0;
_p1 = p1;
_p2 = p2;
_p3 = p3;
_integral = (t) => CubicBezierArcLengthIntegrand(_p0, _p1, _p2, _p3, t);
_length = GetLength(0, 1, 0.00000001f);
_lengths.Add(0, 0);
_lengths.Add(_length, 1);
}
private double GetLength(double left, double right, double epsilon)
{
var fullInt = GaussianQuadrature(_integral, left, right, 4);
return Subdivide(left, right, fullInt, 0, epsilon);
}
private double Subdivide(double left, double right, double fullInt, double totalLength, double epsilon)
{
var mid = (left + right) / 2;
var leftValue = GaussianQuadrature(_integral, left, mid, 4);
var rightValue = GaussianQuadrature(_integral, mid, right, 4);
if (Math.Abs(fullInt - (leftValue + rightValue)) > epsilon) {
var leftSub = Subdivide(left, mid, leftValue, totalLength, epsilon / 2.0);
totalLength += leftSub;
AddElementToTable(mid, totalLength);
return Subdivide(mid, right, rightValue, totalLength, epsilon / 2.0) + leftSub;
}
else
{
return leftValue + rightValue;
}
}
private void AddElementToTable(double position, double totalLength)
{
_lengths.Add(totalLength, position);
}
public void LocationAngleAtOffset(double offset, out PointF point, out float rotation)
{
offset -= StartOffset;
if (offset < 0 || offset > _length) throw new ArgumentOutOfRangeException();
var t = BinarySearchForParam(offset, 0, _lengths.Count - 1);
point = CubicBezierCurve(_p0, _p1, _p2, _p3, t);
var deriv = CubicBezierDerivative(_p0, _p1, _p2, _p3, t);
rotation = (float)(Math.Atan2(deriv.Y, deriv.X) * 180.0 / Math.PI);
}
private double BinarySearchForParam(double length, int first, int last)
{
if (last == first)
{
return _lengths.Values[last];
}
else if ((last - first) == 1)
{
return _lengths.Values[first] + (_lengths.Values[last] - _lengths.Values[first]) *
(length - _lengths.Keys[first]) / (_lengths.Keys[last] - _lengths.Keys[first]);
}
else
{
var mid = (last + first) / 2;
if (length < _lengths.Keys[mid])
{
return BinarySearchForParam(length, first, mid);
}
else
{
return BinarySearchForParam(length, mid, last);
}
}
}
/// <summary>
/// Evaluates the integral of the function over the integral using the specified number of points
/// </summary>
/// <param name="func"></param>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="points"></param>
public static double GaussianQuadrature(Func<double, double> func, double a, double b, int points)
{
switch (points)
{
case 1:
return (b - a) * func.Invoke((a + b) / 2.0);
case 2:
return (b - a) / 2.0 * (func.Invoke((b - a) / 2.0 * -1 * GqBreak_TwoPoint + (a + b) / 2.0) +
func.Invoke((b - a) / 2.0 * GqBreak_TwoPoint + (a + b) / 2.0));
case 3:
return (b - a) / 2.0 * (5.0 / 9 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_ThreePoint + (a + b) / 2.0) +
8.0 / 9 * func.Invoke((a + b) / 2.0) +
5.0 / 9 * func.Invoke((b - a) / 2.0 * GqBreak_ThreePoint + (a + b) / 2.0));
case 4:
return (b - a) / 2.0 * (GqWeight_FourPoint_01 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_FourPoint_01 + (a + b) / 2.0) +
GqWeight_FourPoint_01 * func.Invoke((b - a) / 2.0 * GqBreak_FourPoint_01 + (a + b) / 2.0) +
GqWeight_FourPoint_02 * func.Invoke((b - a) / 2.0 * -1 * GqBreak_FourPoint_02 + (a + b) / 2.0) +
GqWeight_FourPoint_02 * func.Invoke((b - a) / 2.0 * GqBreak_FourPoint_02 + (a + b) / 2.0));
}
throw new NotSupportedException();
}
/// <remarks>http://en.wikipedia.org/wiki/B%C3%A9zier_curve</remarks>
private PointF CubicBezierCurve(PointF p0, PointF p1, PointF p2, PointF p3, double t)
{
return new PointF((float)(Math.Pow(1 - t, 3) * p0.X + 3 * Math.Pow(1 - t, 2) * t * p1.X +
3 * (1 - t) * Math.Pow(t, 2) * p2.X + Math.Pow(t, 3) * p3.X),
(float)(Math.Pow(1 - t, 3) * p0.Y + 3 * Math.Pow(1 - t, 2) * t * p1.Y +
3 * (1 - t) * Math.Pow(t, 2) * p2.Y + Math.Pow(t, 3) * p3.Y));
}
/// <remarks>http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html</remarks>
private PointF CubicBezierDerivative(PointF p0, PointF p1, PointF p2, PointF p3, double t)
{
return new PointF((float)(3 * Math.Pow(1 - t, 2) * (p1.X - p0.X) + 6 * (1 - t) * t * (p2.X - p1.X) + 3 * Math.Pow(t, 2) * (p3.X - p2.X)),
(float)(3 * Math.Pow(1 - t, 2) * (p1.Y - p0.Y) + 6 * (1 - t) * t * (p2.Y - p1.Y) + 3 * Math.Pow(t, 2) * (p3.Y - p2.Y)));
}
private double CubicBezierArcLengthIntegrand(PointF p0, PointF p1, PointF p2, PointF p3, double t)
{
return Math.Sqrt(Math.Pow(3 * Math.Pow(1 - t, 2) * (p1.X - p0.X) + 6 * (1 - t) * t * (p2.X - p1.X) + 3 * Math.Pow(t, 2) * (p3.X - p2.X), 2) +
Math.Pow(3 * Math.Pow(1 - t, 2) * (p1.Y - p0.Y) + 6 * (1 - t) * t * (p2.Y - p1.Y) + 3 * Math.Pow(t, 2) * (p3.Y - p2.Y), 2));
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
[SvgElement("font")]
public class SvgFont : SvgElement
{
[SvgAttribute("horiz-adv-x")]
public float HorizAdvX
{
get { return (this.Attributes["horiz-adv-x"] == null ? 0 : (float)this.Attributes["horiz-adv-x"]); }
set { this.Attributes["horiz-adv-x"] = value; }
}
[SvgAttribute("horiz-origin-x")]
public float HorizOriginX
{
get { return (this.Attributes["horiz-origin-x"] == null ? 0 : (float)this.Attributes["horiz-origin-x"]); }
set { this.Attributes["horiz-origin-x"] = value; }
}
[SvgAttribute("horiz-origin-y")]
public float HorizOriginY
{
get { return (this.Attributes["horiz-origin-y"] == null ? 0 : (float)this.Attributes["horiz-origin-y"]); }
set { this.Attributes["horiz-origin-y"] = value; }
}
[SvgAttribute("vert-adv-y")]
public float VertAdvY
{
get { return (this.Attributes["vert-adv-y"] == null ? this.Children.OfType<SvgFontFace>().First().UnitsPerEm : (float)this.Attributes["vert-adv-y"]); }
set { this.Attributes["vert-adv-y"] = value; }
}
[SvgAttribute("vert-origin-x")]
public float VertOriginX
{
get { return (this.Attributes["vert-origin-x"] == null ? this.HorizAdvX / 2 : (float)this.Attributes["vert-origin-x"]); }
set { this.Attributes["vert-origin-x"] = value; }
}
[SvgAttribute("vert-origin-y")]
public float VertOriginY
{
get { return (this.Attributes["vert-origin-y"] == null ?
(this.Children.OfType<SvgFontFace>().First().Attributes["ascent"] == null ? 0 : this.Children.OfType<SvgFontFace>().First().Ascent) :
(float)this.Attributes["vert-origin-y"]); }
set { this.Attributes["vert-origin-y"] = value; }
}
public override SvgElement DeepCopy()
{
return base.DeepCopy<SvgFont>();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Svg
{
public class SvgFontDefn : IFontDefn
{
private SvgFont _font;
private float _emScale;
private float _ppi;
private float _size;
private Dictionary<string, SvgGlyph> _glyphs;
private Dictionary<string, SvgKern> _kerning;
public float Size
{
get { return _size; }
}
public float SizeInPoints
{
get { return _size * 72.0f / _ppi; }
}
public SvgFontDefn (SvgFont font, float size, float ppi)
{
_font = font;
_size = size;
_ppi = ppi;
var face = _font.Children.OfType<SvgFontFace>().First();
_emScale = _size / face.UnitsPerEm;
}
public float Ascent(ISvgRenderer renderer)
{
float ascent = _font.Descendants().OfType<SvgFontFace>().First().Ascent;
float baselineOffset = this.SizeInPoints * (_emScale / _size) * ascent;
return renderer.DpiY / 72f * baselineOffset;
}
public IList<System.Drawing.RectangleF> MeasureCharacters(ISvgRenderer renderer, string text)
{
var result = new List<RectangleF>();
GetPath(renderer, text, result, false);
return result;
}
public System.Drawing.SizeF MeasureString(ISvgRenderer renderer, string text)
{
var result = new List<RectangleF>();
GetPath(renderer, text, result, true);
var nonEmpty = result.Where(r => r != RectangleF.Empty);
if (!nonEmpty.Any()) return SizeF.Empty;
return new SizeF(nonEmpty.Last().Right - nonEmpty.First().Left, Ascent(renderer));
}
public void AddStringToPath(ISvgRenderer renderer, GraphicsPath path, string text, PointF location)
{
var textPath = GetPath(renderer, text, null, false);
if (textPath.PointCount > 0)
{
var translate = new Matrix();
translate.Translate(location.X, location.Y);
textPath.Transform(translate);
path.AddPath(textPath, false);
}
}
private GraphicsPath GetPath(ISvgRenderer renderer, string text, IList<RectangleF> ranges, bool measureSpaces)
{
EnsureDictionaries();
RectangleF bounds;
SvgGlyph glyph;
SvgKern kern;
GraphicsPath path;
SvgGlyph prevGlyph = null;
Matrix scaleMatrix;
float xPos = 0;
var ascent = Ascent(renderer);
var result = new GraphicsPath();
if (string.IsNullOrEmpty(text)) return result;
for (int i = 0; i < text.Length; i++)
{
if (!_glyphs.TryGetValue(text.Substring(i, 1), out glyph)) glyph = _font.Descendants().OfType<SvgMissingGlyph>().First();
if (prevGlyph != null && _kerning.TryGetValue(prevGlyph.GlyphName + "|" + glyph.GlyphName, out kern))
{
xPos -= kern.Kerning * _emScale;
}
path = (GraphicsPath)glyph.Path(renderer).Clone();
scaleMatrix = new Matrix();
scaleMatrix.Scale(_emScale, -1 * _emScale, MatrixOrder.Append);
scaleMatrix.Translate(xPos, ascent, MatrixOrder.Append);
path.Transform(scaleMatrix);
bounds = path.GetBounds();
if (ranges != null)
{
if (measureSpaces && bounds == RectangleF.Empty)
{
ranges.Add(new RectangleF(xPos, 0, glyph.HorizAdvX * _emScale, ascent));
}
else
{
ranges.Add(bounds);
}
}
if (path.PointCount > 0) result.AddPath(path, false);
xPos += glyph.HorizAdvX * _emScale;
prevGlyph = glyph;
}
return result;
}
private void EnsureDictionaries()
{
if (_glyphs == null) _glyphs = _font.Descendants().OfType<SvgGlyph>().ToDictionary(g => g.Unicode ?? g.GlyphName ?? g.ID);
if (_kerning == null) _kerning = _font.Descendants().OfType<SvgKern>().ToDictionary(k => k.Glyph1 + "|" + k.Glyph2);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
[SvgElement("font-face")]
public class SvgFontFace : SvgElement
{
[SvgAttribute("alphabetic")]
public float Alphabetic
{
get { return (this.Attributes["alphabetic"] == null ? 0 : (float)this.Attributes["alphabetic"]); }
set { this.Attributes["alphabetic"] = value; }
}
[SvgAttribute("ascent")]
public float Ascent
{
get { return (this.Attributes["ascent"] == null ? this.UnitsPerEm - ((SvgFont)this.Parent).VertOriginY : (float)this.Attributes["ascent"]); }
set { this.Attributes["ascent"] = value; }
}
[SvgAttribute("ascent-height")]
public float AscentHeight
{
get { return (this.Attributes["ascent-height"] == null ? this.Ascent : (float)this.Attributes["ascent-height"]); }
set { this.Attributes["ascent-height"] = value; }
}
[SvgAttribute("descent")]
public float Descent
{
get { return (this.Attributes["descent"] == null ? ((SvgFont)this.Parent).VertOriginY : (float)this.Attributes["descent"]); }
set { this.Attributes["descent"] = value; }
}
/// <summary>
/// Indicates which font family is to be used to render the text.
/// </summary>
[SvgAttribute("font-family")]
public virtual string FontFamily
{
get { return this.Attributes["font-family"] as string; }
set { this.Attributes["font-family"] = value; }
}
/// <summary>
/// Refers to the size of the font from baseline to baseline when multiple lines of text are set solid in a multiline layout environment.
/// </summary>
[SvgAttribute("font-size")]
public virtual SvgUnit FontSize
{
get { return (this.Attributes["font-size"] == null) ? SvgUnit.Empty : (SvgUnit)this.Attributes["font-size"]; }
set { this.Attributes["font-size"] = value; }
}
/// <summary>
/// Refers to the style of the font.
/// </summary>
[SvgAttribute("font-style")]
public virtual SvgFontStyle FontStyle
{
get { return (this.Attributes["font-style"] == null) ? SvgFontStyle.inherit : (SvgFontStyle)this.Attributes["font-style"]; }
set { this.Attributes["font-style"] = value; }
}
/// <summary>
/// Refers to the varient of the font.
/// </summary>
[SvgAttribute("font-variant")]
public virtual SvgFontVariant FontVariant
{
get { return (this.Attributes["font-variant"] == null) ? SvgFontVariant.inherit : (SvgFontVariant)this.Attributes["font-variant"]; }
set { this.Attributes["font-variant"] = value; }
}
/// <summary>
/// Refers to the boldness of the font.
/// </summary>
[SvgAttribute("font-weight")]
public virtual SvgFontWeight FontWeight
{
get { return (this.Attributes["font-weight"] == null) ? SvgFontWeight.inherit : (SvgFontWeight)this.Attributes["font-weight"]; }
set { this.Attributes["font-weight"] = value; }
}
[SvgAttribute("panose-1")]
public string Panose1
{
get { return this.Attributes["panose-1"] as string; }
set { this.Attributes["panose-1"] = value; }
}
[SvgAttribute("units-per-em")]
public float UnitsPerEm
{
get { return (this.Attributes["units-per-em"] == null ? 1000 : (float)this.Attributes["units-per-em"]); }
set { this.Attributes["units-per-em"] = value; }
}
[SvgAttribute("x-height")]
public float XHeight
{
get { return (this.Attributes["x-height"] == null ? float.MinValue : (float)this.Attributes["x-height"]); }
set { this.Attributes["x-height"] = value; }
}
public override SvgElement DeepCopy()
{
return base.DeepCopy<SvgFontFace>();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
[SvgElement("font-face-src")]
public class SvgFontFaceSrc : SvgElement
{
public override SvgElement DeepCopy()
{
return base.DeepCopy<SvgFontFaceSrc>();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
[SvgElement("font-face-uri")]
public class SvgFontFaceUri : SvgElement
{
private Uri _referencedElement;
[SvgAttribute("href", SvgAttributeAttribute.XLinkNamespace)]
public virtual Uri ReferencedElement
{
get { return this._referencedElement; }
set { this._referencedElement = value; }
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgFontFaceUri>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgFontFaceUri;
newObj.ReferencedElement = this.ReferencedElement;
return newObj;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Svg.Pathing;
using System.Drawing.Drawing2D;
namespace Svg
{
[SvgElement("glyph")]
public class SvgGlyph : SvgVisualElement
{
private GraphicsPath _path;
/// <summary>
/// Gets or sets a <see cref="SvgPathSegmentList"/> of path data.
/// </summary>
[SvgAttribute("d")]
public SvgPathSegmentList PathData
{
get { return this.Attributes.GetAttribute<SvgPathSegmentList>("d"); }
set { this.Attributes["d"] = value; }
}
[SvgAttribute("glyph-name")]
public virtual string GlyphName
{
get { return this.Attributes["glyph-name"] as string; }
set { this.Attributes["glyph-name"] = value; }
}
[SvgAttribute("horiz-adv-x")]
public float HorizAdvX
{
get { return (this.Attributes["horiz-adv-x"] == null ? this.Parents.OfType<SvgFont>().First().HorizAdvX : (float)this.Attributes["horiz-adv-x"]); }
set { this.Attributes["horiz-adv-x"] = value; }
}
[SvgAttribute("unicode")]
public string Unicode
{
get { return this.Attributes["unicode"] as string; }
set { this.Attributes["unicode"] = value; }
}
[SvgAttribute("vert-adv-y")]
public float VertAdvY
{
get { return (this.Attributes["vert-adv-y"] == null ? this.Parents.OfType<SvgFont>().First().VertAdvY : (float)this.Attributes["vert-adv-y"]); }
set { this.Attributes["vert-adv-y"] = value; }
}
[SvgAttribute("vert-origin-x")]
public float VertOriginX
{
get { return (this.Attributes["vert-origin-x"] == null ? this.Parents.OfType<SvgFont>().First().VertOriginX : (float)this.Attributes["vert-origin-x"]); }
set { this.Attributes["vert-origin-x"] = value; }
}
[SvgAttribute("vert-origin-y")]
public float VertOriginY
{
get { return (this.Attributes["vert-origin-y"] == null ? this.Parents.OfType<SvgFont>().First().VertOriginY : (float)this.Attributes["vert-origin-y"]); }
set { this.Attributes["vert-origin-y"] = value; }
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public override GraphicsPath Path(ISvgRenderer renderer)
{
if (this._path == null || this.IsPathDirty)
{
_path = new GraphicsPath();
foreach (SvgPathSegment segment in this.PathData)
{
segment.AddToPath(_path);
}
this.IsPathDirty = false;
}
return _path;
}
/// <summary>
/// Gets or sets a value to determine if anti-aliasing should occur when the element is being rendered.
/// </summary>
protected override bool RequiresSmoothRendering
{
get { return true; }
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public override System.Drawing.RectangleF Bounds
{
get { return this.Path(null).GetBounds(); }
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgGlyph"/> class.
/// </summary>
public SvgGlyph()
{
var pathData = new SvgPathSegmentList();
this.Attributes["d"] = pathData;
}
public override SvgElement DeepCopy()
{
return DeepCopy<SvgGlyph>();
}
public override SvgElement DeepCopy<T>()
{
var newObj = base.DeepCopy<T>() as SvgGlyph;
foreach (var pathData in this.PathData)
newObj.PathData.Add(pathData.Clone());
return newObj;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Svg
{
public abstract class SvgKern : SvgElement
{
[SvgAttribute("g1")]
public string Glyph1
{
get { return this.Attributes["g1"] as string; }
set { this.Attributes["g1"] = value; }
}
[SvgAttribute("g2")]
public string Glyph2
{
get { return this.Attributes["g2"] as string; }
set { this.Attributes["g2"] = value; }
}
[SvgAttribute("u1")]
public string Unicode1
{
get { return this.Attributes["u1"] as string; }
set { this.Attributes["u1"] = value; }
}
[SvgAttribute("u2")]
public string Unicode2
{
get { return this.Attributes["u2"] as string; }
set { this.Attributes["u2"] = value; }
}
[SvgAttribute("k")]
public float Kerning
{
get { return (this.Attributes["k"] == null ? 0 : (float)this.Attributes["k"]); }
set { this.Attributes["k"] = value; }
}
}
[SvgElement("vkern")]
public class SvgVerticalKern : SvgKern
{
public override SvgElement DeepCopy()
{
return base.DeepCopy<SvgVerticalKern>();
}
}
[SvgElement("hkern")]
public class SvgHorizontalKern : SvgKern
{
public override SvgElement DeepCopy()
{
return base.DeepCopy<SvgHorizontalKern>();
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment