]> git.sesse.net Git - casparcg/blob - core/producer/text/utils/texture_font.cpp
* psd: added support for scaling and tracking in text-producer
[casparcg] / core / producer / text / utils / texture_font.cpp
1 #include "..\..\..\StdAfx.h"
2
3 #include "texture_atlas.h"
4 #include "texture_font.h"
5
6 #include <map>
7 #include <ft2build.h>
8 #include FT_FREETYPE_H
9 #include FT_GLYPH_H
10
11 namespace caspar { namespace core { namespace text {
12
13 struct freetype_exception : virtual caspar_exception
14 {
15         freetype_exception() {}
16         explicit freetype_exception(const char* msg) : caspar_exception(msg) {}
17 };
18
19 struct unicode_range
20 {
21         unicode_range() : first(0), last(0) {}
22         unicode_range(int f, int l) : first(f), last(l) {}
23
24         int first;
25         int last;
26 };
27
28 unicode_range get_range(unicode_block block);
29
30
31 struct texture_font::impl
32 {
33 private:
34         struct glyph_info
35         {
36                 glyph_info(int w, int h, float l, float t, float r, float b) : width(w), height(h), left(l), top(t), right(r), bottom(b)
37                 {}
38
39                 float left, top, right, bottom;
40                 int width, height;
41         };
42
43         FT_Library              lib_;
44         FT_Face                 face_;
45         texture_atlas   atlas_;
46         float                   size_;
47         float                   tracking_;
48         bool                    normalize_;
49         std::map<int, glyph_info> glyphs_;
50
51 public:
52         impl::impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : lib_(nullptr), face_(nullptr), atlas_(atlas), size_(info.size), tracking_(info.size*info.tracking/1000.0f), normalize_(normalize_coordinates)
53         {
54                 try
55                 {
56                         FT_Error err;
57                         err = FT_Init_FreeType(&lib_);
58                         if(err) throw freetype_exception("Failed to initialize freetype");
59
60                         err = FT_New_Face(lib_, u8(info.font_file).c_str(), 0, &face_);
61                         if(err) throw freetype_exception("Failed to load font");
62
63                         err = FT_Set_Char_Size(face_, static_cast<FT_F26Dot6>(size_*64), 0, 72, 72);
64                         if(err) throw freetype_exception("Failed to set font size");
65                 }
66                 catch(std::exception& ex)
67                 {
68                         if(face_ != nullptr)
69                                 FT_Done_Face(face_);
70                         if(lib_ != nullptr)
71                                 FT_Done_FreeType(lib_);
72
73                         throw ex;
74                 }
75         }
76
77         ~impl()
78         {
79                 if(face_ != nullptr)
80                         FT_Done_Face(face_);
81                 if(lib_ != nullptr)
82                         FT_Done_FreeType(lib_);
83         }
84
85         int count_glyphs_in_range(unicode_block block)
86         { 
87                 unicode_range range = get_range(block);
88
89                 //TODO: extract info from freetype
90
91                 //very pesimistic, assumes a glyph for each charcode
92                 return range.last - range.first;
93         }
94
95         void impl::load_glyphs(unicode_block block, const color<float>& col)
96         {
97                 FT_Error err;
98                 int flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
99                 unicode_range range = get_range(block);
100
101                 for(int i = range.first; i <= range.last; ++i)
102                 {
103                         FT_UInt glyph_index = FT_Get_Char_Index(face_, i);
104                         if(!glyph_index)        //ignore codes that doesn't have a glyph for now. Might want to map these to a special glyph later.
105                                 continue;
106                         
107                         err = FT_Load_Glyph(face_, glyph_index, flags);
108                         if(err) continue;       //igonore glyphs that fail to load
109
110                         const FT_Bitmap& bitmap = face_->glyph->bitmap; //shorthand notation
111
112                         auto region = atlas_.get_region(bitmap.width+1, bitmap.rows+1);
113                         if(region.x < 0)
114                         {
115                                 //the glyph doesn't fit in the texture-atlas. ignore it for now.
116                                 //we might want to restart with a bigger atlas in the future
117                                 continue;
118                         }
119
120                         atlas_.set_region(region.x, region.y, bitmap.width, bitmap.rows, bitmap.buffer, bitmap.pitch, col);
121                         glyphs_.insert(std::pair<int, glyph_info>(i, glyph_info(bitmap.width, bitmap.rows, 
122                                                                                 region.x / (float)atlas_.width(), 
123                                                                                 region.y / (float)atlas_.height(), 
124                                                                                 (region.x + bitmap.width) / (float)atlas_.width(), 
125                                                                                 (region.y + bitmap.rows) / (float)atlas_.height())));
126                 }
127         }
128
129         std::vector<float> create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics)
130         {
131                 //TODO: detect glyphs that aren't in the atlas and load them (and maybe that entire unicode_block on the fly
132
133                 std::vector<float> result(16*str.length(), 0);
134
135                 bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
136                 int index = 0;
137                 FT_UInt previous = 0;
138                 float pos_x = (float)x;
139                 float pos_y = (float)y;
140
141                 int maxBearingY = 0;
142                 int maxProtrudeUnderY = 0;
143                 int maxHeight = 0;
144
145                 auto end = str.end();
146                 for(auto it = str.begin(); it != end; ++it, ++index)
147                 {
148                         auto glyph_it = glyphs_.find(*it);
149                         if(glyph_it != glyphs_.end())
150                         {       
151                                 const glyph_info& coords = glyph_it->second;
152
153                                 FT_UInt glyph_index = FT_Get_Char_Index(face_, (*it));
154
155                                 if(use_kerning && previous && glyph_index)
156                                 {
157                                         FT_Vector delta;
158                                         FT_Get_Kerning(face_, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
159
160                                         pos_x += delta.x / 64.0f;
161                                 }
162
163                                 FT_Load_Glyph(face_, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
164
165                                 float left = (pos_x + face_->glyph->metrics.horiBearingX/64.0f) / parent_width ;
166                                 float right = ((pos_x + face_->glyph->metrics.horiBearingX/64.0f) + coords.width) / parent_width;
167
168                                 float top = (pos_y - face_->glyph->metrics.horiBearingY/64.0f) / parent_height;
169                                 float bottom = ((pos_y - face_->glyph->metrics.horiBearingY/64.0f) + coords.height) / parent_height;
170
171                                 //vertex 1 top left
172                                 result[index*16 + 0] = left;                    //vertex.x
173                                 result[index*16 + 1] = top;                             //vertex.y
174                                 result[index*16 + 2] = coords.left;             //texcoord.r
175                                 result[index*16 + 3] = coords.top;              //texcoord.s
176
177                                 //vertex 2 top right
178                                 result[index*16 + 4] = right;                   //vertex.x
179                                 result[index*16 + 5] = top;                             //vertex.y
180                                 result[index*16 + 6] = coords.right;    //texcoord.r
181                                 result[index*16 + 7] = coords.top;              //texcoord.s
182
183                                 //vertex 3 bottom right
184                                 result[index*16 + 8] = right;                   //vertex.x
185                                 result[index*16 + 9] = bottom;                  //vertex.y
186                                 result[index*16 + 10] = coords.right;   //texcoord.r
187                                 result[index*16 + 11] = coords.bottom;  //texcoord.s
188
189                                 //vertex 4 bottom left
190                                 result[index*16 + 12] = left;                   //vertex.x
191                                 result[index*16 + 13] = bottom;                 //vertex.y
192                                 result[index*16 + 14] = coords.left;    //texcoord.r
193                                 result[index*16 + 15] = coords.bottom;  //texcoord.s
194
195                                 int bearingY = face_->glyph->metrics.horiBearingY >> 6;
196
197                                 if(bearingY > maxBearingY)
198                                         maxBearingY = bearingY;
199
200                                 int protrudeUnderY = coords.height - bearingY;
201
202                                 if (protrudeUnderY > maxProtrudeUnderY)
203                                         maxProtrudeUnderY = protrudeUnderY;
204
205                                 if (maxBearingY + maxProtrudeUnderY > maxHeight)
206                                         maxHeight = maxBearingY + maxProtrudeUnderY;
207
208                                 pos_x += face_->glyph->advance.x / 64.0f;
209                                 pos_x += tracking_;
210                                 previous = glyph_index;
211                         }
212                         else
213                         {
214                                 //TODO: maybe we should try to load the glyph on the fly if it is missing.
215                         }
216                 }
217
218                 if(normalize_)
219                 {
220                         float ratio_x = parent_width/(pos_x - x);
221                         float ratio_y = parent_height/(float)(maxHeight);
222                         for(index = 0; index < result.size(); index += 4)
223                         {
224                                 result[index + 0] *= ratio_x;
225                                 result[index + 1] *= ratio_y;
226                         }
227                 }
228
229                 if(metrics != nullptr)
230                 {
231                         metrics->width = (int)(pos_x - x + 0.5f);
232                         metrics->bearingY = maxBearingY;
233                         metrics->height = maxHeight;
234                         metrics->protrudeUnderY = maxProtrudeUnderY;
235                 }
236                 return result;
237         }
238
239         string_metrics measure_string(const std::wstring& str)
240         {
241                 string_metrics result;
242                 
243                 bool use_kerning = (face_->face_flags & FT_FACE_FLAG_KERNING) == FT_FACE_FLAG_KERNING;
244                 int index = 0;
245                 FT_UInt previous = 0;
246                 float pos_x = 0;
247 //              float pos_y = 0;
248
249                 auto end = str.end();
250                 for(auto it = str.begin(); it != end; ++it, ++index)
251                 {
252                         auto glyph_it = glyphs_.find(*it);
253                         if(glyph_it != glyphs_.end())
254                         {       
255                                 const glyph_info& coords = glyph_it->second;
256
257                                 FT_UInt glyph_index = FT_Get_Char_Index(face_, (*it));
258
259                                 if(use_kerning && previous && glyph_index)
260                                 {
261                                         FT_Vector delta;
262                                         FT_Get_Kerning(face_, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
263
264                                         pos_x += delta.x / 64.0f;
265                                 }
266
267                                 FT_Load_Glyph(face_, glyph_index, FT_LOAD_NO_BITMAP | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL);
268
269                                 int bearingY = face_->glyph->metrics.horiBearingY >> 6;
270                                 if(bearingY > result.bearingY)
271                                         result.bearingY = bearingY;
272
273                                 int protrudeUnderY = coords.height - bearingY;
274
275                                 if (protrudeUnderY > result.protrudeUnderY)
276                                         result.protrudeUnderY = protrudeUnderY;
277
278                                 if (result.bearingY + result.protrudeUnderY > result.height)
279                                          result.height = result.bearingY + result.protrudeUnderY;
280
281                                 pos_x += face_->glyph->advance.x / 64.0f;
282                                 previous = glyph_index;
283                         }
284                 }
285
286                 result.width = (int)(pos_x+.5f);
287                 return result;
288         }
289 }; 
290
291 texture_font::texture_font(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : impl_(new impl(atlas, info, normalize_coordinates)) {}
292 void texture_font::load_glyphs(unicode_block range, const color<float>& col) { impl_->load_glyphs(range, col); }
293 std::vector<float> texture_font::create_vertex_stream(const std::wstring& str, int x, int y, int parent_width, int parent_height, string_metrics* metrics) { return impl_->create_vertex_stream(str, x, y, parent_width, parent_height, metrics); }
294 string_metrics texture_font::measure_string(const std::wstring& str) { return impl_->measure_string(str); }
295
296 unicode_range get_range(unicode_block block)
297 {
298         switch(block)
299         {
300                 case Basic_Latin: return unicode_range(0x0000, 0x007F); 
301                 case Latin_1_Supplement: return unicode_range(0x0080, 0x00FF); 
302                 case Latin_Extended_A: return           unicode_range(0x0100, 0x017F); 
303                 case Latin_Extended_B: return           unicode_range(0x0180, 0x024F); 
304                 case IPA_Extensions: return             unicode_range(0x0250, 0x02AF); 
305                 case Spacing_Modifier_Letters: return           unicode_range(0x02B0, 0x02FF); 
306                 case Combining_Diacritical_Marks: return                unicode_range(0x0300, 0x036F); 
307                 case Greek_and_Coptic: return           unicode_range(0x0370, 0x03FF); 
308                 case Cyrillic: return           unicode_range(0x0400, 0x04FF); 
309                 case Cyrillic_Supplement: return                unicode_range(0x0500, 0x052F); 
310                 case Armenian: return           unicode_range(0x0530, 0x058F); 
311                 case Hebrew: return             unicode_range(0x0590, 0x05FF); 
312                 case Arabic: return             unicode_range(0x0600, 0x06FF); 
313                 case Syriac: return             unicode_range(0x0700, 0x074F); 
314                 case Arabic_Supplement: return          unicode_range(0x0750, 0x077F); 
315                 case Thaana: return             unicode_range(0x0780, 0x07BF); 
316                 case NKo: return                unicode_range(0x07C0, 0x07FF); 
317                 case Samaritan: return          unicode_range(0x0800, 0x083F); 
318                 case Mandaic: return            unicode_range(0x0840, 0x085F); 
319                 case Arabic_Extended_A: return          unicode_range(0x08A0, 0x08FF); 
320                 case Devanagari: return         unicode_range(0x0900, 0x097F); 
321                 case Bengali: return            unicode_range(0x0980, 0x09FF); 
322                 case Gurmukhi: return           unicode_range(0x0A00, 0x0A7F); 
323                 case Gujarati: return           unicode_range(0x0A80, 0x0AFF); 
324                 case Oriya: return              unicode_range(0x0B00, 0x0B7F); 
325                 case Tamil: return              unicode_range(0x0B80, 0x0BFF); 
326                 case Telugu: return             unicode_range(0x0C00, 0x0C7F); 
327                 case Kannada: return            unicode_range(0x0C80, 0x0CFF); 
328                 case Malayalam: return          unicode_range(0x0D00, 0x0D7F); 
329                 case Sinhala: return            unicode_range(0x0D80, 0x0DFF); 
330                 case Thai: return               unicode_range(0x0E00, 0x0E7F); 
331                 case Lao: return                unicode_range(0x0E80, 0x0EFF); 
332                 case Tibetan: return            unicode_range(0x0F00, 0x0FFF); 
333                 case Myanmar: return            unicode_range(0x1000, 0x109F); 
334                 case Georgian: return           unicode_range(0x10A0, 0x10FF); 
335                 case Hangul_Jamo: return                unicode_range(0x1100, 0x11FF); 
336                 case Ethiopic: return           unicode_range(0x1200, 0x137F); 
337                 case Ethiopic_Supplement: return                unicode_range(0x1380, 0x139F); 
338                 case Cherokee: return           unicode_range(0x13A0, 0x13FF); 
339                 case Unified_Canadian_Aboriginal_Syllabics: return              unicode_range(0x1400, 0x167F); 
340                 case Ogham: return              unicode_range(0x1680, 0x169F); 
341                 case Runic: return              unicode_range(0x16A0, 0x16FF); 
342                 case Tagalog: return            unicode_range(0x1700, 0x171F); 
343                 case Hanunoo: return            unicode_range(0x1720, 0x173F); 
344                 case Buhid: return              unicode_range(0x1740, 0x175F); 
345                 case Tagbanwa: return           unicode_range(0x1760, 0x177F); 
346                 case Khmer: return              unicode_range(0x1780, 0x17FF); 
347                 case Mongolian: return          unicode_range(0x1800, 0x18AF); 
348                 case Unified_Canadian_Aboriginal_Syllabics_Extended: return             unicode_range(0x18B0, 0x18FF); 
349                 case Limbu: return              unicode_range(0x1900, 0x194F); 
350                 case Tai_Le: return             unicode_range(0x1950, 0x197F); 
351                 case New_Tai_Lue: return                unicode_range(0x1980, 0x19DF); 
352                 case Khmer_Symbols: return              unicode_range(0x19E0, 0x19FF); 
353                 case Buginese: return           unicode_range(0x1A00, 0x1A1F); 
354                 case Tai_Tham: return           unicode_range(0x1A20, 0x1AAF); 
355                 case Balinese: return           unicode_range(0x1B00, 0x1B7F); 
356                 case Sundanese: return          unicode_range(0x1B80, 0x1BBF); 
357                 case Batak: return              unicode_range(0x1BC0, 0x1BFF); 
358                 case Lepcha: return             unicode_range(0x1C00, 0x1C4F); 
359                 case Ol_Chiki: return           unicode_range(0x1C50, 0x1C7F); 
360                 case Sundanese_Supplement: return               unicode_range(0x1CC0, 0x1CCF); 
361                 case Vedic_Extensions: return           unicode_range(0x1CD0, 0x1CFF); 
362                 case Phonetic_Extensions: return                unicode_range(0x1D00, 0x1D7F); 
363                 case Phonetic_Extensions_Supplement: return             unicode_range(0x1D80, 0x1DBF); 
364                 case Combining_Diacritical_Marks_Supplement: return             unicode_range(0x1DC0, 0x1DFF); 
365                 case Latin_Extended_Additional: return          unicode_range(0x1E00, 0x1EFF); 
366                 case Greek_Extended: return             unicode_range(0x1F00, 0x1FFF); 
367                 case General_Punctuation: return                unicode_range(0x2000, 0x206F); 
368                 case Superscripts_and_Subscripts: return                unicode_range(0x2070, 0x209F); 
369                 case Currency_Symbols: return           unicode_range(0x20A0, 0x20CF); 
370                 case Combining_Diacritical_Marks_for_Symbols: return            unicode_range(0x20D0, 0x20FF); 
371                 case Letterlike_Symbols: return         unicode_range(0x2100, 0x214F); 
372                 case Number_Forms: return               unicode_range(0x2150, 0x218F); 
373                 case Arrows: return             unicode_range(0x2190, 0x21FF); 
374                 case Mathematical_Operators: return             unicode_range(0x2200, 0x22FF); 
375                 case Miscellaneous_Technical: return            unicode_range(0x2300, 0x23FF); 
376                 case Control_Pictures: return           unicode_range(0x2400, 0x243F); 
377                 case Optical_Character_Recognition: return              unicode_range(0x2440, 0x245F); 
378                 case Enclosed_Alphanumerics: return             unicode_range(0x2460, 0x24FF); 
379                 case Box_Drawing: return                unicode_range(0x2500, 0x257F); 
380                 case Block_Elements: return             unicode_range(0x2580, 0x259F); 
381                 case Geometric_Shapes: return           unicode_range(0x25A0, 0x25FF); 
382                 case Miscellaneous_Symbols: return              unicode_range(0x2600, 0x26FF); 
383                 case Dingbats: return           unicode_range(0x2700, 0x27BF); 
384                 case Miscellaneous_Mathematical_Symbols_A: return               unicode_range(0x27C0, 0x27EF); 
385                 case Supplemental_Arrows_A: return              unicode_range(0x27F0, 0x27FF); 
386                 case Braille_Patterns: return           unicode_range(0x2800, 0x28FF); 
387                 case Supplemental_Arrows_B: return              unicode_range(0x2900, 0x297F); 
388                 case Miscellaneous_Mathematical_Symbols_B: return               unicode_range(0x2980, 0x29FF); 
389                 case Supplemental_Mathematical_Operators: return                unicode_range(0x2A00, 0x2AFF); 
390                 case Miscellaneous_Symbols_and_Arrows: return           unicode_range(0x2B00, 0x2BFF); 
391                 case Glagolitic: return         unicode_range(0x2C00, 0x2C5F); 
392                 case Latin_Extended_C: return           unicode_range(0x2C60, 0x2C7F); 
393                 case Coptic: return             unicode_range(0x2C80, 0x2CFF); 
394                 case Georgian_Supplement: return                unicode_range(0x2D00, 0x2D2F); 
395                 case Tifinagh: return           unicode_range(0x2D30, 0x2D7F); 
396                 case Ethiopic_Extended: return          unicode_range(0x2D80, 0x2DDF); 
397                 case Cyrillic_Extended_A: return                unicode_range(0x2DE0, 0x2DFF); 
398                 case Supplemental_Punctuation: return           unicode_range(0x2E00, 0x2E7F); 
399                 case CJK_Radicals_Supplement: return            unicode_range(0x2E80, 0x2EFF); 
400                 case Kangxi_Radicals: return            unicode_range(0x2F00, 0x2FDF); 
401                 case Ideographic_Description_Characters: return         unicode_range(0x2FF0, 0x2FFF); 
402                 case CJK_Symbols_and_Punctuation: return                unicode_range(0x3000, 0x303F); 
403                 case Hiragana: return           unicode_range(0x3040, 0x309F); 
404                 case Katakana: return           unicode_range(0x30A0, 0x30FF); 
405                 case Bopomofo: return           unicode_range(0x3100, 0x312F); 
406                 case Hangul_Compatibility_Jamo: return          unicode_range(0x3130, 0x318F); 
407                 case Kanbun: return             unicode_range(0x3190, 0x319F); 
408                 case Bopomofo_Extended: return          unicode_range(0x31A0, 0x31BF); 
409                 case CJK_Strokes: return                unicode_range(0x31C0, 0x31EF); 
410                 case Katakana_Phonetic_Extensions: return               unicode_range(0x31F0, 0x31FF); 
411                 case Enclosed_CJK_Letters_and_Months: return            unicode_range(0x3200, 0x32FF); 
412                 case CJK_Compatibility: return          unicode_range(0x3300, 0x33FF); 
413                 case CJK_Unified_Ideographs_Extension_A: return         unicode_range(0x3400, 0x4DBF); 
414                 case Yijing_Hexagram_Symbols: return            unicode_range(0x4DC0, 0x4DFF); 
415                 case CJK_Unified_Ideographs: return             unicode_range(0x4E00, 0x9FFF); 
416                 case Yi_Syllables: return               unicode_range(0xA000, 0xA48F); 
417                 case Yi_Radicals: return                unicode_range(0xA490, 0xA4CF); 
418                 case Lisu: return               unicode_range(0xA4D0, 0xA4FF); 
419                 case Vai: return                unicode_range(0xA500, 0xA63F); 
420                 case Cyrillic_Extended_B: return                unicode_range(0xA640, 0xA69F); 
421                 case Bamum: return              unicode_range(0xA6A0, 0xA6FF); 
422                 case Modifier_Tone_Letters: return              unicode_range(0xA700, 0xA71F); 
423                 case Latin_Extended_D: return           unicode_range(0xA720, 0xA7FF); 
424                 case Syloti_Nagri: return               unicode_range(0xA800, 0xA82F); 
425                 case Common_Indic_Number_Forms: return          unicode_range(0xA830, 0xA83F); 
426                 case Phags_pa: return           unicode_range(0xA840, 0xA87F); 
427                 case Saurashtra: return         unicode_range(0xA880, 0xA8DF); 
428                 case Devanagari_Extended: return                unicode_range(0xA8E0, 0xA8FF); 
429                 case Kayah_Li: return           unicode_range(0xA900, 0xA92F); 
430                 case Rejang: return             unicode_range(0xA930, 0xA95F); 
431                 case Hangul_Jamo_Extended_A: return             unicode_range(0xA960, 0xA97F); 
432                 case Javanese: return           unicode_range(0xA980, 0xA9DF); 
433                 case Cham: return               unicode_range(0xAA00, 0xAA5F); 
434                 case Myanmar_Extended_A: return         unicode_range(0xAA60, 0xAA7F); 
435                 case Tai_Viet: return           unicode_range(0xAA80, 0xAADF); 
436                 case Meetei_Mayek_Extensions: return            unicode_range(0xAAE0, 0xAAFF); 
437                 case Ethiopic_Extended_A: return                unicode_range(0xAB00, 0xAB2F); 
438                 case Meetei_Mayek: return               unicode_range(0xABC0, 0xABFF); 
439                 case Hangul_Syllables: return           unicode_range(0xAC00, 0xD7AF); 
440                 case Hangul_Jamo_Extended_B: return             unicode_range(0xD7B0, 0xD7FF); 
441                 case High_Surrogates: return            unicode_range(0xD800, 0xDB7F); 
442                 case High_Private_Use_Surrogates: return                unicode_range(0xDB80, 0xDBFF); 
443                 case Low_Surrogates: return             unicode_range(0xDC00, 0xDFFF); 
444                 case Private_Use_Area: return           unicode_range(0xE000, 0xF8FF); 
445                 case CJK_Compatibility_Ideographs: return               unicode_range(0xF900, 0xFAFF); 
446                 case Alphabetic_Presentation_Forms: return              unicode_range(0xFB00, 0xFB4F); 
447                 case Arabic_Presentation_Forms_A: return                unicode_range(0xFB50, 0xFDFF); 
448                 case Variation_Selectors: return                unicode_range(0xFE00, 0xFE0F); 
449                 case Vertical_Forms: return             unicode_range(0xFE10, 0xFE1F); 
450                 case Combining_Half_Marks: return               unicode_range(0xFE20, 0xFE2F); 
451                 case CJK_Compatibility_Forms: return            unicode_range(0xFE30, 0xFE4F); 
452                 case Small_Form_Variants: return                unicode_range(0xFE50, 0xFE6F); 
453                 case Arabic_Presentation_Forms_B: return                unicode_range(0xFE70, 0xFEFF); 
454                 case Halfwidth_and_Fullwidth_Forms: return              unicode_range(0xFF00, 0xFFEF); 
455                 case Specials: return           unicode_range(0xFFF0, 0xFFFF); 
456                 case Linear_B_Syllabary: return         unicode_range(0x10000, 0x1007F); 
457                 case Linear_B_Ideograms: return         unicode_range(0x10080, 0x100FF); 
458                 case Aegean_Numbers: return             unicode_range(0x10100, 0x1013F); 
459                 case Ancient_Greek_Numbers: return              unicode_range(0x10140, 0x1018F); 
460                 case Ancient_Symbols: return            unicode_range(0x10190, 0x101CF); 
461                 case Phaistos_Disc: return              unicode_range(0x101D0, 0x101FF); 
462                 case Lycian: return             unicode_range(0x10280, 0x1029F); 
463                 case Carian: return             unicode_range(0x102A0, 0x102DF); 
464                 case Old_Italic: return         unicode_range(0x10300, 0x1032F); 
465                 case Gothic: return             unicode_range(0x10330, 0x1034F); 
466                 case Ugaritic: return           unicode_range(0x10380, 0x1039F); 
467                 case Old_Persian: return                unicode_range(0x103A0, 0x103DF); 
468                 case Deseret: return            unicode_range(0x10400, 0x1044F); 
469                 case Shavian: return            unicode_range(0x10450, 0x1047F); 
470                 case Osmanya: return            unicode_range(0x10480, 0x104AF); 
471                 case Cypriot_Syllabary: return          unicode_range(0x10800, 0x1083F); 
472                 case Imperial_Aramaic: return           unicode_range(0x10840, 0x1085F); 
473                 case Phoenician: return         unicode_range(0x10900, 0x1091F); 
474                 case Lydian: return             unicode_range(0x10920, 0x1093F); 
475                 case Meroitic_Hieroglyphs: return               unicode_range(0x10980, 0x1099F); 
476                 case Meroitic_Cursive: return           unicode_range(0x109A0, 0x109FF); 
477                 case Kharoshthi: return         unicode_range(0x10A00, 0x10A5F); 
478                 case Old_South_Arabian: return          unicode_range(0x10A60, 0x10A7F); 
479                 case Avestan: return            unicode_range(0x10B00, 0x10B3F); 
480                 case Inscriptional_Parthian: return             unicode_range(0x10B40, 0x10B5F); 
481                 case Inscriptional_Pahlavi: return              unicode_range(0x10B60, 0x10B7F); 
482                 case Old_Turkic: return         unicode_range(0x10C00, 0x10C4F); 
483                 case Rumi_Numeral_Symbols: return               unicode_range(0x10E60, 0x10E7F); 
484                 case Brahmi: return             unicode_range(0x11000, 0x1107F); 
485                 case Kaithi: return             unicode_range(0x11080, 0x110CF); 
486                 case Sora_Sompeng: return               unicode_range(0x110D0, 0x110FF); 
487                 case Chakma: return             unicode_range(0x11100, 0x1114F); 
488                 case Sharada: return            unicode_range(0x11180, 0x111DF); 
489                 case Takri: return              unicode_range(0x11680, 0x116CF); 
490                 case Cuneiform: return          unicode_range(0x12000, 0x123FF); 
491                 case Cuneiform_Numbers_and_Punctuation: return          unicode_range(0x12400, 0x1247F); 
492                 case Egyptian_Hieroglyphs: return               unicode_range(0x13000, 0x1342F); 
493                 case Bamum_Supplement: return           unicode_range(0x16800, 0x16A3F); 
494                 case Miao: return               unicode_range(0x16F00, 0x16F9F); 
495                 case Kana_Supplement: return            unicode_range(0x1B000, 0x1B0FF); 
496                 case Byzantine_Musical_Symbols: return          unicode_range(0x1D000, 0x1D0FF); 
497                 case Musical_Symbols: return            unicode_range(0x1D100, 0x1D1FF); 
498                 case Ancient_Greek_Musical_Notation: return             unicode_range(0x1D200, 0x1D24F); 
499                 case Tai_Xuan_Jing_Symbols: return              unicode_range(0x1D300, 0x1D35F); 
500                 case Counting_Rod_Numerals: return              unicode_range(0x1D360, 0x1D37F); 
501                 case Mathematical_Alphanumeric_Symbols: return          unicode_range(0x1D400, 0x1D7FF); 
502                 case Arabic_Mathematical_Alphabetic_Symbols: return             unicode_range(0x1EE00, 0x1EEFF); 
503                 case Mahjong_Tiles: return              unicode_range(0x1F000, 0x1F02F); 
504                 case Domino_Tiles: return               unicode_range(0x1F030, 0x1F09F); 
505                 case Playing_Cards: return              unicode_range(0x1F0A0, 0x1F0FF); 
506                 case Enclosed_Alphanumeric_Supplement: return           unicode_range(0x1F100, 0x1F1FF); 
507                 case Enclosed_Ideographic_Supplement: return            unicode_range(0x1F200, 0x1F2FF); 
508                 case Miscellaneous_Symbols_And_Pictographs: return              unicode_range(0x1F300, 0x1F5FF); 
509                 case Emoticons: return          unicode_range(0x1F600, 0x1F64F); 
510                 case Transport_And_Map_Symbols: return          unicode_range(0x1F680, 0x1F6FF); 
511                 case Alchemical_Symbols: return         unicode_range(0x1F700, 0x1F77F); 
512                 case CJK_Unified_Ideographs_Extension_B: return         unicode_range(0x20000, 0x2A6DF); 
513                 case CJK_Unified_Ideographs_Extension_C: return         unicode_range(0x2A700, 0x2B73F); 
514                 case CJK_Unified_Ideographs_Extension_D: return         unicode_range(0x2B740, 0x2B81F); 
515                 case CJK_Compatibility_Ideographs_Supplement: return            unicode_range(0x2F800, 0x2FA1F); 
516                 case Tags: return               unicode_range(0xE0000, 0xE007F); 
517                 case Variation_Selectors_Supplement: return             unicode_range(0xE0100, 0xE01EF); 
518                 case Supplementary_Private_Use_Area_A: return           unicode_range(0xF0000, 0xFFFFF); 
519                 case Supplementary_Private_Use_Area_B: return           unicode_range(0x100000, 0x10FFFF);
520         }
521         return unicode_range(0,0);
522 }
523
524 }}}