.
1 /*
2 Copyright (C) 2013 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using B;
16
17 namespace BirdFont {
18
19 class SvgFont : GLib.Object {
20 Font font;
21 double units = 1;
22 double font_advance = 0;
23
24 public SvgFont (Font f) {
25 this.font = f;
26 }
27
28 /** Load svg font from file. */
29 public void load (string path) {
30 string data;
31 XmlParser xml_parser;
32 try {
33 FileUtils.get_contents (path, out data);
34 xml_parser = new XmlParser (data);
35 parse_svg_font (xml_parser.get_root_tag ());
36 } catch (GLib.Error e) {
37 warning (e.message);
38 }
39 }
40
41 void parse_svg_font (Tag tag) {
42 foreach (Tag t in tag) {
43 if (t.get_name () == "defs") {
44 parse_svg_font (t);
45 }
46
47 if (t.get_name () == "font") {
48 parse_font_tag (t);
49 parse_svg_font (t);
50 }
51
52 if (t.get_name () == "font-face") {
53 parse_font_limits (t);
54 }
55
56 if (t.get_name () == "hkern") {
57 parse_hkern (t);
58 }
59
60 if (t.get_name () == "glyph") {
61 parse_glyph (t);
62 }
63 }
64 }
65
66 void parse_hkern (Tag tag) {
67 string left = "";
68 string right = "";
69 string left_name = "";
70 string right_name = "";
71 double kerning = 0;
72 unichar l, r;
73 StringBuilder sl, sr;
74 GlyphRange grr, grl;
75 KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();
76
77 foreach (Attribute attr in tag.get_attributes ()) {
78 // left
79 if (attr.get_name () == "u1") {
80 left = attr.get_content ();
81 }
82
83 // right
84 if (attr.get_name () == "u2") {
85 right = attr.get_content ();
86 }
87
88 if (attr.get_name () == "g1") {
89 left_name = attr.get_content ();
90 }
91
92 if (attr.get_name () == "g2") {
93 right_name = attr.get_content ();
94 }
95
96 // kerning
97 if (attr.get_name () == "k") {
98 kerning = double.parse (attr.get_content ()) * units;
99 }
100 }
101
102 // FIXME: ranges and sequences for u1 & u2 + g1 & g2
103 foreach (string lk in left.split (",")) {
104 foreach (string rk in right.split (",")) {
105 l = get_unichar (lk);
106 r = get_unichar (rk);
107
108 sl = new StringBuilder ();
109 sl.append_unichar (l);
110
111 sr = new StringBuilder ();
112 sr.append_unichar (r);
113
114 try {
115 grl = new GlyphRange ();
116 grl.parse_ranges (sl.str);
117
118 grr = new GlyphRange ();
119 grr.parse_ranges (sr.str);
120
121 classes.set_kerning (grl, grr, -kerning);
122 } catch (MarkupError e) {
123 warning (e.message);
124 }
125 }
126 }
127 }
128
129 void parse_font_limits (Tag tag) {
130 double top_limit = 0;
131 double bottom_limit = 0;
132
133 foreach (Attribute attr in tag.get_attributes ()) {
134 if (attr.get_name () == "units-per-em") {
135 units = 100.0 / double.parse (attr.get_content ());
136 }
137 }
138
139 foreach (Attribute attr in tag.get_attributes ()) {
140 if (attr.get_name () == "ascent") {
141 top_limit = double.parse (attr.get_content ());
142 }
143
144 if (attr.get_name () == "descent") {
145 bottom_limit = double.parse (attr.get_content ());
146 }
147 }
148
149 top_limit *= units;
150 bottom_limit *= units;
151
152 font.bottom_limit = bottom_limit;
153 font.top_limit = top_limit;
154 }
155
156 void parse_font_tag (Tag tag) {
157 foreach (Attribute attr in tag.get_attributes ()) {
158 if (attr.get_name () == "horiz-adv-x") {
159 font_advance = double.parse (attr.get_content ());
160 }
161
162 if (attr.get_name () == "id") {
163 font.set_name (attr.get_content ());
164 }
165 }
166 }
167
168 /** Obtain unichar value from either a character, a hex representation of
169 * a character or series of characters (f, m or ffi).
170 */
171 static unichar get_unichar (string val) {
172 string v = val;
173 unichar unicode_value;
174
175 if (val == "&") {
176 return '&';
177 }
178
179 // TODO: parse ligatures
180 if (v.has_prefix ("&")) {
181 // parse hex value
182 v = v.substring (0, v.index_of (";"));
183 v = v.replace ("&#x", "U+");
184 v = v.replace (";", "");
185 unicode_value = Font.to_unichar (v);
186 } else {
187 // obtain unicode value
188
189 if (v.char_count () > 1) {
190 warning ("font contains ligatures");
191 return '\0';
192 }
193
194 unicode_value = v.get_char (0);
195 }
196
197 return unicode_value;
198 }
199
200 bool is_ligature (string v) {
201 if (v.has_prefix ("&")) {
202 return false;
203 }
204
205 return v.char_count () > 1;
206 }
207
208 void parse_glyph (Tag tag) {
209 unichar unicode_value = 0;
210 string glyph_name = "";
211 string svg = "";
212 Glyph glyph;
213 GlyphCollection glyph_collection;
214 double advance = font_advance;
215 string ligature = "";
216 SvgParser parser = new SvgParser ();
217 StringBuilder unicode_name;
218
219 parser.set_format (SvgFormat.INKSCAPE);
220
221 foreach (Attribute attr in tag.get_attributes ()) {
222 if (attr.get_name () == "unicode") {
223 unicode_value = get_unichar (attr.get_content ());
224
225 if (glyph_name == "") {
226 glyph_name = attr.get_content ();
227 }
228
229 if (is_ligature (attr.get_content ())) {
230 ligature = attr.get_content ();
231 }
232 }
233
234 // svg data
235 if (attr.get_name () == "d") {
236 svg = attr.get_content ();
237 }
238
239 if (attr.get_name () == "glyph-name") {
240 glyph_name = attr.get_content ();
241 }
242
243 if (attr.get_name () == "horiz-adv-x") {
244 advance = double.parse (attr.get_content ());
245 }
246 }
247
248 unicode_name = new StringBuilder ();
249 unicode_name.append_unichar (unicode_value);
250
251 glyph = new Glyph (unicode_name.str, unicode_value);
252 parser.add_path_to_glyph (svg, glyph, true, units);
253 glyph.right_limit = glyph.left_limit + advance * units;
254
255 // FIXME: add svg font ligatures
256 /*
257 if (ligature != "") {
258 glyph.set_ligature_substitution (ligature);
259 }
260 */
261
262 glyph_collection = new GlyphCollection (unicode_value, glyph_name);
263 glyph_collection.insert_glyph (glyph, true);
264
265 font.add_glyph_collection (glyph_collection);
266 }
267 }
268
269 }
270