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.
Revert "Error handeling in merge code"
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 ligature_sequence.replace_contextual (c.get_backtrack (), 85 c.get_input (), c.get_lookahead (), c.get_ligature_sequence ()); 86 } 87 88 ligatures.get_single_substitution_ligatures ((substitute, ligature) => { 89 ligature_sequence.replace (substitute, ligature); 90 }); 91 92 // salt and similar tags 93 foreach (string tag in otf_tags.elements) { 94 Gee.ArrayList<Alternate> alternates; 95 alternates = font.alternates.get_alt (tag); 96 97 foreach (Alternate a in alternates) { 98 GlyphSequence old = new GlyphSequence (); 99 Glyph? g = font.get_glyph_by_name (a.glyph_name); 100 101 if (g != null) { 102 old.add (g); 103 104 if (a.alternates.size > 0) { 105 // FIXME: pick one of several alternates 106 string alt_name = a.alternates.get (0); 107 Glyph? alt = font.get_glyph_by_name (alt_name); 108 109 if (alt != null) { 110 GlyphSequence replacement = new GlyphSequence (); 111 replacement.add (alt); 112 ligature_sequence.replace (old, replacement); 113 } else { 114 warning (@"Alternate does not exist: $(alt_name)"); 115 } 116 } 117 } else { 118 warning (@"Alternative for a missing glyph: $(a.glyph_name)"); 119 } 120 } 121 } 122 123 ligature_sequence.ranges.clear (); 124 for (int i = 0; i < ligature_sequence.glyph.size; i++) { 125 ligature_sequence.ranges.add (null); 126 } 127 128 return ligature_sequence; 129 } 130 131 void replace (GlyphSequence old, GlyphSequence replacement) { 132 int i = 0; 133 while (i < glyph.size) { 134 if (starts_with (old, i)) { 135 glyph = substitute (i, old.glyph.size, replacement); 136 i += replacement.length (); 137 } else { 138 i++; 139 } 140 } 141 } 142 143 void replace_contextual (GlyphSequence backtrack, GlyphSequence input, GlyphSequence lookahead, 144 GlyphSequence replacement) { 145 146 bool start, middle, end; 147 int i = 0; 148 int advance = 0; 149 150 while (i < glyph.size) { 151 start = starts_with (backtrack, i); 152 middle = starts_with (input, i + backtrack.length ()); 153 end = starts_with (lookahead, i + backtrack.length () + input.length ()); 154 155 if (start && middle && end) { 156 glyph = substitute (i + backtrack.length (), input.length (), replacement); 157 158 advance = backtrack.length () + replacement.length (); 159 i += advance; 160 161 if (advance <= 0) { 162 warning ("No advancement."); 163 return; 164 } 165 } else { 166 i++; 167 } 168 } 169 } 170 171 bool starts_with (GlyphSequence old, uint index) { 172 Glyph? gl; 173 174 foreach (Glyph? g in old.glyph) { 175 if (index >= glyph.size) { 176 return false; 177 } 178 179 gl = glyph.get ((int) index); 180 181 if (g != gl) { 182 return false; 183 } 184 185 index++; 186 } 187 188 return true; 189 } 190 191 Gee.ArrayList<Glyph?> substitute (uint index, uint length, GlyphSequence substitute) { 192 Gee.ArrayList<Glyph?> new_list = new Gee.ArrayList<Glyph?> (); 193 int i = 0; 194 195 foreach (Glyph? g in glyph) { 196 if (i == index) { 197 foreach (Glyph? gn in substitute.glyph) { 198 new_list.add (gn); 199 } 200 } 201 202 if (!(i >= index && i < index + length)) { 203 new_list.add (g); 204 } 205 206 i++; 207 } 208 209 return new_list; 210 } 211 } 212 213 } 214