1/* 2 Copyright (C) 2012 2013 2014 2017 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*/ 14using Math; 15 16namespace BirdFont { 17 18public class GposTable : OtfTable { 19 20 GlyfTable glyf_table; 21 KernList pairs; 22 23 public GposTable () { 24 id = "GPOS"; 25 } 26 27 public override void parse (FontData dis) throws Error { 28 // Not implemented, freetype2 is used for loading fonts 29 } 30 31 public void process (GlyfTable glyf_table) throws GLib.Error { 32 FontData fd = new FontData (); 33 34 this.glyf_table = glyf_table; 35 this.pairs = new KernList (glyf_table); 36 37 printd ("Processing GPOS\n"); 38 39 fd.add_ulong (0x00010000); // table version 40 fd.add_ushort (10); // offset to script list 41 fd.add_ushort (30); // offset to feature list 42 fd.add_ushort (44); // offset to lookup list 43 44 // script list 45 fd.add_ushort (1); // number of items in script list 46 fd.add_tag ("DFLT"); // default script 47 fd.add_ushort (8); // offset to script table from script list 48 49 // script table 50 fd.add_ushort (4); // offset to default language system 51 fd.add_ushort (0); // number of languages 52 53 // LangSys table 54 fd.add_ushort (0); // reserved 55 fd.add_ushort (0); // required features (0xFFFF is none) 56 fd.add_ushort (1); // number of features 57 fd.add_ushort (0); // feature index 58 59 // feature table 60 fd.add_ushort (1); // number of features 61 62 fd.add_tag ("kern"); // feature tag 63 fd.add_ushort (8); // offset to feature 64 65 fd.add_ushort (0); // feature prameters (null) 66 fd.add_ushort (1); // number of lookups 67 fd.add_ushort (0); // lookup index 68 69 Gee.ArrayList<FontData> pair_set_data = new Gee.ArrayList<FontData>(); 70 pairs.fetch_all_pairs (); 71 72 KernSplitter kern_splitter = new KernSplitter (pairs); 73 74 while (true) { 75 if (offset < 0) { 76 break; 77 } 78 79 KernList pairs_subset = kern_splitter.get_subset (offset); 80 81 if (pairs_subset.get_length () == 0) { 82 break; 83 } 84 85 FontData pairs = get_pair_pos_format1 (pairs_subset); 86 pair_set_data.add (pairs); 87 88 offset += pairs_subset.get_length (); 89 } 90 91 // lookup table 92 fd.add_ushort (1); // number of lookups 93 fd.add_ushort (4); // offset to lookup 1 94 95 int16 extention_pos = (int16) (pair_set_data.size * 2); 96 97 for (int16 index = 0; index < pair_set_data.size; index++) { 98 extention_pos += index * 8; 99 } 100 101 int64 pair_set_data_length = 0; 102 int64 extension_length = 8; 103 104 foreach (FontData pair_set in pair_set_data) { 105 pair_set_data_length += pair_set.length_with_padding (); 106 extension_length += 8; 107 } 108 109 if (pair_set_data.size > 0) { 110 FontData last_pair_set = pair_set_data.get (pair_set_data.size - 1); 111 uint64 last_externsion_offset = extension_length + pair_set_data_length; 112 113 last_externsion_offset -= 8; 114 last_externsion_offset -= last_pair_set.length (); 115 116 if (last_externsion_offset > uint32.MAX) { 117 warning ("Too manu kerning pairs for the extension positioning table." 118 + @"last_externsion_offset: $last_externsion_offset > " 119 + @"$(uint32.MAX)"); 120 pair_set_data.clear (); 121 } 122 } 123 124 fd.add_ushort (9); // lookup type 125 fd.add_ushort (0); // lookup flags 126 fd.add_ushort ((uint16) pair_set_data.size); // number of subtables 127 128 if (pair_set_data.size > 0) { 129 for (int j = 0; j < pair_set_data.size; j++) { 130 uint16 lookup_size = (uint16) pair_set_data.size * 2; 131 uint16 offset_to_extension = (uint16) (6 + lookup_size + j * 8); 132 fd.add_ushort (offset_to_extension); // array of offsets to subtables 133 } 134 135 // extension positioning table 136 int k = pair_set_data.size - 1; 137 while (extension_length > 0 && k >= 0) { 138 FontData pair_set = pair_set_data.get (k); 139 pair_set_data_length -= pair_set.length (); 140 extension_length -= 8; 141 142 uint64 externsion_offset = extension_length + pair_set_data_length; 143 144 if (externsion_offset > uint32.MAX) { 145 warning ("Too manu kerning pairs for extension positioning table."); 146 } 147 148 fd.add_ushort (1); // format 149 fd.add_ushort (2); // lookup type 150 fd.add_ulong ((uint32) externsion_offset); // extension offset 151 k--; 152 } 153 154 // MarkFilteringSet 155 156 foreach (FontData pair_set in pair_set_data) { 157 fd.append (pair_set); 158 } 159 } 160 161 fd.pad (); 162 this.font_data = fd; 163 } 164 165 // PairPosFormat1 subtable 166 FontData get_pair_pos_format1 (KernList pairs_subset) throws GLib.Error { 167 FontData fd = new FontData (); 168 uint coverage_offset; 169 uint16 num_pairs; 170 int i; 171 uint pair_set_offset; 172 uint written; 173 uint written_pairs; 174 uint last_gid_left; 175 uint last_gid_right; 176 uint16 pair_set_count; 177 178 num_pairs = (uint16) pairs_subset.get_length (); 179 180 pair_set_count = (uint16) pairs_subset.get_length_left (); // FIXME: boundaries 181 182 coverage_offset = 10 + pairs_offset_length (pairs_subset) + pairs_set_length (pairs_subset); 183 184 if (coverage_offset > uint16.MAX) { 185 warning (@"Invalid coverage offset." + 186 @"Total: $(pairs.get_length ()), " + 187 @"subset: $(pairs_subset.get_length ())" + 188 @"coverage_offset: $(coverage_offset) > 65535"); 189 190 num_pairs = 0; 191 coverage_offset = 10; 192 } 193 194 fd.add_ushort (1); // position format 195 // offset to coverage table from beginning of kern pair table 196 fd.add_ushort ((uint16) coverage_offset); 197 fd.add_ushort (0x0004); // ValueFormat1 (0x0004 is x advance) 198 fd.add_ushort (0x0000); // ValueFormat2 (null, no value) 199 fd.add_ushort (pair_set_count); // n pairs 200 201 // pair offsets orderd by coverage index 202 pair_set_offset = 10 + pairs_offset_length (pairs_subset); 203 204 written = 0; 205 written_pairs = 0; 206 pairs_subset.all_pairs_format1 ((k) => { 207 try { 208 if (pair_set_offset > uint16.MAX) { 209 warning ("Invalid offset."); 210 return; 211 } 212 213 if (k.pairs.size == 0) { 214 warning ("No pairs."); 215 } 216 217 fd.add_ushort ((uint16) pair_set_offset); 218 pair_set_offset += 2; 219 pair_set_offset += 4 * k.pairs.size; 220 written += 2; 221 } catch (Error e) { 222 warning (e.message); 223 } 224 }, num_pairs); 225 226 if (unlikely (written != pairs_offset_length (pairs_subset))) { 227 warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length (pairs_subset)), real length $written"); 228 } 229 230 // pair table 231 i = 0; 232 written = 0; 233 last_gid_left = 0; 234 last_gid_right = 0; 235 pairs_subset.all_pairs_format1 ((pn) => { 236 try { 237 PairFormat1 p = pn; 238 uint pairset_length = p.pairs.size; 239 240 if (pairset_length > uint16.MAX) { 241 warning ("Too many pairs"); 242 pairset_length = uint16.MAX; 243 } 244 245 if (unlikely (p.left < last_gid_left)) { 246 warning (@"Kerning table is not sorted $(p.left) < $last_gid_left."); 247 } 248 249 last_gid_left = p.left; 250 251 fd.add_ushort ((uint16) pairset_length); 252 written += 2; 253 last_gid_right = 0; 254 written_pairs = 0; 255 foreach (Kern k in p.pairs) { 256 257 if (k.right == 0) { 258 warning (@"GID $(p.left) is kerned zero units to $(k.right)."); 259 } 260 261 // pair value record 262 fd.add_ushort (k.right); // gid of the second glyph 263 fd.add_short (k.kerning); // value of ValueFormat1, horizontal adjustment for advance of the first glyph 264 // value of ValueFormat2 (null) 265 266 if (unlikely (k.right < last_gid_right)) { 267 warning (@"Kerning table is not sorted $(k.right) < $last_gid_right)."); 268 } 269 270 last_gid_right = k.right; 271 272 written += 4; 273 written_pairs++; 274 } 275 276 if (unlikely (written_pairs != p.pairs.size)) { 277 warning (@"written_pairs != p.pairs.length () $(written_pairs) != $(pairs.get_length ()) pairset_length: $pairset_length"); 278 } 279 280 i++; 281 } catch (Error e) { 282 warning (e.message); 283 } 284 }, num_pairs); 285 286 if (unlikely (pairs_set_length (pairs_subset) != written)) { 287 warning (@"Bad pair set length: $(pairs_set_length (pairs_subset)), real length: $written"); 288 } 289 290 if (unlikely (fd.length () != coverage_offset)) { 291 warning (@"Bad coverage offset, coverage_offset: $coverage_offset, real length: $(fd.length ())"); 292 warning (@"pairs_offset_length: $(pairs_offset_length (pairs_subset)) pairs_set_length: $(pairs_set_length (pairs_subset))"); 293 } 294 295 // coverage 296 fd.add_ushort (1); // format 297 fd.add_ushort (pair_set_count); 298 299 written = 0; 300 pairs_subset.all_pairs_format1 ((p) => { 301 try { 302 fd.add_ushort (p.left); // gid 303 written += 2; 304 } catch (Error e) { 305 warning (e.message); 306 } 307 }, num_pairs); 308 309 if (unlikely (written != 2 * pair_set_count)) { 310 warning (@"written != 2 * pair_set_count: $written != 2 * $(pair_set_count)"); 311 } 312 313 return fd; 314 } 315 316 public static uint pairs_set_length (KernList kerning_list) { 317 uint len = 0; 318 319 kerning_list.all_pairs_format1 ((p) => { 320 len += 2 + 4 * p.pairs.size; 321 }); 322 323 return len; 324 } 325 326 public static uint pairs_offset_length (KernList kerning_list) { 327 return 2 * kerning_list.pairs.size; 328 } 329} 330 331} 332