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