The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

CmapSubtableFormat4.vala in libbirdfont/OpenFontFormat

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/OpenFontFormat/CmapSubtableFormat4.vala.
Language fix heads/multimaster
1 /* 2 Copyright (C) 2012, 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 namespace BirdFont { 16 17 /** Format 4 cmap subtable */ 18 public class CmapSubtableFormat4 : GLib.Object { 19 public uint32 offset; 20 uint16 format = 0; 21 HashTable <uint64?, unichar> table = new HashTable <uint64?, unichar> (int64_hash, int_equal); 22 23 uint16 length = 0; 24 25 public CmapSubtableFormat4 () { 26 } 27 28 public uint get_length () { 29 return table.size (); 30 } 31 32 public unichar get_char (uint32 index) { 33 int64? c = table.lookup (index); 34 35 if (c == 0 && index == 0) { 36 return 0; 37 } 38 39 if (c == 0) { 40 while (table.lookup (--index) == 0) { 41 if (index == 0) { 42 return 0; 43 } 44 } 45 46 warning (@"There is no character for glyph number $index in cmap table. table.size: $(table.size ()))"); 47 return 0; 48 } 49 50 return (unichar) c; 51 } 52 53 public void parse (FontData dis) throws GLib.Error { 54 dis.seek (offset); 55 56 format = dis.read_ushort (); 57 58 switch (format) { 59 case 4: 60 parse_format4 (dis); 61 break; 62 63 default: 64 stderr.printf (@"CmapSubtable is in format $format, it is not supportet (yet).\n"); 65 break; 66 } 67 } 68 69 public void parse_format4 (FontData dis) throws GLib.Error { 70 uint16 lang; 71 uint16 seg_count_x2; 72 uint16 seg_count; 73 uint16 search_range; 74 uint16 entry_selector; 75 uint16 range_shift; 76 77 uint16* end_char = null; 78 uint16* start_char = null; 79 int16* id_delta = null; 80 uint16* id_range_offset = null; 81 uint16* glyph_id_array = null; 82 83 uint32 gid_len; 84 85 length = dis.read_ushort (); 86 lang = dis.read_ushort (); 87 seg_count_x2 = dis.read_ushort (); 88 search_range = dis.read_ushort (); 89 entry_selector = dis.read_ushort (); 90 range_shift = dis.read_ushort (); 91 92 return_if_fail (seg_count_x2 % 2 == 0); 93 94 seg_count = seg_count_x2 / 2; 95 96 end_char = new uint16[seg_count]; 97 for (int i = 0; i < seg_count; i++) { 98 end_char[i] = dis.read_ushort (); 99 } 100 101 if (end_char[seg_count - 1] != 0xFFFF) { 102 warning ("end_char is $(end_char[seg_count - 1]), expecting 0xFFFF."); 103 } 104 105 dis.read_ushort (); // Reserved 106 107 start_char = new uint16[seg_count]; 108 for (int i = 0; i < seg_count; i++) { 109 start_char[i] = dis.read_ushort (); 110 } 111 112 id_delta = new int16[seg_count]; 113 for (int i = 0; i < seg_count; i++) { 114 id_delta[i] = dis.read_short (); 115 } 116 117 id_range_offset = new uint16[seg_count]; 118 for (int i = 0; i < seg_count; i++) { 119 id_range_offset[i] = dis.read_ushort (); 120 } 121 122 if (length == 0) { 123 warning ("cmap subtable version 4 has length 0."); 124 return; 125 } 126 127 gid_len = (length - 16 - 8 * seg_count) / 2; 128 glyph_id_array = new uint16[gid_len]; 129 for (int i = 0; i < gid_len; i++) { 130 glyph_id_array[i] = dis.read_ushort (); 131 } 132 133 // map all values in a hashtable 134 int index = 0; 135 unichar character = 0; 136 uint32 id; 137 for (uint16 i = 0; i < seg_count && start_char[i] != 0xFFFF; i++) { 138 139 // print_range (start_char[i], end_char[i], id_delta[i], id_range_offset[i]); 140 141 uint16 j = 0; 142 do { 143 character = start_char[i] + j; 144 index = start_char[i] + id_delta[i] + j; 145 146 if (id_range_offset[i] == 0) { 147 table.insert (index, character); 148 } else { 149 // the indexing trick: 150 id = id_range_offset[i] / 2 + j + i - seg_count; 151 152 if (!(0 <= id < gid_len)) { 153 warning (@"(0 <= id < gid_len) (0 <= $id < $gid_len)"); 154 break; 155 } 156 157 index = glyph_id_array [id] + id_delta[i]; 158 159 StringBuilder s = new StringBuilder (); 160 s.append_unichar (character); 161 162 table.insert (index, character); 163 } 164 165 j++; 166 } while (character != end_char[i]); 167 168 } 169 170 if (end_char != null) delete end_char; 171 if (start_char != null) delete start_char; 172 if (id_delta != null) delete id_delta; 173 if (id_range_offset != null) delete id_range_offset; 174 if (glyph_id_array != null) delete glyph_id_array; 175 } 176 177 public FontData get_cmap_data (GlyfTable glyf_table) throws GLib.Error { 178 FontData fd = new FontData (); 179 GlyphRange glyph_range = new GlyphRange (); 180 Gee.ArrayList<UniRange> ranges; 181 182 uint16 seg_count_2; 183 uint16 seg_count; 184 uint16 search_range; 185 uint16 entry_selector; 186 uint16 range_shift; 187 188 uint16 gid_length = 0; 189 190 uint32 index; 191 uint32 first_assigned; 192 193 first_assigned = 1; 194 195 foreach (GlyphCollection g in glyf_table.glyphs) { 196 if (!g.is_unassigned () && g.get_unicode_character () < 0xFFFF) { 197 glyph_range.add_single (g.get_unicode_character ()); 198 } 199 } 200 201 ranges = glyph_range.get_ranges (); 202 seg_count = (uint16) ranges.size + 1; 203 seg_count_2 = seg_count * 2; 204 search_range = 2 * largest_pow2 (seg_count); 205 entry_selector = largest_pow2_exponent (seg_count); 206 range_shift = seg_count_2 - search_range; 207 208 // format 209 fd.add_ushort (4); 210 211 // length of subtable 212 fd.add_ushort (16 + 8 * seg_count + gid_length); 213 214 // language 215 fd.add_ushort (0); 216 217 fd.add_ushort (seg_count_2); 218 fd.add_ushort (search_range); 219 fd.add_ushort (entry_selector); 220 fd.add_ushort (range_shift); 221 222 // end codes 223 index = first_assigned; 224 foreach (UniRange u in ranges) { 225 if (u.stop >= 0xFFFF) { 226 warning ("Not implemented yet."); 227 } else { 228 fd.add_ushort ((uint16) u.stop); 229 index += u.length (); 230 } 231 } 232 fd.add_ushort (0xFFFF); 233 234 fd.add_ushort (0); // Reserved 235 236 // start codes 237 index = first_assigned; // since first glyph are notdef, null and nonmarkingreturn 238 foreach (UniRange u in ranges) { 239 if (u.start >= 0xFFFF) { 240 warning ("Not implemented yet."); 241 } else { 242 fd.add_ushort ((uint16) u.start); 243 index += u.length (); 244 } 245 } 246 fd.add_ushort (0xFFFF); 247 248 // delta 249 index = first_assigned; 250 foreach (UniRange u in ranges) { 251 if ((u.start - index) > 0xFFFF && u.start > index) { 252 warning ("Need range offset."); 253 } else { 254 fd.add_ushort ((uint16) (index - u.start)); 255 index += u.length (); 256 } 257 } 258 fd.add_ushort (1); 259 260 // range offset 261 foreach (UniRange u in ranges) { 262 if (u.stop <= 0xFFFF) { 263 fd.add_ushort (0); 264 } else { 265 warning ("Not implemented yet."); 266 } 267 } 268 fd.add_ushort (0); 269 270 // FIXME: implement the rest of type 4 (mind gid_length in length field) 271 272 return fd; 273 } 274 } 275 276 } 277