The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

KerningClasses.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
Circle boundaries heads/master
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 Cairo; 16 using B; 17 using Math; 18 using Gee; 19 20 namespace BirdFont { 21 22 public class KerningClasses : GLib.Object { 23 24 // kerning for classes 25 public Gee.ArrayList<GlyphRange> classes_first; 26 public Gee.ArrayList<GlyphRange> classes_last; 27 public Gee.ArrayList<Kerning> classes_kerning; 28 29 // kerning for single glyphs 30 Gee.HashMap<string, double?> single_kerning; 31 public Gee.ArrayList<string> single_kerning_letters_left; 32 public Gee.ArrayList<string> single_kerning_letters_right; 33 34 public delegate void KerningIterator (KerningPair list); 35 public delegate void KerningClassIterator (string left, string right, double kerning); 36 37 /** Ensure that map iterator is not invalidated because of inserts. */ 38 bool protect_map = false; 39 40 public unowned Font font; 41 42 public KerningClasses (Font font) { 43 this.font = font; 44 45 font.font_deleted.connect (() => { 46 this.font = Font.empty; 47 }); 48 49 classes_first = new Gee.ArrayList<GlyphRange> (); 50 classes_last = new Gee.ArrayList<GlyphRange> (); 51 classes_kerning = new Gee.ArrayList<Kerning> (); 52 53 single_kerning_letters_left = new Gee.ArrayList<string> (); 54 single_kerning_letters_right = new Gee.ArrayList<string> (); 55 56 single_kerning = new HashMap<string, double?> (); 57 } 58 59 public void update_range (GlyphRange old, GlyphRange new_range) { 60 string o = old.get_all_ranges (); 61 62 foreach (GlyphRange gr in classes_first) { 63 try { 64 if (gr.get_all_ranges () == o) { 65 gr.parse_ranges (new_range.get_all_ranges ()); 66 } 67 } catch (GLib.MarkupError e) { 68 warning (e.message); 69 } 70 } 71 72 foreach (GlyphRange gr in classes_last) { 73 try { 74 if (gr.get_all_ranges () == o) { 75 gr.parse_ranges (new_range.get_all_ranges ()); 76 } 77 } catch (GLib.MarkupError e) { 78 warning (e.message); 79 } 80 } 81 } 82 83 /** Copy kerning pairs for newly created spacing classes. */ 84 public void copy_single_kerning_pairs (string from_spacing_class, string to_spacing_class) { 85 double? kerning; 86 87 foreach (string left in single_kerning_letters_left) { 88 kerning = get_kerning_for_single_glyphs (left, from_spacing_class); 89 90 if (kerning != null) { 91 set_kerning_for_single_glyphs (left, to_spacing_class, (!) kerning); 92 } 93 } 94 95 foreach (string right in single_kerning_letters_right) { 96 kerning = get_kerning_for_single_glyphs (from_spacing_class, right); 97 98 if (kerning != null) { 99 set_kerning_for_single_glyphs (to_spacing_class, right, (!) kerning); 100 } 101 } 102 } 103 104 /** Class based gpos kerning. */ 105 public double get_kerning_for_pair (string a, string b, GlyphRange? gr_left, GlyphRange? gr_right) { 106 double k = 0; 107 GlyphRange grl, grr; 108 109 try { 110 if (gr_left == null) { 111 grl = new GlyphRange (); 112 grl.parse_ranges (a); 113 } else { 114 grl = (!) gr_left; 115 } 116 117 if (gr_right == null) { 118 grr = new GlyphRange (); 119 grr.parse_ranges (a); 120 } else { 121 grr = (!) gr_right; 122 } 123 124 if (gr_left == null && gr_right == null) { 125 k = get_kerning (a, b); 126 return k; 127 } 128 129 if (gr_left != null && gr_right != null) { 130 return get_kerning_for_range (grl, grr); 131 } 132 133 if (gr_left != null && gr_right == null) { 134 return get_kern_for_range_to_char (grl, b); 135 } 136 137 if (gr_left == null && gr_right != null) { 138 return get_kern_for_char_to_range (a, grr); 139 } 140 } catch (MarkupError e) { 141 warning (e.message); 142 } 143 144 if (unlikely (k == 0)) { 145 warning ("no kerning found"); 146 } 147 148 return 0; 149 } 150 151 public double? get_kerning_for_single_glyphs (string first, string next) { 152 double? k = null; 153 double? kerning = null; 154 string left = GlyphRange.serialize (first); 155 string right = GlyphRange.serialize (next); 156 157 foreach (string l in get_spacing_class (left)) { 158 foreach (string r in get_spacing_class (right)) { 159 kerning = single_kerning.get (@"$l - $r"); 160 161 if (kerning != null) { 162 k = kerning; 163 } 164 } 165 } 166 167 return k; 168 } 169 170 private Gee.ArrayList<string> get_spacing_class (string c) { 171 return font.get_spacing ().get_all_connections (c); 172 } 173 174 public void set_kerning_for_single_glyphs (string le, string ri, double k) { 175 string left = GlyphRange.serialize (le); 176 string right = GlyphRange.serialize (ri); 177 string cleft = (!)GlyphRange.unserialize (left); 178 string cright = (!)GlyphRange.unserialize (right); // FIXME: get_char? 179 180 if (protect_map) { 181 warning ("Map is protected."); 182 return; 183 } 184 185 foreach (string l in get_spacing_class (cleft)) { 186 foreach (string r in get_spacing_class (cright)) { 187 if (!single_kerning_letters_left.contains (cleft)) { 188 single_kerning_letters_left.add (cleft); 189 } 190 191 if (!single_kerning_letters_right.contains (cright)) { 192 single_kerning_letters_right.add (cright); 193 } 194 195 left = GlyphRange.serialize (l); 196 right = GlyphRange.serialize (r); 197 single_kerning.set (@"$left - $right", k); 198 } 199 } 200 } 201 202 public void set_kerning (GlyphRange left_range, GlyphRange right_range, double k, int class_index = -1) { 203 int index; 204 205 if (left_range.get_length () == 0 || right_range.get_length () == 0) { 206 warning ("no glyphs"); 207 return; 208 } 209 210 if (protect_map) { 211 warning ("Map is protected."); 212 return; 213 } 214 215 if (!left_range.is_class () && !right_range.is_class ()) { 216 set_kerning_for_single_glyphs (left_range.get_all_ranges (), right_range.get_all_ranges (), k); 217 return; 218 } 219 220 index = get_kerning_item_index (left_range, right_range); 221 222 // keep the list sorted (classes first then single glyphs) 223 if (index == -1) { 224 if (class_index < 0) { 225 classes_first.add (left_range); 226 classes_last.add (right_range); 227 classes_kerning.add (new Kerning (k)); 228 } else { 229 classes_first.insert (class_index, left_range); 230 classes_last.insert (class_index, right_range); 231 classes_kerning.insert (class_index, new Kerning (k)); 232 } 233 } else { 234 return_if_fail (0 <= index < classes_first.size); 235 classes_kerning.get (index).val = k; 236 } 237 } 238 239 public bool has_kerning (string first, string next) { 240 string f = ""; 241 string n = ""; 242 GlyphRange gr; 243 GlyphRange gl; 244 int len; 245 246 foreach (string l in get_spacing_class (first)) { 247 foreach (string r in get_spacing_class (next)) { 248 f = GlyphRange.serialize (l); 249 n = GlyphRange.serialize (r); 250 if (single_kerning.has_key (@"$f - $n")) { 251 return true; 252 } 253 } 254 } 255 256 len = (int) classes_first.size; 257 258 return_val_if_fail (len == classes_last.size, false); 259 return_val_if_fail (len == classes_kerning.size, false); 260 261 for (int i = len - 1; i >= 0; i--) { 262 gl = classes_first.get (i); 263 gr = classes_last.get (i); 264 265 if (gl.has_character (first) 266 && gr.has_character (next)) { 267 268 return true; 269 } 270 } 271 272 return false; 273 } 274 275 public double get_kerning_for_range (GlyphRange range_first, GlyphRange range_last) { 276 GlyphRange r; 277 GlyphRange l; 278 int len = (int) classes_first.size; 279 280 len = (int) classes_first.size; 281 return_val_if_fail (len == classes_last.size, 0); 282 return_val_if_fail (len == classes_kerning.size, 0); 283 284 if (!(range_first.is_class () || range_last.is_class ())) { 285 get_kerning_for_single_glyphs (range_first.get_all_ranges (), range_last.get_all_ranges ()); 286 return 0; 287 } 288 289 for (int i = len - 1; i >= 0; i--) { // last class is applied first 290 l = classes_first.get (i); 291 r = classes_last.get (i); 292 293 if (l.get_all_ranges () == range_first.get_all_ranges () 294 && r.get_all_ranges () == range_last.get_all_ranges ()) { 295 return classes_kerning.get (i).val; 296 } 297 } 298 299 return 0; 300 } 301 302 public int get_kerning_item_index (GlyphRange range_first, GlyphRange range_last) { 303 GlyphRange r; 304 GlyphRange l; 305 int len = (int) classes_first.size; 306 307 len = (int) classes_first.size; 308 return_val_if_fail (len == classes_last.size, 0); 309 return_val_if_fail (len == classes_kerning.size, 0); 310 311 if (!(range_first.is_class () || range_last.is_class ())) { 312 warning (@"Expecting a class, $(range_first.get_all_ranges ()) and $(range_last.get_all_ranges ())"); 313 return -1; 314 } 315 316 for (int i = len - 1; i >= 0; i--) { 317 l = classes_first.get (i); 318 r = classes_last.get (i); 319 320 if (l.get_all_ranges () == range_first.get_all_ranges () 321 && r.get_all_ranges () == range_last.get_all_ranges ()) { 322 return i; 323 } 324 } 325 326 return -1; 327 } 328 329 public double get_kerning (string left_glyph, string right_glyph) { 330 GlyphRange r; 331 GlyphRange l; 332 int len = (int) classes_first.size; 333 double? d; 334 335 d = get_kerning_for_single_glyphs (left_glyph, right_glyph); 336 if (d != null) { 337 return (!)d; 338 } 339 340 len = (int)classes_first.size; 341 return_val_if_fail (len == classes_last.size, 0); 342 return_val_if_fail (len == classes_kerning.size, 0); 343 344 for (int i = len - 1; i >= 0; i--) { 345 l = classes_first.get (i); 346 r = classes_last.get (i); 347 348 if (l.has_character (left_glyph) 349 && r.has_character (right_glyph)) { 350 351 return classes_kerning.get (i).val; 352 } 353 } 354 355 return 0; 356 } 357 358 public double get_kern_for_range_to_char (GlyphRange left_range, string right_char) { 359 GlyphRange r; 360 GlyphRange l; 361 int len = (int) classes_first.size; 362 363 len = (int)classes_first.size; 364 return_val_if_fail (len == classes_last.size, 0); 365 return_val_if_fail (len == classes_kerning.size, 0); 366 367 if (unlikely (!left_range.is_class ())) { 368 warning (@"Expecting a class, $(left_range.get_all_ranges ())"); 369 return -1; 370 } 371 372 foreach (string right in get_spacing_class (right_char)) { 373 for (int i = len - 1; i >= 0; i--) { 374 l = classes_first.get (i); 375 r = classes_last.get (i); 376 377 if (l.get_all_ranges () == left_range.get_all_ranges () 378 && r.has_character (right)) { 379 return classes_kerning.get (i).val; 380 } 381 } 382 } 383 384 return 0; 385 } 386 387 public double get_kern_for_char_to_range (string left_char, GlyphRange right_range) { 388 GlyphRange r; 389 GlyphRange l; 390 int len = (int) classes_first.size; 391 392 len = (int)classes_first.size; 393 return_val_if_fail (len == classes_last.size, 0); 394 return_val_if_fail (len == classes_kerning.size, 0); 395 396 if (!right_range.is_class ()) { 397 warning ("Expecting a class"); 398 return 0; 399 } 400 401 foreach (string left in get_spacing_class (left_char)) { 402 for (int i = len - 1; i >= 0; i--) { 403 l = classes_first.get (i); 404 r = classes_last.get (i); 405 406 if (l.has_character (left) 407 && r.get_all_ranges () == right_range.get_all_ranges ()) { 408 return classes_kerning.get (i).val; 409 } 410 } 411 } 412 413 return 0; 414 } 415 416 public void print_all () { 417 print ("Kernings classes:\n"); 418 for (int i = 0; i < classes_first.size; i++) { 419 print (classes_first.get (i).get_all_ranges ()); 420 print ("\t\t"); 421 print (classes_last.get (i).get_all_ranges ()); 422 print ("\t\t"); 423 print (@"$(classes_kerning.get (i).val)"); 424 print ("\t\t"); 425 426 if (classes_first.get (i).is_class () || classes_last.get (i).is_class ()) { 427 print ("class"); 428 } 429 430 print ("\n"); 431 } 432 433 print ("\n"); 434 print ("Kernings for pairs:\n"); 435 if (!set_protect_map (true)) { 436 warning ("Map is protected."); 437 return; 438 } 439 440 foreach (string key in single_kerning.keys) { 441 print (key); 442 print ("\t\t"); 443 print (@"$((!) single_kerning.get (key))\n"); 444 } 445 446 set_protect_map (false); 447 448 print ("\n"); 449 print ("Generated table:\n"); 450 all_pairs ((k) => { 451 k.print (); 452 }); 453 } 454 455 public void get_classes (KerningClassIterator kerningIterator) { 456 for (int i = 0; i < classes_first.size; i++) { 457 kerningIterator (classes_first.get (i).get_all_ranges (), 458 classes_last.get (i).get_all_ranges (), 459 classes_kerning.get (i).val); 460 } 461 } 462 463 public void get_single_position_pairs (KerningClassIterator kerningIterator) { 464 double k = 0; 465 466 if (!set_protect_map (true)) { 467 warning ("Map is protected."); 468 return; 469 } 470 471 foreach (string key in single_kerning.keys) { 472 var chars = key.split (" - "); 473 474 if (chars.length != 2) { 475 warning (@"Can not parse characters from key: $key"); 476 } else { 477 k = (!) single_kerning.get (key); 478 kerningIterator (chars[0], chars[1], k); 479 } 480 } 481 482 set_protect_map (false); 483 } 484 485 public void each_pair (KerningClassIterator iter) { 486 all_pairs ((kl) => { 487 Glyph g2; 488 KerningPair kerning_list = kl; 489 string g1 = kerning_list.character.get_name (); 490 Kerning kerning; 491 int i = 0; 492 493 return_if_fail (kerning_list.kerning.size > 0); 494 495 foreach (Kerning k in kerning_list.kerning) { 496 g2 = k.get_glyph (); 497 kerning = kerning_list.kerning.get (i); 498 iter (g1, g2.get_name (), kerning.val); 499 } 500 }); 501 } 502 503 public void all_pairs (KerningIterator kerningIterator) { 504 Gee.ArrayList<Glyph> left_glyphs = new Gee.ArrayList<Glyph> (); 505 Gee.ArrayList<KerningPair> pairs = new Gee.ArrayList<KerningPair> (); 506 double kerning; 507 string right; 508 string name; 509 Glyph? g; 510 511 // Create a list of first glyph in all pairs 512 foreach (GlyphRange r in classes_first) { 513 foreach (UniRange u in r.ranges) { 514 for (unichar c = u.start; c <= u.stop; c++) { 515 name = (!)c.to_string (); 516 g = font.get_glyph (name); 517 if (g != null && !left_glyphs.contains ((!) g)) { 518 left_glyphs.add ((!) g); 519 } 520 } 521 } 522 523 foreach (string n in r.unassigned) { 524 g = font.get_glyph (n); 525 if (g != null && !left_glyphs.contains ((!) g)) { 526 left_glyphs.add ((!) g); 527 } 528 } 529 } 530 531 foreach (string n in single_kerning_letters_left) { 532 g = font.get_glyph (n); 533 if (g != null && !left_glyphs.contains ((!) g)) { 534 left_glyphs.add ((!) g); 535 } 536 } 537 538 // add the right hand glyph and the kerning value 539 foreach (Glyph character in left_glyphs) { 540 KerningPair kl = new KerningPair (character); 541 542 foreach (GlyphRange r in classes_last) { 543 foreach (UniRange u in r.ranges) { 544 for (unichar c = u.start; c <= u.stop; c++) { 545 right = (!)c.to_string (); 546 547 if (font.has_glyph (right) && has_kerning (character.get_name (), right)) { 548 kerning = get_kerning (character.get_name (), right); 549 kl.add_unique ((!) font.get_glyph (right), kerning); 550 } 551 } 552 } 553 554 foreach (string n in r.unassigned) { 555 if (font.has_glyph (n) && has_kerning (character.get_name (), n)) { 556 kerning = get_kerning (character.get_name (), n); 557 kl.add_unique ((!) font.get_glyph (n), kerning); 558 } 559 } 560 } 561 562 // TODO: The get_kerning () function is still slow. Optimize it. 563 foreach (string right_glyph_name in single_kerning_letters_right) { 564 Glyph? gl = font.get_glyph (right_glyph_name); 565 if (gl != null && has_kerning (character.get_name (), right_glyph_name)) { 566 kerning = get_kerning (character.get_name (), right_glyph_name); 567 kl.add_unique ((!) gl , kerning); 568 } 569 } 570 571 if (kl.kerning.size > 0) { 572 pairs.add (kl); 573 } 574 575 if (kl.kerning.size == 0) { 576 warning (@"No kerning pairs for character: $((kl.character.get_name ()))"); 577 } 578 579 kl.sort (); 580 } 581 582 // obtain the kerning value 583 foreach (KerningPair p in pairs) { 584 kerningIterator (p); 585 } 586 } 587 588 private bool set_protect_map (bool p) { 589 if (unlikely (p && protect_map)) { 590 warning ("Map is already protected."); 591 return false; 592 } 593 594 protect_map = p; 595 return true; 596 } 597 598 public void delete_kerning_for_class (string left, string right) { 599 int i = 0; 600 int index = -1; 601 602 get_classes ((l, r, kerning) => { 603 if (left == l && r == right) { 604 index = i; 605 } 606 i++; 607 }); 608 609 if (unlikely (index < 0)) { 610 warning (@"Kerning class not found for $left to $right"); 611 return; 612 } 613 614 classes_first.remove_at (index); 615 classes_last.remove_at (index); 616 classes_kerning.remove_at (index); 617 } 618 619 public void delete_kerning_for_pair (string left, string right) { 620 foreach (string l in get_spacing_class (left)) { 621 foreach (string r in get_spacing_class (right)) { 622 delete_kerning_for_one_pair (l, r); 623 } 624 } 625 } 626 627 private void delete_kerning_for_one_pair (string left, string right) { 628 bool has_left, has_right; 629 string[] p; 630 631 single_kerning.unset (@"$left - $right"); 632 633 has_left = false; 634 has_right = false; 635 636 foreach (string k in single_kerning.keys) { 637 p = k.split (" - "); 638 return_if_fail (p.length == 2); 639 640 if (p[0] == left) { 641 has_left = true; 642 } 643 644 if (p[1] == right) { 645 has_right = true; 646 } 647 } 648 649 if (!has_left) { 650 single_kerning_letters_left.remove (left); 651 } 652 653 if (!has_right) { 654 single_kerning_letters_right.remove (left); 655 } 656 } 657 658 public void remove_all_pairs () { 659 if (protect_map) { 660 warning ("Map is protected."); 661 return; 662 } 663 664 classes_first.clear (); 665 classes_last.clear (); 666 classes_kerning.clear (); 667 single_kerning_letters_left.clear (); 668 single_kerning_letters_right.clear (); 669 670 GlyphCanvas.redraw (); 671 672 if (!is_null (MainWindow.get_toolbox ())) { // FIXME: reorganize 673 Toolbox.redraw_tool_box (); 674 } 675 676 single_kerning.clear (); 677 } 678 679 public uint get_number_of_pairs () { 680 return single_kerning.keys.size + classes_first.size; 681 } 682 } 683 684 } 685