Upload tizen 2.0 beta source
[external/pango1.0.git] / pango / modules.c
1 /* Pango
2  * modules.c:
3  *
4  * Copyright (C) 1999 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <limits.h>
26 #include <errno.h>
27
28 #include <gmodule.h>
29 #include <glib/gstdio.h>
30
31 #include "pango-enum-types.h"
32 #include "pango-modules.h"
33 #include "pango-impl-utils.h"
34 #include "modules.h"
35
36 typedef struct _PangoModule      PangoModule;
37 typedef struct _PangoModuleClass PangoModuleClass;
38
39 #define PANGO_TYPE_MODULE           (pango_module_get_type ())
40 #define PANGO_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), PANGO_TYPE_MODULE, PangoModule))
41 #define PANGO_IS_MODULE(module)  (G_TYPE_CHECK_INSTANCE_TYPE ((module), PANGO_TYPE_MODULE))
42
43 typedef struct _PangoMapInfo PangoMapInfo;
44 typedef struct _PangoEnginePair PangoEnginePair;
45 typedef struct _PangoSubmap PangoSubmap;
46
47 struct _PangoMap
48 {
49   GArray *entries;
50 };
51
52 struct _PangoMapEntry
53 {
54   GSList *exact;
55   GSList *fallback;
56 };
57
58 struct _PangoMapInfo
59 {
60   PangoLanguage *language;
61   guint engine_type_id;
62   guint render_type_id;
63   PangoMap *map;
64 };
65
66 struct _PangoEnginePair
67 {
68   PangoEngineInfo info;
69   PangoModule *module;
70   PangoEngine *engine;
71 };
72
73 struct _PangoModule
74 {
75   GTypeModule parent_instance;
76
77   char *path;
78   GModule *library;
79
80   void         (*list)   (PangoEngineInfo **engines, gint *n_engines);
81   void         (*init)   (GTypeModule *module);
82   void         (*exit)   (void);
83   PangoEngine *(*create) (const gchar *id);
84 };
85
86 struct _PangoModuleClass
87 {
88   GTypeModuleClass parent_class;
89 };
90
91 static GList *maps = NULL;
92 static GSList *registered_engines = NULL;
93 static GSList *dlloaded_engines = NULL;
94 static GHashTable *dlloaded_modules;
95
96 static GObjectClass *parent_class;
97
98 static void build_map    (PangoMapInfo *info);
99 static void init_modules (void);
100
101 static GType pango_module_get_type (void);
102
103 /**
104  * pango_find_map:
105  * @language: the language tag for which to find the map
106  * @engine_type_id: the engine type for the map to find
107  * @render_type_id: the render type for the map to find
108  *
109  * Locate a #PangoMap for a particular engine type and render
110  * type. The resulting map can be used to determine the engine
111  * for each character.
112  *
113  * Return value: the suitable #PangoMap.
114  **/
115 PangoMap *
116 pango_find_map (PangoLanguage *language,
117                 guint          engine_type_id,
118                 guint          render_type_id)
119 {
120   GList *tmp_list = maps;
121   PangoMapInfo *map_info = NULL;
122   gboolean found_earlier = FALSE;
123
124   while (tmp_list)
125     {
126       map_info = tmp_list->data;
127       if (map_info->engine_type_id == engine_type_id &&
128           map_info->render_type_id == render_type_id)
129         {
130           if (map_info->language == language)
131             break;
132           else
133             found_earlier = TRUE;
134         }
135
136       tmp_list = tmp_list->next;
137     }
138
139   if (!tmp_list)
140     {
141       map_info = g_slice_new (PangoMapInfo);
142       map_info->language = language;
143       map_info->engine_type_id = engine_type_id;
144       map_info->render_type_id = render_type_id;
145
146       build_map (map_info);
147
148       maps = g_list_prepend (maps, map_info);
149     }
150   else if (found_earlier)
151     {
152       /* Move the found map to the beginning of the list
153        * for speed next time around if we had to do
154        * any failing comparison. (No longer so important,
155        * since we don't strcmp.)
156        */
157       maps = g_list_remove_link(maps, tmp_list);
158       maps = g_list_prepend(maps, tmp_list->data);
159       g_list_free_1(tmp_list);
160     }
161
162   return map_info->map;
163 }
164
165 static gboolean
166 pango_module_load (GTypeModule *module)
167 {
168   PangoModule *pango_module = PANGO_MODULE (module);
169
170   if (pango_module->path)
171     {
172       pango_module->library = g_module_open (pango_module->path, G_MODULE_BIND_LOCAL);
173       if (!pango_module->library)
174         {
175           g_warning ("%s", g_module_error());
176           return FALSE;
177         }
178
179       /* extract symbols from the lib */
180       if (!g_module_symbol (pango_module->library, "script_engine_init",
181                             (gpointer *)(void *)&pango_module->init) ||
182           !g_module_symbol (pango_module->library, "script_engine_exit",
183                             (gpointer *)(void *)&pango_module->exit) ||
184           !g_module_symbol (pango_module->library, "script_engine_list",
185                             (gpointer *)(void *)&pango_module->list) ||
186           !g_module_symbol (pango_module->library, "script_engine_create",
187                             (gpointer *)(void *)&pango_module->create))
188         {
189           g_warning ("%s", g_module_error());
190           g_module_close (pango_module->library);
191
192           return FALSE;
193         }
194     }
195
196   /* call the module's init function to let it */
197   /* setup anything it needs to set up. */
198   pango_module->init (module);
199
200   return TRUE;
201 }
202
203 static void
204 pango_module_unload (GTypeModule *module)
205 {
206   PangoModule *pango_module = PANGO_MODULE (module);
207
208   pango_module->exit();
209
210   if (pango_module->path)
211     {
212       g_module_close (pango_module->library);
213       pango_module->library = NULL;
214
215       pango_module->init = NULL;
216       pango_module->exit = NULL;
217       pango_module->list = NULL;
218       pango_module->create = NULL;
219     }
220 }
221
222 /* This only will ever be called if an error occurs during
223  * initialization
224  */
225 static void
226 pango_module_finalize (GObject *object)
227 {
228   PangoModule *module = PANGO_MODULE (object);
229
230   g_free (module->path);
231
232   parent_class->finalize (object);
233 }
234
235 static void
236 pango_module_class_init (PangoModuleClass *class)
237 {
238   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
239   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
240
241   parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class));
242
243   module_class->load = pango_module_load;
244   module_class->unload = pango_module_unload;
245
246   gobject_class->finalize = pango_module_finalize;
247 }
248
249 static PANGO_DEFINE_TYPE (PangoModule, pango_module,
250                           pango_module_class_init, NULL,
251                           G_TYPE_TYPE_MODULE)
252
253 static PangoEngine *
254 pango_engine_pair_get_engine (PangoEnginePair *pair)
255 {
256   if (!pair->engine)
257     {
258       if (g_type_module_use (G_TYPE_MODULE (pair->module)))
259         {
260           pair->engine = pair->module->create (pair->info.id);
261           g_type_module_unuse (G_TYPE_MODULE (pair->module));
262         }
263
264       if (!pair->engine)
265         {
266           /* If a module cannot be used, or doesn't not create an engine
267            * correctly, we print out an error containing module name and id,
268            * but to not flood the terminal with zillions of the message, we
269            * set a flag on the module to only err once per module.
270            */
271           static GQuark warned_quark = 0;
272
273           if (!warned_quark)
274             warned_quark = g_quark_from_static_string ("pango-module-warned");
275
276           if (!g_object_get_qdata (G_OBJECT (pair->module), warned_quark))
277             {
278               g_warning ("Failed to load Pango module '%s' for id '%s'", pair->module->path, pair->info.id);
279
280               g_object_set_qdata_full (G_OBJECT (pair->module), warned_quark,
281                                        GINT_TO_POINTER (1), NULL);
282             }
283         }
284     }
285
286   return pair->engine;
287 }
288
289 static void
290 handle_included_module (PangoIncludedModule *included_module,
291                         GSList             **engine_list)
292 {
293   PangoModule *module = g_object_new (PANGO_TYPE_MODULE, NULL);
294   PangoEngineInfo *engine_info;
295   int n_engines;
296   int i;
297
298   module->list = included_module->list;
299   module->init = included_module->init;
300   module->exit = included_module->exit;
301   module->create = included_module->create;
302
303   module->list (&engine_info, &n_engines);
304
305   for (i = 0; i < n_engines; i++)
306     {
307       PangoEnginePair *pair = g_slice_new (PangoEnginePair);
308
309       pair->info = engine_info[i];
310       pair->module = module;
311       pair->engine = NULL;
312
313       *engine_list = g_slist_prepend (*engine_list, pair);
314     }
315 }
316
317 static PangoModule *
318 find_or_create_module (const char *raw_path)
319 {
320   PangoModule *module;
321   char *path;
322
323 #if defined(G_OS_WIN32) && defined(LIBDIR)
324   if (strncmp (raw_path,
325                LIBDIR "/pango/" MODULE_VERSION "/modules/",
326                strlen (LIBDIR "/pango/" MODULE_VERSION "/modules/")) == 0)
327     {
328       /* This is an entry put there by make install on the
329        * packager's system. On Windows a prebuilt Pango
330        * package can be installed in a random
331        * location. The pango.modules file distributed in
332        * such a package contains paths from the package
333        * builder's machine. Replace the path with the real
334        * one on this machine. */
335       path =
336         g_strconcat (pango_get_lib_subdirectory (),
337                      "\\" MODULE_VERSION "\\modules\\",
338                      raw_path + strlen (LIBDIR "/pango/" MODULE_VERSION "/modules/"),
339                      NULL);
340     }
341   else
342 #endif
343     {
344       path = g_strdup (raw_path);
345     }
346
347   module = g_hash_table_lookup (dlloaded_modules, path);
348   if (module)
349     g_free (path);
350   else
351     {
352       module = g_object_new (PANGO_TYPE_MODULE, NULL);
353       module->path = path;
354       g_hash_table_insert (dlloaded_modules, path, module);
355     }
356
357   return module;
358 }
359
360 static PangoScript
361 script_from_string (const char *str)
362 {
363   static GEnumClass *class = NULL;
364   GEnumValue *value;
365   if (!class)
366     class = g_type_class_ref (PANGO_TYPE_SCRIPT);
367
368   value = g_enum_get_value_by_nick (class, str);
369   if (!value)
370     return PANGO_SCRIPT_INVALID_CODE;
371
372   return value->value;
373 }
374
375 static void
376 script_info_free (PangoEngineScriptInfo *script_info,
377                   gpointer data G_GNUC_UNUSED)
378 {
379   g_slice_free (PangoEngineScriptInfo, script_info);
380 }
381
382 static gboolean /* Returns true if succeeded, false if failed */
383 process_module_file (FILE *module_file)
384 {
385   GString *line_buf = g_string_new (NULL);
386   GString *tmp_buf = g_string_new (NULL);
387   gboolean have_error = FALSE;
388
389   while (pango_read_line (module_file, line_buf))
390     {
391       PangoEnginePair *pair = g_slice_new (PangoEnginePair);
392       PangoEngineScriptInfo *script_info;
393       PangoScript script;
394       GList *scripts = NULL;
395       GList *tmp_list;
396
397       const char *p;
398       char *q;
399       int i;
400
401       p = line_buf->str;
402
403       if (!pango_skip_space (&p))
404         {
405           g_slice_free (PangoEnginePair, pair);
406           continue;
407         }
408
409       i = 0;
410       while (1)
411         {
412           if (!pango_scan_string (&p, tmp_buf))
413             {
414               have_error = TRUE;
415               goto error;
416             }
417
418           switch (i)
419             {
420             case 0:
421               pair->module = find_or_create_module (tmp_buf->str);
422               break;
423             case 1:
424               pair->info.id = g_strdup (tmp_buf->str);
425               break;
426             case 2:
427               pair->info.engine_type = g_strdup (tmp_buf->str);
428               break;
429             case 3:
430               pair->info.render_type = g_strdup (tmp_buf->str);
431               break;
432             default:
433               q = strchr (tmp_buf->str, ':');
434               if (!q)
435                 {
436                   have_error = TRUE;
437                   goto error;
438                 }
439               *q = '\0';
440               script = script_from_string (tmp_buf->str);
441               if (script == PANGO_SCRIPT_INVALID_CODE)
442                 {
443                   have_error = TRUE;
444                   goto error;
445                 }
446
447               script_info = g_slice_new (PangoEngineScriptInfo);
448               script_info->script = script;
449               script_info->langs = g_strdup (q + 1);
450
451               scripts = g_list_prepend (scripts, script_info);
452             }
453
454           if (!pango_skip_space (&p))
455             break;
456
457           i++;
458         }
459
460       if (i<3)
461         {
462           have_error = TRUE;
463           goto error;
464         }
465
466       scripts = g_list_reverse (scripts);
467       pair->info.n_scripts = g_list_length (scripts);
468       pair->info.scripts = g_new (PangoEngineScriptInfo, pair->info.n_scripts);
469
470       tmp_list = scripts;
471       for (i=0; i<pair->info.n_scripts; i++)
472         {
473           pair->info.scripts[i] = *(PangoEngineScriptInfo *)tmp_list->data;
474           tmp_list = tmp_list->next;
475         }
476
477       pair->engine = NULL;
478
479       dlloaded_engines = g_slist_prepend (dlloaded_engines, pair);
480
481     error:
482       g_list_foreach (scripts, (GFunc)script_info_free, NULL);
483       g_list_free (scripts);
484
485       if (have_error)
486         {
487           g_printerr ("Error reading Pango modules file\n");
488           g_slice_free(PangoEnginePair, pair);
489           break;
490         }
491     }
492
493   g_string_free (line_buf, TRUE);
494   g_string_free (tmp_buf, TRUE);
495
496   return !have_error;
497 }
498
499 static void
500 read_modules (void)
501 {
502   FILE *module_file;
503
504   char *file_str =  pango_config_key_get ("Pango/ModuleFiles");
505   char **files;
506   int n;
507
508   dlloaded_modules = g_hash_table_new (g_str_hash, g_str_equal);
509
510   if (!file_str)
511     file_str = g_build_filename (pango_get_sysconf_subdirectory (),
512                                  "pango.modules",
513                                  NULL);
514
515   files = pango_split_file_list (file_str);
516
517   n = 0;
518   while (files[n])
519     n++;
520
521   while (n-- > 0)
522     {
523       module_file = g_fopen (files[n], "r");
524       if (module_file)
525         {
526           process_module_file(module_file);
527           fclose(module_file);
528         }
529     }
530
531   g_strfreev (files);
532   g_free (file_str);
533
534   dlloaded_engines = g_slist_reverse (dlloaded_engines);
535 }
536
537 static void
538 init_modules (void)
539 {
540   static gboolean init = FALSE;
541   int i;
542
543   if (init)
544     return;
545   else
546     init = TRUE;
547
548   /* Make sure that the type system is initialized */
549   g_type_init ();
550
551   for (i = 0; _pango_included_lang_modules[i].list; i++)
552     pango_module_register (&_pango_included_lang_modules[i]);
553   read_modules ();
554 }
555
556 static void
557 map_add_engine (PangoMapInfo    *info,
558                 PangoEnginePair *pair)
559 {
560   PangoMap *map = info->map;
561   int i;
562
563   for (i=0; i<pair->info.n_scripts; i++)
564     {
565       PangoScript script;
566       PangoMapEntry *entry;
567       gboolean is_exact = FALSE;
568
569       if (pair->info.scripts[i].langs)
570         {
571           if (pango_language_matches (info->language, pair->info.scripts[i].langs))
572             is_exact = TRUE;
573         }
574
575       script = pair->info.scripts[i].script;
576       if ((guint)script >= map->entries->len)
577         g_array_set_size (map->entries, script + 1);
578
579       entry = &g_array_index (map->entries, PangoMapEntry, script);
580
581       if (is_exact)
582         entry->exact = g_slist_prepend (entry->exact, pair);
583       else
584         entry->fallback = g_slist_prepend (entry->fallback, pair);
585     }
586 }
587
588 static void
589 map_add_engine_list (PangoMapInfo *info,
590                      GSList       *engines,
591                      const char   *engine_type,
592                      const char   *render_type)
593 {
594   GSList *tmp_list = engines;
595
596   while (tmp_list)
597     {
598       PangoEnginePair *pair = tmp_list->data;
599       tmp_list = tmp_list->next;
600
601       if (strcmp (pair->info.engine_type, engine_type) == 0 &&
602           strcmp (pair->info.render_type, render_type) == 0)
603         {
604           map_add_engine (info, pair);
605         }
606     }
607 }
608
609 static void
610 build_map (PangoMapInfo *info)
611 {
612   const char *engine_type = g_quark_to_string (info->engine_type_id);
613   const char *render_type = g_quark_to_string (info->render_type_id);
614
615   init_modules();
616
617   if (!dlloaded_engines && !registered_engines)
618     {
619       static gboolean no_module_warning = FALSE;
620       if (!no_module_warning)
621         {
622           gchar *filename = g_build_filename (pango_get_sysconf_subdirectory (),
623                                               "pango.modules",
624                                               NULL);
625           g_critical ("No modules found:\n"
626                       "No builtin or dynamically loaded modules were found.\n"
627                       "PangoFc will not work correctly.\n"
628                       "This probably means there was an error in the creation of:\n"
629                       "  '%s'\n"
630                       "You should create this file by running:\n"
631                       "  pango-querymodules > '%s'",
632                      filename,
633                      filename);
634           g_free (filename);
635
636           no_module_warning = TRUE;
637         }
638     }
639
640   info->map = g_slice_new (PangoMap);
641   info->map->entries = g_array_new (FALSE, TRUE, sizeof (PangoMapEntry));
642
643   map_add_engine_list (info, dlloaded_engines, engine_type, render_type);
644   map_add_engine_list (info, registered_engines, engine_type, render_type);
645 }
646
647 /**
648  * pango_map_get_engine:
649  * @map: a #PangoMap
650  * @script: a #PangoScript
651  *
652  * Returns the best engine listed in the map for a given script
653  *
654  * Return value: the best engine, if one is listed for the script,
655  *    or %NULL. The lookup may cause the engine to be loaded;
656  *    once an engine is loaded, it won't be unloaded. If multiple
657  *    engines are exact for the script, the choice of which is
658  *    returned is arbitrary.
659  **/
660 PangoEngine *
661 pango_map_get_engine (PangoMap   *map,
662                       PangoScript script)
663 {
664   PangoMapEntry *entry = NULL;
665   PangoMapEntry *common_entry = NULL;
666
667   if ((guint)script < map->entries->len)
668     entry = &g_array_index (map->entries, PangoMapEntry, script);
669
670   if (PANGO_SCRIPT_COMMON < map->entries->len)
671     common_entry = &g_array_index (map->entries, PangoMapEntry, PANGO_SCRIPT_COMMON);
672
673   if (entry && entry->exact)
674     return pango_engine_pair_get_engine (entry->exact->data);
675   else if (common_entry && common_entry->exact)
676     return pango_engine_pair_get_engine (common_entry->exact->data);
677   else if (entry && entry->fallback)
678     return pango_engine_pair_get_engine (entry->fallback->data);
679   else if (common_entry && common_entry->fallback)
680     return pango_engine_pair_get_engine (common_entry->fallback->data);
681   else
682     return NULL;
683 }
684
685 static void
686 append_engines (GSList **engine_list,
687                 GSList  *pair_list)
688 {
689   GSList *l;
690
691   for (l = pair_list; l; l = l->next)
692     {
693       PangoEngine *engine = pango_engine_pair_get_engine (l->data);
694       if (engine)
695         *engine_list = g_slist_append (*engine_list, engine);
696     }
697 }
698
699 /**
700  * pango_map_get_engines:
701  * @map: a #PangoMap
702  * @script: a #PangoScript
703  * @exact_engines: location to store list of engines that exactly
704  *  handle this script.
705  * @fallback_engines: location to store list of engines that approximately
706  *  handle this script.
707  *
708  * Finds engines in the map that handle the given script. The returned
709  * lists should be freed with g_slist_free, but the engines in the
710  * lists are owned by GLib and will be kept around permanently, so
711  * they should not be unref'ed.
712  *
713  * Since: 1.4
714  **/
715 void
716 pango_map_get_engines (PangoMap     *map,
717                        PangoScript   script,
718                        GSList      **exact_engines,
719                        GSList      **fallback_engines)
720 {
721   PangoMapEntry *entry = NULL;
722   PangoMapEntry *common_entry = NULL;
723
724   if ((guint)script < map->entries->len)
725     entry = &g_array_index (map->entries, PangoMapEntry, script);
726
727   if (PANGO_SCRIPT_COMMON < map->entries->len)
728     common_entry = &g_array_index (map->entries, PangoMapEntry, PANGO_SCRIPT_COMMON);
729
730   if (exact_engines)
731     {
732       *exact_engines = NULL;
733       if (entry && entry->exact)
734         append_engines (exact_engines, entry->exact);
735       else if (common_entry && common_entry->exact)
736         append_engines (exact_engines, common_entry->exact);
737     }
738
739   if (fallback_engines)
740     {
741       *fallback_engines = NULL;
742       if (entry && entry->fallback)
743         append_engines (fallback_engines, entry->fallback);
744       else if (common_entry && common_entry->fallback)
745         append_engines (fallback_engines, common_entry->fallback);
746     }
747 }
748
749 /**
750  * pango_module_register:
751  * @module: a #PangoIncludedModule
752  *
753  * Registers a statically linked module with Pango. The
754  * #PangoIncludedModule structure that is passed in contains the
755  * functions that would otherwise be loaded from a dynamically loaded
756  * module.
757  **/
758 void
759 pango_module_register (PangoIncludedModule *module)
760 {
761   GSList *tmp_list = NULL;
762
763   handle_included_module (module, &tmp_list);
764
765   registered_engines = g_slist_concat (registered_engines,
766                                        g_slist_reverse (tmp_list));
767 }