.
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 using Cairo;
16
17 namespace BirdFont {
18
19 public enum FontFormat {
20 BIRDFONT,
21 BIRDFONT_PART,
22 FFI,
23 SVG,
24 FREETYPE
25 }
26
27 public class Font : GLib.Object {
28
29 /** Table with glyphs sorted by their unicode value. */
30 public GlyphTable glyph_cache;
31
32 /** Table with glyphs sorted by their name. */
33 public GlyphTable glyph_name;
34
35 /** Table with ligatures. */
36 public GlyphTable ligature;
37
38 /** List of alternate glyphs. */
39 public AlternateSets alternates;
40
41 public Gee.ArrayList<BackgroundImage> background_images;
42 public string background_scale = "1";
43
44 /** Top margin */
45 public double top_limit;
46
47 /** Height of upper case letters. */
48 public double top_position;
49
50 /** x-height upper bearing from origo. */
51 public double xheight_position;
52
53 /** Base line coordinate from origo. */
54 public double base_line;
55
56 /** Descender position */
57 public double bottom_position;
58
59 /** Bottom margin */
60 public double bottom_limit;
61
62 /** Custom guides. */
63 public Gee.ArrayList<Line> custom_guides = new Gee.ArrayList<Line> ();
64
65 public string? font_file = null;
66
67 bool modified = false;
68
69 // name table strings
70 public string postscript_name;
71 public string name;
72 public string subfamily;
73 public string full_name;
74 public string unique_identifier;
75 public string version;
76 public string description;
77 public string copyright;
78 public string license;
79 public string license_url;
80 public string trademark;
81 public string manufacturer;
82 public string designer;
83 public string vendor_url;
84 public string designer_url;
85
86 public bool bold = false;
87 public bool italic = false;
88 public int weight = 400;
89
90 public bool initialised = true;
91
92 OpenFontFormatReader otf;
93 bool otf_font = false;
94
95 /** Grid sizes. */
96 public Gee.ArrayList<string> grid_width;
97
98 /** File format. */
99 public FontFormat format = FontFormat.BIRDFONT;
100
101 public SpacingData spacing;
102
103 bool read_only = false;
104
105 /** Save font as many small .bfp files instead of one big .bf file */
106 bool bfp = false;
107 BirdFontPart bfp_file;
108
109 public Gee.ArrayList<Glyph> deleted_glyphs;
110
111 public Ligatures ligature_substitution;
112
113 public static string? default_license = null;
114 public static string? default_license_url = null;
115
116 public FontSettings settings;
117 public KerningStrings kerning_strings;
118
119 public signal void font_deleted ();
120
121 public static Font empty;
122
123 public int format_major = 0;
124 public int format_minor = 0;
125
126 public int units_per_em = 1024;
127
128 public Font () {
129 KerningClasses kerning_classes;
130
131 postscript_name = "Typeface";
132 name = "Typeface";
133 subfamily = "Regular";
134 full_name = "Typeface";
135 unique_identifier = "Typeface";
136 version = "Version 1.0";
137 description = "";
138 copyright = "";
139 license = "";
140 license_url = "";
141 trademark = "";
142 manufacturer = "";
143 designer = "";
144 vendor_url = "";
145 designer_url = "";
146
147 if (default_license != null) {
148 copyright = (!) default_license;
149 license = (!) default_license;
150 }
151
152 if (default_license_url != null) {
153 license_url = (!) default_license_url;
154 }
155
156 glyph_cache = new GlyphTable ();
157 glyph_name = new GlyphTable ();
158 ligature = new GlyphTable ();
159
160 grid_width = new Gee.ArrayList<string> ();
161
162 kerning_classes = new KerningClasses (this);
163 spacing = new SpacingData (kerning_classes);
164
165 top_limit = 84 ;
166 top_position = 72;
167 xheight_position = 56;
168 base_line = 0;
169 bottom_position = -20;
170 bottom_limit = -27;
171
172 bfp_file = new BirdFontPart (this);
173
174 deleted_glyphs = new Gee.ArrayList<Glyph> ();
175 ligature_substitution = new Ligatures (this);
176
177 background_images = new Gee.ArrayList<BackgroundImage> ();
178
179 settings = new FontSettings ();
180 kerning_strings = new KerningStrings ();
181
182 alternates = new AlternateSets ();
183 }
184
185 ~Font () {
186 font_deleted ();
187 }
188
189 public Alternate? get_alternate (string glyph_name, string tag) {
190 Gee.ArrayList<Alternate> alt = alternates.get_alt (tag);
191
192 foreach (Alternate a in alt) {
193 if (a.glyph_name == glyph_name) {
194 return a;
195 }
196 }
197
198 return null;
199 }
200
201 public void add_new_alternate (GlyphCollection glyph,
202 GlyphCollection alternate, string tag) {
203
204 Alternate a;
205 Alternate? alt = get_alternate (glyph.get_name (), tag);
206
207 if (alt == null) {
208 a = new Alternate (glyph.get_name (), tag);
209 alternates.add (a);
210 } else {
211 a = (!) alt;
212 }
213
214 a.add (alternate.get_name ());
215 glyph_name.insert (alternate.get_name (), alternate);
216 glyph_cache.insert (alternate.get_name (), alternate);
217 }
218
219 public void add_alternate (string glyph_name, string alternate,
220 string tag) {
221
222 Alternate a;
223 Alternate? alt = get_alternate (glyph_name, tag);
224
225 if (alt == null) {
226 a = new Alternate (glyph_name, tag);
227 alternates.add (a);
228 } else {
229 a = (!) alt;
230 }
231
232 a.add (alternate);
233 }
234
235 public bool has_compatible_format () {
236 return !newer_format () && !older_format ();
237 }
238
239 public bool older_format () {
240 if (format_major < BirdFontFile.MIN_FORMAT_MAJOR) {
241 return true;
242 }
243
244 if (format_major == BirdFontFile.MIN_FORMAT_MAJOR
245 && format_minor < BirdFontFile.MIN_FORMAT_MINOR) {
246 return true;
247 }
248
249 return false;
250 }
251
252 public bool newer_format () {
253 if (format_major > BirdFontFile.FORMAT_MAJOR) {
254 return true;
255 }
256
257 if (BirdFontFile.FORMAT_MAJOR == format_major
258 && format_minor > BirdFontFile.FORMAT_MINOR) {
259 return true;
260 }
261
262 return false;
263 }
264
265 public static void set_default_license (string license, string url) {
266 default_license = license;
267 default_license_url = url;
268 }
269
270 public Ligatures get_ligatures () {
271 return ligature_substitution;
272 }
273
274 public void set_weight (string w) {
275 int wi = int.parse (w);
276
277 if (wi > 0) {
278 weight = wi;
279 }
280 }
281
282 public string get_weight () {
283 return @"$weight";
284 }
285
286 public void touch () {
287 modified = true;
288 }
289
290 public KerningClasses get_kerning_classes () {
291 return spacing.get_kerning_classes ();
292 }
293
294 public SpacingData get_spacing () {
295 return spacing;
296 }
297
298 public File get_backgrounds_folder () {
299 string fn = @"$(get_name ()) backgrounds";
300 File f = get_child (BirdFont.get_settings_directory (), fn);
301 return f;
302 }
303
304 /** Retuns true if the current font has be modified */
305 public bool is_modified () {
306 return modified;
307 }
308
309 /** Full path to this font file. */
310 public string get_path () {
311 int i = 0;
312 string fn;
313 File f;
314 File file;
315
316 if (font_file != null) {
317 fn = (!) font_file;
318
319 // assume only absolute paths are used on windows
320 if (BirdFont.win32) {
321 return fn;
322 } else {
323 file = File.new_for_path (fn);
324 return (!) file.resolve_relative_path ("").get_path ();
325 }
326 }
327
328 StringBuilder sb = new StringBuilder ();
329 sb.append (Environment.get_home_dir ());
330 sb.append (@"/$(get_name ()).bf");
331
332 f = File.new_for_path (sb.str);
333
334 while (f.query_exists ()) {
335 sb.erase ();
336 sb.append (Environment.get_home_dir ());
337 sb.append (@"/$(get_name ())$(++i).bf");
338 f = File.new_for_path (sb.str);
339 }
340
341 return sb.str;
342 }
343
344 public string get_file_name () {
345 string p = get_path ();
346 int i = p.last_index_of ("/");
347
348 if (i == -1) {
349 i = p.last_index_of ("\\");
350 }
351
352 p = p.substring (i + 1);
353
354 return p;
355 }
356
357 /** @return an absolute path to the font folder. */
358 public File get_folder () {
359 string p = get_folder_path ();
360 File fp = File.new_for_path (p);
361
362 if (BirdFont.win32) {
363 if (p.index_of (":\\") == -1) {
364 p = (!) fp.resolve_relative_path ("").get_path ();
365 }
366 } else {
367 if (!p.has_prefix ("/")) {
368 p = (!) fp.resolve_relative_path ("").get_path ();
369 }
370 }
371
372 return File.new_for_path (p);
373 }
374
375 /** @return a path to the font folder, it can be relative. */
376 public string get_folder_path () {
377 string p = get_path ();
378 int i = p.last_index_of ("/");
379
380 if (i == -1) {
381 i = p.last_index_of ("\\");
382 }
383
384 if (i == -1) {
385 warning (@"Can not find folder in $p.");
386 p = ".";
387 } else {
388 p = p.substring (0, i);
389 }
390
391 if (p.index_of (":") != -1 && p.char_count () == 2) {
392 p += "\\";
393 }
394
395 return p;
396 }
397
398 public double get_height () {
399 double r = top_position - base_line;
400 return (r > 0) ? r : -r;
401 }
402
403 public void set_name (string name) {
404 string n = name;
405 this.name = n;
406 }
407
408 public string get_full_name () {
409 return full_name;
410 }
411
412 public string get_name () {
413 return name;
414 }
415
416 public void print_all () {
417 stdout.printf ("Unicode:\n");
418 glyph_cache.for_each((g) => {
419 stdout.printf (@"$(g.get_unicode ())\n");
420 });
421
422 stdout.printf ("Names:\n");
423 glyph_name.for_each((g) => {
424 stdout.printf (@"$(g.get_name ())\n");
425 });
426 }
427
428 public bool has_glyph (string n) {
429 return get_glyph (n) != null;
430 }
431
432 public GlyphCollection get_nonmarking_return () {
433 Glyph g;
434 GlyphCollection gc;
435
436 if (has_glyph ("nonmarkingreturn")) {
437 return (!) get_glyph_collection ("nonmarkingreturn");
438 }
439
440 gc = new GlyphCollection ('\r', "nonmarkingreturn");
441
442 g = new Glyph ("nonmarkingreturn", '\r');
443 g.left_limit = 0;
444 g.right_limit = 0;
445 g.remove_empty_paths ();
446
447 gc.set_unassigned (false);
448 gc.add_glyph (g);
449
450 return gc;
451 }
452
453 public GlyphCollection get_null_character () {
454 Glyph n;
455 GlyphCollection gc;
456
457 if (has_glyph ("null")) {
458 return (!) get_glyph_collection ("null");
459 }
460
461 gc = new GlyphCollection ('\0', "null");
462 n = new Glyph ("null", '\0');
463
464 gc.add_glyph (n);
465 gc.set_unassigned (false);
466
467 n.left_limit = 0;
468 n.right_limit = 0;
469 n.remove_empty_paths ();
470
471 return gc;
472 }
473
474 public GlyphCollection get_space () {
475 Glyph n;
476 GlyphCollection gc;
477
478 if (has_glyph (" ")) {
479 return (!) get_glyph_collection (" ");
480 }
481
482 if (has_glyph ("space")) {
483 return (!) get_glyph_collection ("space");
484 }
485
486 gc = new GlyphCollection (' ', "space");
487
488 n = new Glyph ("space", ' ');
489 n.left_limit = 0;
490 n.right_limit = 27;
491 n.remove_empty_paths ();
492
493 gc.add_glyph (n);
494 gc.set_unassigned (false);
495
496 return gc;
497 }
498
499 public GlyphCollection get_not_def_character () {
500 Glyph g;
501 GlyphCollection gc;
502
503 Path p;
504 Path i;
505
506 if (has_glyph (".notdef")) {
507 return (!) get_glyph_collection (".notdef");
508 }
509
510 gc = new GlyphCollection ('\0', ".notdef");
511 g = new Glyph (".notdef", 0);
512 p = new Path ();
513 i = new Path ();
514
515 gc.set_unassigned (true);
516 gc.add_glyph (g);
517
518 g.left_limit = -20;
519 g.right_limit = 33;
520
521 g.add_help_lines ();
522
523 p.add (-20, top_position - 5);
524 p.add (20, top_position - 5);
525 p.add (20, base_line + 5);
526 p.add (-20, base_line + 5);
527 p.close ();
528
529 i.add (-15, top_position - 10);
530 i.add (15, top_position - 10);
531 i.add (15, base_line + 10);
532 i.add (-15, base_line + 10);
533 i.reverse ();
534 i.close ();
535
536 g.add_path (i);
537 g.add_path (p);
538
539 i.recalculate_linear_handles ();
540 p.recalculate_linear_handles ();
541
542 return gc;
543 }
544
545 public void add_glyph_collection (GlyphCollection glyph_collection) {
546 GlyphCollection? gc;
547
548 if (unlikely (glyph_collection.get_name () == "")) {
549 warning ("Refusing to add glyph with name \"\", null character should be named null.");
550 return;
551 }
552
553 gc = glyph_name.get (glyph_collection.get_name ());
554 if (unlikely (gc != null)) {
555 warning ("glyph has already been added");
556 return;
557 }
558
559 glyph_name.insert (glyph_collection.get_name (), glyph_collection);
560
561 if (glyph_collection.get_unicode () != "") {
562 glyph_cache.insert ((!) glyph_collection.get_unicode (), glyph_collection);
563 } else {
564 glyph_cache.insert ((!) glyph_collection.get_name (), glyph_collection);
565 }
566
567 if (glyph_collection.is_unassigned ()) {
568 ligature.insert (glyph_collection.get_name (), glyph_collection);
569 }
570 }
571
572 public static string get_name_for_character (unichar c) {
573 StringBuilder sb;
574
575 if (c == 0) {
576 return ".null".dup ();
577 }
578
579 sb = new StringBuilder ();
580 sb.append_unichar (c);
581 return sb.str;
582 }
583
584 public bool has_name (string name) {
585 return glyph_name.has_key (name);
586 }
587
588 public void delete_glyph (GlyphCollection glyph) {
589 glyph_cache.remove (glyph.get_unicode ());
590 glyph_cache.remove (glyph.get_name ());
591 glyph_name.remove (glyph.get_name ());
592 ligature.remove (glyph.get_current ().get_name ());
593
594 foreach (Alternate a in alternates.alternates) {
595 a.remove (glyph);
596 }
597
598 foreach (Glyph g in glyph.glyphs) {
599 deleted_glyphs.add (g);
600 }
601 }
602
603 // FIXME: the order of ligature substitutions
604 public GlyphCollection? get_ligature (uint indice) {
605 return ligature.nth (indice);
606 }
607
608 /** Obtain all versions and alterntes for this glyph. */
609 public GlyphCollection? get_glyph_collection (string unichar_code) {
610 GlyphCollection? gc = null;
611 gc = glyph_cache.get (unichar_code);
612 return gc;
613 }
614
615 /** Get glyph collection by name. */
616 public GlyphCollection? get_glyph_collection_by_name (string? glyph) {
617 GlyphCollection? gc = null;
618
619 if (glyph != null) {
620 gc = glyph_name.get ((!) glyph);
621 }
622
623 return gc;
624 }
625
626 /** Get glyph by name. */
627 public Glyph? get_glyph_by_name (string glyph) {
628 GlyphCollection? gc = get_glyph_collection_by_name (glyph);
629
630 if (gc == null) {
631 return null;
632 }
633
634 return ((!)gc).get_current ();
635 }
636
637 public Glyph? get_glyph (string name) {
638 GlyphCollection? gc = null;
639 gc = glyph_name.get (name);
640
641 if (gc == null || ((!)gc).length () == 0) {
642 return null;
643 }
644
645 return ((!)gc).get_current ();
646 }
647
648 public GlyphCollection? get_glyph_collection_indice (unichar glyph_indice) {
649 if (!(0 <= glyph_indice < glyph_name.length ())) {
650 return null;
651 }
652
653 return glyph_name.nth (glyph_indice);
654 }
655
656 public Glyph? get_glyph_indice (unichar glyph_indice) {
657 GlyphCollection? gc;
658
659 gc = get_glyph_collection_indice (glyph_indice);
660
661 if (gc != null) {
662 return ((!) gc).get_current ();
663 }
664
665 return null;
666 }
667
668 public void add_background_image (BackgroundImage image) {
669 background_images.add (image);
670 }
671
672 /** Delete temporary rescue files. */
673 public void delete_backup () {
674 File dir = BirdFont.get_backup_directory ();
675 File? new_file = null;
676 File file;
677 string backup_file;
678
679 new_file = get_child (dir, @"$(name).bf");
680 backup_file = (!) ((!) new_file).get_path ();
681
682 try {
683 file = File.new_for_path (backup_file);
684 if (file.query_exists ()) {
685 file.delete ();
686 }
687 } catch (GLib.Error e) {
688 stderr.printf (@"Failed to delete backup\n");
689 warning (@"$(e.message) \n");
690 }
691 }
692
693 /** Returns path to backup file. */
694 public string save_backup () {
695 File dir = BirdFont.get_backup_directory ();
696 File? temp_file = null;
697 string backup_file;
698 BirdFontFile birdfont_file = new BirdFontFile (this);
699
700 temp_file = get_child (dir, @"$(name).bf");
701 backup_file = (!) ((!) temp_file).get_path ();
702 backup_file = backup_file.replace (" ", "_");
703
704 if (get_path () == backup_file) {
705 warning ("Refusing to write backup of a backup.");
706 return backup_file;
707 }
708
709 birdfont_file.write_font_file (backup_file, true);
710 return backup_file;
711 }
712
713 public void init_bfp (string directory) {
714 try {
715 bfp_file = new BirdFontPart (this);
716 bfp_file.create_directory (directory);
717 bfp_file.save ();
718 this.bfp = true;
719 } catch (GLib.Error e) {
720 warning (e.message);
721 // FIXME: notify user
722 }
723 }
724
725 public void set_bfp (bool bfp) {
726 this.bfp = bfp;
727 }
728
729 public bool is_bfp () {
730 return bfp;
731 }
732
733 public void save () {
734 if (is_bfp ()) {
735 save_bfp ();
736 } else {
737 save_bf ();
738 }
739
740 settings.save (get_file_name ());
741 }
742
743 public bool save_bfp () {
744 return bfp_file.save ();
745 }
746
747 public void save_bf () {
748 Font font;
749 BirdFontFile birdfont_file = new BirdFontFile (this);
750 string path;
751 bool file_written;
752
753 if (font_file == null) {
754 warning ("File name not set.");
755 return;
756 }
757
758 path = (!) font_file;
759 file_written = birdfont_file.write_font_file (path);
760
761 if (read_only) {
762 warning (@"$path is write protected.");
763 return;
764 }
765
766 if (!path.has_suffix (".bf")) {
767 warning ("Expecting .bf format.");
768 return;
769 }
770
771 if (file_written) {
772 // delete the backup when the font is saved
773 font = BirdFont.get_current_font ();
774 font.delete_backup ();
775 }
776
777 modified = false;
778 }
779
780 public void set_font_file (string path) {
781 font_file = path;
782 modified = false;
783 }
784
785 /** Number of glyphs in this font. */
786 public uint length () {
787 return glyph_name.length ();
788 }
789
790 public bool is_empty () {
791 return (glyph_name.length () == 0);
792 }
793
794 public void set_file (string path) {
795 font_file = path;
796 }
797
798 public bool load () {
799 string path;
800 bool loaded = false;
801
802 initialised = true;
803 otf_font = false;
804
805 if (font_file == null) {
806 warning ("No file name.");
807 return false;
808 }
809
810 path = (!) font_file;
811
812 grid_width.clear ();
813
814 // empty cache and fill it with new glyphs from disk
815 glyph_cache.remove_all ();
816 glyph_name.remove_all ();
817 ligature.remove_all ();
818
819 if (path.has_suffix (".svg") || path.has_suffix (".SVG")) {
820 Toolbox.select_tool_by_name ("cubic_points");
821 loaded = parse_svg_file (path);
822
823 if (!loaded) {
824 warning ("Failed to load SVG font.");
825 }
826
827 format = FontFormat.SVG;
828 }
829
830 if (path.has_suffix (".ffi")) {
831 loaded = parse_bf_file (path);
832 format = FontFormat.FFI;
833 }
834
835 if (path.has_suffix (".bf") || path.has_suffix (".BF")) {
836 loaded = parse_bf_file (path);
837 format = FontFormat.BIRDFONT;
838 }
839
840 if (path.has_suffix (".bfp") || path.has_suffix (".BFP")) {
841 loaded = parse_bfp_file (path);
842 format = FontFormat.BIRDFONT_PART;
843 }
844
845 if (path.has_suffix (".ttf") || path.has_suffix (".TTF")) {
846 loaded = parse_freetype_file (path);
847
848 if (!loaded) {
849 warning ("Failed to load TTF font.");
850 }
851
852 format = FontFormat.FREETYPE;
853
854 // run the old parser for debugging puposes
855 if (BirdFont.has_argument ("--test")) {
856 try {
857 OpenFontFormatReader or = new OpenFontFormatReader ();
858 or.parse_index (path);
859 } catch (GLib.Error e) {
860 warning (e.message);
861 }
862 }
863
864 font_file = null; // make sure BirdFont asks where to save the file
865 }
866
867 if (path.has_suffix (".otf") || path.has_suffix (".OTF")) {
868 loaded = parse_freetype_file (path);
869
870 if (!loaded) {
871 warning ("Failed to load OTF font.");
872 }
873
874 format = FontFormat.FREETYPE;
875
876 font_file = null;
877 }
878
879 if (loaded) {
880 settings.load (get_file_name ());
881 kerning_strings.load (this);
882 }
883
884 return loaded;
885 }
886
887 private bool parse_bfp_file (string path) {
888 return bfp_file.load (path);
889 }
890
891 private bool parse_bf_file (string path) {
892 BirdFontFile font = new BirdFontFile (this);
893 return font.load (path);
894 }
895
896 private bool parse_freetype_file (string path) {
897 string font_data;
898 StringBuilder? data;
899 int error;
900 bool parsed;
901 BirdFontFile bf_font = new BirdFontFile (this);
902
903 data = load_freetype_font (path, out error);
904
905 if (error != 0) {
906 warning ("Failed to load freetype font.");
907 return false;
908 }
909
910 if (data == null) {
911 warning ("No svg data.");
912 return false;
913 }
914
915 font_data = ((!) data).str;
916 parsed = bf_font.load_data (font_data);
917
918 if (!parsed) {
919 warning ("Failed to parse loaded freetype font.");
920 }
921
922 return parsed;
923 }
924
925 private bool parse_svg_file (string path) {
926 SvgFont svg_font = new SvgFont (this);
927 svg_font.load (path);
928 return true;
929 }
930
931 public bool parse_otf_file (string path) throws GLib.Error {
932 otf = new OpenFontFormatReader ();
933 otf_font = true;
934 otf.parse_index (path);
935 return true;
936 }
937
938 public void set_read_only (bool r) {
939 read_only = r;
940 }
941
942 /**
943 * @param glyphs Name of glyphs or unicode values separated by space.
944 * @return glyph names
945 */
946 public Gee.ArrayList<string> get_names (string glyphs) {
947 return get_names_order (glyphs, false);
948 }
949
950 public Gee.ArrayList<string> get_names_in_reverse_order (string glyphs) {
951 return get_names_order (glyphs, true);
952 }
953
954 public Gee.ArrayList<string> get_names_order (string glyphs, bool reverse) {
955 Gee.ArrayList<string> names = new Gee.ArrayList<string> ();
956 string[] parts = glyphs.strip ().split (" ");
957
958 foreach (string p in parts) {
959 if (p.has_prefix ("U+") || p.has_prefix ("u+")) {
960 p = (!) to_unichar (p).to_string ();
961 }
962
963 if (p == "space") {
964 p = " ";
965 }
966
967 if (p == "divis") {
968 p = "-";
969 }
970
971 if (!has_glyph (p)) {
972 warning (@"The character $p does not have a glyph in " + get_file_name ());
973 p = ".notdef";
974 }
975
976 if (p != "") {
977 if (reverse) {
978 names.insert (0, p);
979 } else {
980 names.add (p);
981 }
982 }
983 }
984
985 return names;
986 }
987
988 public static unichar to_unichar (string unicode) {
989 int index = 2;
990 int i = 0;
991 unichar c;
992 unichar rc = 0;
993 bool r;
994
995 if (unlikely (!unicode.has_prefix ("U+") && !unicode.has_prefix ("u+"))) {
996 warning (@"All unicode values must begin with U+ ($unicode)");
997 return '\0';
998 }
999
1000 try {
1001 while (r = unicode.get_next_char (ref index, out c)) {
1002 rc <<= 4;
1003 rc += hex_to_oct (c);
1004
1005 if (++i > 6) {
1006 throw new ConvertError.FAILED ("i > 6");
1007 }
1008 }
1009 } catch (ConvertError e) {
1010 warning (@"unicode: $unicode\n");
1011 warning (e.message);
1012 rc = '\0';
1013 }
1014
1015 return rc;
1016 }
1017
1018 private static string oct_to_hex (uint8 o) {
1019 switch (o) {
1020 case 10: return "a";
1021 case 11: return "b";
1022 case 12: return "c";
1023 case 13: return "d";
1024 case 14: return "e";
1025 case 15: return "f";
1026 }
1027
1028 return_val_if_fail (0 <= o <= 9, "-".dup ());
1029
1030 return o.to_string ();
1031 }
1032
1033 private static uint8 hex_to_oct (unichar o)
1034 throws ConvertError {
1035 StringBuilder s = new StringBuilder ();
1036 s.append_unichar (o);
1037
1038 switch (o) {
1039 case 'a': return 10;
1040 case 'b': return 11;
1041 case 'c': return 12;
1042 case 'd': return 13;
1043 case 'e': return 14;
1044 case 'f': return 15;
1045 case 'A': return 10;
1046 case 'B': return 11;
1047 case 'C': return 12;
1048 case 'D': return 13;
1049 case 'E': return 14;
1050 case 'F': return 15;
1051 }
1052
1053 if (!('0' <= o <= '9')) {
1054 throw new ConvertError.FAILED (@"Expecting a number ($(s.str)).");
1055 }
1056
1057 return (uint8) (o - '0');
1058 }
1059
1060 public static string to_hex (unichar ch) {
1061 StringBuilder s = new StringBuilder ();
1062 s.append ("U+");
1063 s.append (to_hex_code (ch));
1064 return s.str;
1065 }
1066
1067 public static string to_hex_code (unichar ch) {
1068 StringBuilder s = new StringBuilder ();
1069
1070 uint8 a = (uint8)(ch & 0x00000F);
1071 uint8 b = (uint8)((ch & 0x0000F0) >> 4 * 1);
1072 uint8 c = (uint8)((ch & 0x000F00) >> 4 * 2);
1073 uint8 d = (uint8)((ch & 0x00F000) >> 4 * 3);
1074 uint8 e = (uint8)((ch & 0x0F0000) >> 4 * 4);
1075 uint8 f = (uint8)((ch & 0xF00000) >> 4 * 5);
1076
1077 if (e != 0 || f != 0) {
1078 s.append (oct_to_hex (f));
1079 s.append (oct_to_hex (e));
1080 }
1081
1082 if (c != 0 || d != 0) {
1083 s.append (oct_to_hex (d));
1084 s.append (oct_to_hex (c));
1085 }
1086
1087 s.append (oct_to_hex (b));
1088 s.append (oct_to_hex (a));
1089
1090 return s.str;
1091 }
1092 }
1093
1094 }
1095