SvgFontDefn.cs 4.7 KB
Newer Older
Eric Domke's avatar
Eric Domke committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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>();
Eric Domke's avatar
Eric Domke committed
48
            using (var path = GetPath(renderer, text, result, false)) { }
Eric Domke's avatar
Eric Domke committed
49
50
51
52
53
54
            return result;
        }

        public System.Drawing.SizeF MeasureString(ISvgRenderer renderer, string text)
        {
            var result = new List<RectangleF>();
Eric Domke's avatar
Eric Domke committed
55
            using (var path = GetPath(renderer, text, result, true)) { }
Eric Domke's avatar
Eric Domke committed
56
57
58
59
60
61
62
63
64
65
            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)
            {
Eric Domke's avatar
Eric Domke committed
66
67
68
69
70
71
                using (var translate = new Matrix())
                {
                    translate.Translate(location.X, location.Y);
                    textPath.Transform(translate);
                    path.AddPath(textPath, false);
                }
Eric Domke's avatar
Eric Domke committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
            }
        }

        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);
Eric Domke's avatar
Eric Domke committed
104
                scaleMatrix.Dispose();
Eric Domke's avatar
Eric Domke committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

                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);
        }
Eric Domke's avatar
Eric Domke committed
132
133
134
135
136
137

        public void Dispose()
        {
            _glyphs = null;
            _kerning = null;
        }
Eric Domke's avatar
Eric Domke committed
138
139
    }
}