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
Circle boundaries heads/master
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 string name; 104 Glyph? g; 105 106 name = a.glyph_name; 107 108 if (name == "space") { 109 name = " "; 110 } 111 112 g = font.get_glyph_by_name (name); 113 114 if (likely (g != null)) { 115 old.add (g); 116 117 if (a.alternates.size > 0) { 118 // FIXME: pick one of several alternates 119 string alt_name = a.alternates.get (0); 120 Glyph? alt = font.get_glyph_by_name (alt_name); 121 122 if (likely (alt != null)) { 123 GlyphSequence replacement = new GlyphSequence (); 124 replacement.add (alt); 125 ligature_sequence.replace (old, replacement); 126 } else { 127 warning (@"Alternate does not exist: $(alt_name)"); 128 } 129 } 130 } else { 131 warning (@"Alternative for a missing glyph: $(a.glyph_name)"); 132 } 133 } 134 } 135 136 ligature_sequence.ranges.clear (); 137 for (int i = 0; i < ligature_sequence.glyph.size; i++) { 138 ligature_sequence.ranges.add (null); 139 } 140 141 return ligature_sequence; 142 } 143 144 void replace (GlyphSequence old, GlyphSequence replacement) { 145 int i = 0; 146 while (i < glyph.size) { 147 if (starts_with (old, i)) { 148 glyph = substitute (i, old.glyph.size, replacement); 149 i += replacement.length (); 150 } else { 151 i++; 152 } 153 } 154 } 155 156 void replace_contextual (GlyphSequence backtrack, GlyphSequence input, GlyphSequence lookahead, 157 GlyphSequence replacement) { 158 159 bool start, middle, end; 160 int i = 0; 161 int advance = 0; 162 163 while (i < glyph.size) { 164 start = starts_with (backtrack, i); 165 middle = starts_with (input, i + backtrack.length ()); 166 end = starts_with (lookahead, i + backtrack.length () + input.length ()); 167 168 if (start && middle && end) { 169 glyph = substitute (i + backtrack.length (), input.length (), replacement); 170 171 advance = backtrack.length () + replacement.length (); 172 i += advance + 1; 173 174 if (advance <= 0) { 175 warning ("No advancement."); 176 return; 177 } 178 } else { 179 i++; 180 } 181 } 182 } 183 184 bool starts_with (GlyphSequence old, uint index) { 185 Glyph? gl; 186 187 foreach (Glyph? g in old.glyph) { 188 if (index >= glyph.size) { 189 return false; 190 } 191 192 gl = glyph.get ((int) index); 193 194 if (g != gl) { 195 return false; 196 } 197 198 index++; 199 } 200 201 return true; 202 } 203 204 Gee.ArrayList<Glyph?> substitute (uint index, uint length, GlyphSequence substitute) { 205 Gee.ArrayList<Glyph?> new_list = new Gee.ArrayList<Glyph?> (); 206 int i = 0; 207 208 foreach (Glyph? g in glyph) { 209 if (i == index) { 210 foreach (Glyph? gn in substitute.glyph) { 211 new_list.add (gn); 212 } 213 } 214 215 if (!(i >= index && i < index + length)) { 216 new_list.add (g); 217 } 218 219 i++; 220 } 221 222 return new_list; 223 } 224 } 225 226 } 227