The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

GlyphRange.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/GlyphRange.vala.
Parse more style attributes in SVG files
1 /* 2 Copyright (C) 2012 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 namespace BirdFont { 16 17 public class GlyphRange { 18 19 public string name {get; set;} 20 21 public Gee.ArrayList<UniRange> ranges; 22 23 /** Glyphs without a corresponding unicode value (ligatures). */ 24 public Gee.ArrayList<string> unassigned; 25 26 uint32 len = 0; 27 28 bool range_is_class = false; 29 30 public GlyphRange () { 31 ranges = new Gee.ArrayList<UniRange> (); 32 unassigned = new Gee.ArrayList<string> (); 33 name = "No name"; 34 } 35 36 public void add_unassigned (string glyph_name) { 37 unassigned.add (glyph_name); 38 } 39 40 public bool is_class () { 41 return range_is_class || len > 1; 42 } 43 44 public void set_class (bool c) { 45 range_is_class = true; 46 } 47 48 public bool is_empty () { 49 return len == 0; 50 } 51 52 public void empty () { 53 unassigned.clear (); 54 ranges.clear (); 55 len = 0; 56 } 57 58 public unowned Gee.ArrayList<UniRange> get_ranges () { 59 return ranges; 60 } 61 62 // TODO: complete localized alphabetical sort åäö is not the right order for example. 63 public void sort () { 64 ranges.sort ((a, b) => { 65 UniRange first, next; 66 bool r; 67 68 first = (UniRange) a; 69 next = (UniRange) b; 70 71 r = first.start > next.start; 72 return_val_if_fail (first.start != next.start, 0); 73 74 return (r) ? 1 : -1; 75 }); 76 } 77 78 public void add_single (unichar c) { 79 add_range (c, c); 80 } 81 82 public uint32 get_length () { 83 uint32 l = len; 84 l += unassigned.size; 85 return l; 86 } 87 88 public void add_range (unichar start, unichar stop) { 89 unichar b, s; 90 if (unique (start, stop)) { 91 append_range (start, stop); 92 } else { 93 94 // make sure this range does not overlap existing ranges 95 b = start; 96 s = b; 97 if (!unique (b, b)) { 98 while (b < stop) { 99 if (!unique (b, b)) { 100 b++; 101 } else { 102 if (s != b) { 103 add_range (b, stop); 104 } 105 106 b++; 107 s = b; 108 } 109 } 110 } else { 111 while (b < stop) { 112 if (unique (b, b)) { 113 b++; 114 } else { 115 if (s != b) { 116 add_range (start, b - 1); 117 } 118 119 b++; 120 s = b; 121 } 122 } 123 } 124 } 125 } 126 127 /** Parse ranges on the form a-z. Single characters can be added as well as 128 * multiple ranges separated by space. The word "space" is used to kern against 129 * the space character and the word "divis" is used to kern against "-". 130 * @param ranges unicode ranges 131 */ 132 public void parse_ranges (string ranges) throws MarkupError { 133 parse_range_string (ranges); 134 } 135 136 private void parse_range_string (string ranges) throws MarkupError { 137 string[] r; 138 139 if (ranges == " ") { 140 add_single (' '); 141 } 142 143 r = ranges.split (" "); 144 145 foreach (string w in r) { 146 w = w.replace (" ", ""); 147 148 if (w == "") { 149 continue; 150 } 151 152 if (w.char_count () == 1) { 153 add_single (w.get_char ()); 154 } else if (w == "space") { 155 add_single (' '); 156 } else if (w == "divis") { 157 add_single ('-'); 158 } else if (w == "null") { 159 add_single ('\0'); 160 } else if (w.index_of ("-") > -1) { 161 parse_range (w); 162 } else if (w == "quote") { 163 add_single ('"'); 164 } else if (w == "ampersand") { 165 add_single ('&'); 166 } else { 167 unassigned.add (w); 168 } 169 } 170 } 171 172 /** A readable representation of ranges, see parse_ranges for parsing 173 * this string. This function is used for storing ranges in th .bf format. 174 */ 175 public string get_all_ranges () { 176 bool first = true; 177 StringBuilder s = new StringBuilder (); 178 foreach (UniRange u in ranges) { 179 if (!first) { 180 s.append (" "); 181 } 182 183 if (u.start == u.stop) { 184 s.append (get_serialized_char (u.start)); 185 } else { 186 s.append (get_serialized_char (u.start)); 187 s.append ("-"); 188 s.append (get_serialized_char (u.stop)); 189 } 190 191 first = false; 192 } 193 194 foreach (string ur in unassigned) { 195 if (!first) { 196 s.append (" "); 197 } 198 199 s.append (ur); 200 201 first = false; 202 } 203 return s.str; 204 } 205 206 public static string serialize (string s) { 207 208 if (s == "space") { 209 return s; 210 } 211 212 if (s == "divis") { 213 return s; 214 } 215 216 if (s == "null") { 217 return s; 218 } 219 220 if (s == "quote") { 221 return s; 222 } 223 224 if (s == "ampersand") { 225 return s; 226 } 227 228 if (s == "&quot;") { 229 return s; 230 } 231 232 if (s == "&amp;") { 233 return s; 234 } 235 236 if (s == "&lt;") { 237 return s; 238 } 239 240 if (s == "&gt;") { 241 return s; 242 } 243 244 if (s.char_count () > 1) { 245 return s; // ligature 246 } 247 248 return get_serialized_char (s.get_char (0)); 249 } 250 251 public static string get_serialized_char (unichar c) { 252 StringBuilder s = new StringBuilder (); 253 254 if (c == '&') { 255 return "&amp;"; 256 } 257 258 if (c == '<') { 259 return "&lt;"; 260 } 261 262 if (c == '>') { 263 return "&gt;"; 264 } 265 266 if (c == ' ') { 267 return "space"; 268 } 269 270 if (c == '-') { 271 return "divis"; 272 } 273 274 if (c == '\0') { 275 return "null"; 276 } 277 278 if (c == '"') { 279 return "quote"; 280 } 281 282 if (c == '&') { 283 return "ampersand"; 284 } 285 286 s.append_unichar (c); 287 return s.str; 288 } 289 290 public static string unserialize (string c) { 291 if (c == "&quot;") { 292 return "\""; 293 } 294 295 if (c == "&amp;") { 296 return "&"; 297 } 298 299 if (c == "&lt;") { 300 return "<"; 301 } 302 303 if (c == "&gt;") { 304 return ">"; 305 } 306 307 if (c == "space") { 308 return " "; 309 } 310 311 if (c == "divis") { 312 return "-"; 313 } 314 315 if (c == "null") { 316 return "\0"; 317 } 318 319 if (c == "quote") { 320 return "\""; 321 } 322 323 if (c == "ampersand") { 324 return "&"; 325 } 326 327 return c; 328 } 329 330 private void parse_range (string s) throws MarkupError { 331 string[] r = s.split ("-"); 332 bool null_range = false; 333 334 if (r.length == 2 && r[0] == "null" && r[1] == "null") { 335 null_range = true; 336 } else if (r.length == 2 && r[0] == "null" && unserialize (r[1]).char_count () == 1) { 337 null_range = true; 338 } 339 340 if (!null_range) { 341 if (r.length != 2 342 || unserialize (r[0]).char_count () != 1 343 || unserialize (r[1]).char_count () != 1) { 344 throw new MarkupError.PARSE (@"$s is not a valid range, it should be on the form A-Z."); 345 } 346 } 347 348 append_range (unserialize (r[0]).get_char (), unserialize (r[1]).get_char ()); 349 } 350 351 private void append_range (unichar start, unichar stop) { 352 UniRange r; 353 r = insert_range (start, stop); // insert a unique range 354 merge_range (r); // join connecting ranges 355 } 356 357 private void merge_range (UniRange r) { 358 Gee.ArrayList<UniRange> deleted = new Gee.ArrayList<UniRange> (); 359 Gee.ArrayList<UniRange> merged = new Gee.ArrayList<UniRange> (); 360 bool updated = false; 361 362 foreach (UniRange u in ranges) { 363 if (u == r) { 364 continue; 365 } 366 367 if (u.start == r.stop + 1) { 368 u.start = r.start; 369 deleted.add (r); 370 merged.add (u); 371 break; 372 } 373 374 if (u.stop == r.start - 1) { 375 u.stop = r.stop; 376 deleted.add (r); 377 merged.add (u); 378 break; 379 } 380 } 381 382 updated = merged.size > 0; 383 384 foreach (UniRange m in deleted) { 385 while (ranges.remove (m)); 386 } 387 388 foreach (UniRange m in merged) { 389 merge_range (m); 390 } 391 392 if (updated) { 393 merge_range (r); 394 } 395 } 396 397 public string get_char (uint32 index) { 398 int64 ti; 399 string chr; 400 UniRange r; 401 StringBuilder sb; 402 unichar c; 403 404 if (index > len + unassigned.size) { 405 return "\0".dup(); 406 } 407 408 if (index >= len) { 409 if (index - len >= unassigned.size) { 410 return "\0".dup(); 411 } 412 413 chr = unassigned.get ((int) (index - len)); 414 return chr; 415 } 416 417 r = ranges.get (0); 418 ti = index; 419 420 foreach (UniRange u in ranges) { 421 ti -= u.length (); 422 423 if (ti < 0) { 424 r = u; 425 break; 426 } 427 } 428 429 sb = new StringBuilder (); 430 c = r.get_char ((unichar) (ti + r.length ())); 431 432 if (unlikely (!c.validate ())) { 433 warning ("Not a valid unicode character."); 434 return ""; 435 } 436 437 sb.append_unichar (c); 438 439 return sb.str; 440 } 441 442 public uint32 length () { 443 return len; 444 } 445 446 public bool has_character (string c) { 447 unichar s; 448 string uns; 449 450 if (unassigned.index_of (c) != -1) { 451 return true; 452 } 453 454 uns = unserialize (c); 455 456 if (uns.char_count () != 1) { 457 warning (@"Expecting a single character got $c"); 458 } 459 460 s = uns.get_char (); 461 return !unique (s, s); 462 } 463 464 public bool has_unichar (unichar c) { 465 return !unique (c, c); 466 } 467 468 private bool unique (unichar start, unichar stop) { 469 foreach (UniRange u in ranges) { 470 if (inside (start, u.start, u.stop)) { 471 return false; 472 } 473 474 if (inside (stop, u.start, u.stop)) { 475 return false; 476 } 477 478 if (inside (u.start, start, stop)) { 479 return false; 480 } 481 482 if (inside (u.stop, start, stop)) { 483 return false; 484 } 485 } 486 487 return true; 488 } 489 490 private static bool inside (unichar start, unichar u_start, unichar u_stop) { 491 return (u_start <= start <= u_stop); 492 } 493 494 private UniRange insert_range (unichar start, unichar stop) { 495 if (unlikely (start > stop)) { 496 warning ("start > stop"); 497 stop = start; 498 } 499 500 UniRange ur = new UniRange (start, stop); 501 len += ur.length (); 502 ranges.add (ur); 503 504 return ur; 505 } 506 507 public void print_all () { 508 stdout.printf ("Ranges:\n"); 509 stdout.printf (get_all_ranges ()); 510 stdout.printf ("\n"); 511 } 512 513 public string to_string () { 514 return get_all_ranges (); 515 } 516 } 517 518 } 519