The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

GlyphSequence.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/GlyphSequence.vala.
Fix ligature preview
1 /* 2 Copyright (C) 2013 2015 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 namespace BirdFont { 15 16 public class GlyphSequence : GLib.Object { 17 18 /** A list of all glyphs */ 19 public Gee.ArrayList<Glyph?> glyph; 20 21 /** A list of corresponding glyph ranges if applicable. */ 22 public Gee.ArrayList<GlyphRange?> ranges; 23 24 OtfTags otf_tags; 25 26 public GlyphSequence () { 27 glyph = new Gee.ArrayList<Glyph?> (); 28 ranges = new Gee.ArrayList<GlyphRange?> (); 29 otf_tags = new OtfTags (); 30 } 31 32 public void set_otf_tags (OtfTags tags) { 33 this.otf_tags = tags; 34 } 35 36 public int length () { 37 return glyph.size; 38 } 39 40 public void add (Glyph? g) { 41 glyph.add (g); 42 ranges.add (null); 43 } 44 45 public void append (GlyphSequence c) { 46 foreach (Glyph? g in c.glyph) { 47 glyph.add (g); 48 } 49 50 foreach (GlyphRange? r in c.ranges) { 51 ranges.add (r); 52 } 53 } 54 55 /** Perform glyph substitution. 56 * @param tags enable otf features 57 * @return a new sequence with ligatures 58 */ 59 public GlyphSequence process_ligatures (Font font) { 60 // FIXME add range to ligature 61 GlyphSequence ligature_sequence = new GlyphSequence (); 62 bool has_range = false; 63 Ligatures ligatures; 64 65 foreach (Glyph? g in glyph) { 66 ligature_sequence.glyph.add (g); 67 } 68 69 foreach (GlyphRange? r in ranges) { 70 ligature_sequence.ranges.add (r); 71 if (r != null) { 72 has_range = true; 73 } 74 } 75 76 // skip ligature substitution if this sequence contains ranges 77 if (has_range) { 78 return ligature_sequence; 79 } 80 81 ligatures = font.get_ligatures (); 82 83 foreach (ContextualLigature c in ligatures.contextual_ligatures) { 84 if (c.is_valid ()) { 85 ligature_sequence.replace_contextual (c.get_backtrack (), 86 c.get_input (), 87 c.get_lookahead (), 88 c.get_ligature_sequence ()); 89 } 90 } 91 92 ligatures.get_single_substitution_ligatures ((substitute, ligature) => { 93 ligature_sequence.replace (substitute, ligature); 94 }); 95 96 // salt and similar tags 97 foreach (string tag in otf_tags.elements) { 98 Gee.ArrayList<Alternate> alternates; 99 alternates = font.alternates.get_alt (tag); 100 101 foreach (Alternate a in alternates) { 102 GlyphSequence old = new GlyphSequence (); 103 Glyph? g = font.get_glyph_by_name (a.glyph_name); 104 105 if (g != null) { 106 old.add (g); 107 108 if (a.alternates.size > 0) { 109 // FIXME: pick one of several alternates 110 string alt_name = a.alternates.get (0); 111 Glyph? alt = font.get_glyph_by_name (alt_name); 112 113 if (alt != null) { 114 GlyphSequence replacement = new GlyphSequence (); 115 replacement.add (alt); 116 ligature_sequence.replace (old, replacement); 117 } else { 118 warning (@"Alternate does not exist: $(alt_name)"); 119 } 120 } 121 } else { 122 warning (@"Alternative for a missing glyph: $(a.glyph_name)"); 123 } 124 } 125 } 126 127 ligature_sequence.ranges.clear (); 128 for (int i = 0; i < ligature_sequence.glyph.size; i++) { 129 ligature_sequence.ranges.add (null); 130 } 131 132 return ligature_sequence; 133 } 134 135 void replace (GlyphSequence old, GlyphSequence replacement) { 136 int i = 0; 137 while (i < glyph.size) { 138 if (starts_with (old, i)) { 139 glyph = substitute (i, old.glyph.size, replacement); 140 i += replacement.length (); 141 } else { 142 i++; 143 } 144 } 145 } 146 147 void replace_contextual (GlyphSequence backtrack, GlyphSequence input, GlyphSequence lookahead, 148 GlyphSequence replacement) { 149 150 bool start, middle, end; 151 int i = 0; 152 int advance = 0; 153 154 while (i < glyph.size) { 155 start = starts_with (backtrack, i); 156 middle = starts_with (input, i + backtrack.length ()); 157 end = starts_with (lookahead, i + backtrack.length () + input.length ()); 158 159 if (start && middle && end) { 160 glyph = substitute (i + backtrack.length (), input.length (), replacement); 161 162 advance = backtrack.length () + replacement.length (); 163 i += advance + 1; 164 165 if (advance <= 0) { 166 warning ("No advancement."); 167 return; 168 } 169 } else { 170 i++; 171 } 172 } 173 } 174 175 bool starts_with (GlyphSequence old, uint index) { 176 Glyph? gl; 177 178 foreach (Glyph? g in old.glyph) { 179 if (index >= glyph.size) { 180 return false; 181 } 182 183 gl = glyph.get ((int) index); 184 185 if (g != gl) { 186 return false; 187 } 188 189 index++; 190 } 191 192 return true; 193 } 194 195 Gee.ArrayList<Glyph?> substitute (uint index, uint length, GlyphSequence substitute) { 196 Gee.ArrayList<Glyph?> new_list = new Gee.ArrayList<Glyph?> (); 197 int i = 0; 198 199 foreach (Glyph? g in glyph) { 200 if (i == index) { 201 foreach (Glyph? gn in substitute.glyph) { 202 new_list.add (gn); 203 } 204 } 205 206 if (!(i >= index && i < index + length)) { 207 new_list.add (g); 208 } 209 210 i++; 211 } 212 213 return new_list; 214 } 215 } 216 217 } 218