Git init
[external/pango1.0.git] / modules / basic / basic-x.c
1 /* Pango
2  * basic.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 #include <glib.h>
24 #include <string.h>
25 #include "pango-engine.h"
26 #include "pango-utils.h"
27
28 #undef PANGO_DISABLE_DEPRECATED
29 #include "pangox.h"
30
31 /* No extra fields needed */
32 typedef PangoEngineShape      BasicEngineX;
33 typedef PangoEngineShapeClass BasicEngineXClass ;
34
35 typedef struct _CharRange CharRange;
36 typedef struct _Charset Charset;
37 typedef struct _CharsetOrdering CharsetOrdering;
38 typedef struct _CharCache CharCache;
39 typedef struct _CharCachePointer CharCachePointer;
40 typedef struct _MaskTable MaskTable;
41
42 typedef PangoGlyph (*ConvFunc) (CharCache   *cache,
43                                 GIConv       cd,
44                                 const gchar *input);
45
46 #define MAX_CHARSETS 32
47
48 #define SCRIPT_ENGINE_NAME "BasicScriptEngineX"
49
50 struct _Charset
51 {
52   int   index;
53   const char *id;
54   const char *x_charset;
55   ConvFunc conv_func;
56 };
57
58 struct _CharsetOrdering
59 {
60   const char *langs;
61   char charsets[MAX_CHARSETS];
62 };
63
64 struct _CharRange
65 {
66   guint16 start;
67   guint16 end;
68   guint16 charsets;
69 };
70
71 struct _MaskTable
72 {
73   int n_subfonts;
74
75   PangoXSubfont *subfonts;
76   Charset **charsets;
77 };
78
79 struct _CharCache
80 {
81   guint ref_count;
82   CharsetOrdering *ordering;
83   MaskTable *mask_tables[256];
84   GIConv converters[MAX_CHARSETS];
85   PangoCoverage *coverage;
86 };
87
88 struct _CharCachePointer
89 {
90   PangoLanguage *lang;
91   CharCache *cache;
92 };
93
94 static PangoGlyph conv_8bit (CharCache  *cache,
95                              GIConv      cd,
96                              const char *input);
97 static PangoGlyph conv_eucjp (CharCache  *cache,
98                               GIConv      cd,
99                               const char *input);
100 static PangoGlyph conv_16bit (CharCache  *cache,
101                               GIConv      cd,
102                               const char *input);
103 static PangoGlyph conv_ucs4 (CharCache  *cache,
104                              GIConv      cd,
105                              const char *input);
106 static PangoGlyph conv_16bit_MSB_on (CharCache  *cache,
107                               GIConv      cd,
108                               const char *input);
109 static PangoGlyph conv_gb18030_1 (CharCache  *cache,
110                               GIConv      cd,
111                               const char *input);
112 static PangoGlyph conv_euctw (CharCache  *cache,
113                               GIConv      cd,
114                               const char *input);
115
116 #include "tables-big.i"
117
118 static PangoEngineScriptInfo basic_scripts[] = {
119   { PANGO_SCRIPT_COMMON,  "" },
120 };
121
122 static PangoEngineInfo script_engines[] = {
123   {
124     SCRIPT_ENGINE_NAME,
125     PANGO_ENGINE_TYPE_SHAPE,
126     PANGO_RENDER_TYPE_X,
127     basic_scripts, G_N_ELEMENTS(basic_scripts)
128   }
129 };
130
131 /*
132  * X window system script engine portion
133  */
134
135 /* Structure of our cache:
136  *
137  * PangoFont => CharCachePointer  ===\
138  *                    |               \
139  *              CharCachePointer  ======> CharCache => CharsetOrdering
140  *                    |                       |======> MaskTable[0]    => {subfonts,charset}[n_subfonts],
141  *                    |                       |======> MaskTable[1]    => {subfonts,charset}[n_subfonts],
142  *                    |                       \======> MaskTable[...]  => {subfonts,charset}[n_subfonts]
143  *                    |
144  *              CharCachePointer  ======> CharCache => CharsetOrdering
145  *                                            |======> MaskTable[0]    => {subfonts,charset}[n_subfonts],
146  *                                            |======> MaskTable[1]    => {subfonts,charset}[n_subfonts],
147  *                                            \======> MaskTable[...]  => {subfonts,charset}[n_subfonts]
148  *
149  * A CharCache structure caches the lookup of what subfonts can be used for what characters for a pair of a Font
150  * and CharsetOrdering. Multiple language tags can share the same CharsetOrdering - the list of CharCachePointer
151  * structures that is attached to the font as object data provides lookups from language tag to charcache.
152  */
153 static CharCache *
154 char_cache_new (CharsetOrdering *ordering)
155 {
156   CharCache *result;
157   int i;
158
159   result = g_new0 (CharCache, 1);
160
161   result->ref_count = 1;
162   result->ordering = ordering;
163   for (i=0; i<MAX_CHARSETS; i++)
164     result->converters[i] = (GIConv)-1;
165
166   return result;
167 }
168
169 static void
170 char_cache_free (CharCache *cache)
171 {
172   int i;
173
174   for (i=0; i<256; i++)
175     if (cache->mask_tables[i])
176       {
177         g_free (cache->mask_tables[i]->subfonts);
178         g_free (cache->mask_tables[i]->charsets);
179
180         g_free (cache->mask_tables[i]);
181       }
182
183   for (i=0; i<MAX_CHARSETS; i++)
184     if (cache->converters[i] != (GIConv)-1)
185       g_iconv_close (cache->converters[i]);
186
187   g_free (cache);
188 }
189
190 static PangoGlyph
191 find_char (CharCache *cache, PangoFont *font, gunichar wc, const char *input)
192 {
193   int mask_index;
194   MaskTable *mask_table;
195   int i;
196
197   switch (wc)
198     {
199     case '\n':
200     case '\r':
201     case 0x2028: /* Line separator */
202     case 0x2029: /* Paragraph separator */
203       return PANGO_GET_UNKNOWN_GLYPH (wc);
204       break;
205     }
206
207   if (wc >= G_N_ELEMENTS (char_masks))
208     mask_index = 0;
209   else
210     mask_index = char_masks[wc];
211
212   if (cache->mask_tables[mask_index])
213     mask_table = cache->mask_tables[mask_index];
214   else
215     {
216       const char *charset_names[G_N_ELEMENTS(charsets)];
217       Charset *charsets_map[G_N_ELEMENTS(charsets)];
218       guint mask;
219       int n_charsets = 0;
220       int *subfont_charsets;
221
222       mask_table = g_new (MaskTable, 1);
223
224       mask = char_mask_map[mask_index] | ENC_ISO_10646;
225
226       /* Find the character sets that are included in this mask
227        */
228
229       for (i=0; i<(int)G_N_ELEMENTS(charsets); i++)
230         {
231           int charset_index = cache->ordering->charsets[i];
232
233           if (mask & (1 << charset_index))
234             {
235               charset_names[n_charsets] = charsets[charset_index].x_charset;
236               charsets_map[n_charsets] = &charsets[charset_index];
237
238               n_charsets++;
239             }
240         }
241
242       mask_table->n_subfonts = pango_x_list_subfonts (font, (char**)(void*)charset_names, n_charsets, &mask_table->subfonts, &subfont_charsets);
243
244       mask_table->charsets = g_new (Charset *, mask_table->n_subfonts);
245       for (i=0; i<mask_table->n_subfonts; i++)
246         mask_table->charsets[i] = charsets_map[subfont_charsets[i]];
247
248       g_free (subfont_charsets);
249
250       cache->mask_tables[mask_index] = mask_table;
251     }
252
253   for (i=0; i < mask_table->n_subfonts; i++)
254     {
255       PangoGlyph index;
256       PangoGlyph glyph;
257       Charset *charset;
258
259       charset = mask_table->charsets[i];
260       if (charset)
261         {
262           GIConv cd = cache->converters[charset->index];
263
264           if (charset->id && cd == (GIConv)-1)
265             {
266               cd = g_iconv_open (charset->id, "UTF-8");
267               if (cd == (GIConv)-1)
268                 {
269                   g_warning ("Could not load converter from %s to UTF-8", charset->id);
270                   mask_table->charsets[i] = NULL;
271                   continue;
272                 }
273
274               cache->converters[charset->index] = cd;
275             }
276
277           index = (*charset->conv_func) (cache, cd, input);
278           glyph = PANGO_X_MAKE_GLYPH (mask_table->subfonts[i], index);
279
280           if (pango_x_has_glyph (font, glyph))
281             return glyph;
282         }
283     }
284
285   return 0;
286 }
287
288 static void
289 set_glyph (PangoFont *font, PangoGlyphString *glyphs, int i, int offset, PangoGlyph glyph)
290 {
291   PangoRectangle logical_rect;
292
293   glyphs->glyphs[i].glyph = glyph;
294
295   glyphs->glyphs[i].geometry.x_offset = 0;
296   glyphs->glyphs[i].geometry.y_offset = 0;
297
298   glyphs->log_clusters[i] = offset;
299
300   pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
301   glyphs->glyphs[i].geometry.width = logical_rect.width;
302 }
303
304 static PangoGlyph
305 conv_8bit (CharCache  *cache G_GNUC_UNUSED,
306            GIConv      cd,
307            const char *input)
308 {
309   char outbuf;
310
311   const char *inptr = input;
312   size_t inbytesleft;
313   char *outptr = &outbuf;
314   size_t outbytesleft = 1;
315
316   inbytesleft = g_utf8_next_char (input) - input;
317
318   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
319
320   return (guchar)outbuf;
321 }
322
323 static PangoGlyph
324 conv_eucjp (CharCache  *cache G_GNUC_UNUSED,
325             GIConv      cd,
326             const char *input)
327 {
328   char outbuf[4];
329
330   const char *inptr = input;
331   size_t inbytesleft;
332   char *outptr = outbuf;
333   size_t outbytesleft = 4;
334
335   inbytesleft = g_utf8_next_char (input) - input;
336
337   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
338
339   if ((guchar)outbuf[0] < 128)
340     return outbuf[0];
341   else if ((guchar)outbuf[0] == 0x8e && outbytesleft == 2)
342     return ((guchar)outbuf[1]);
343   else if ((guchar)outbuf[0] == 0x8f && outbytesleft == 1)
344     return ((guchar)outbuf[1] & 0x7f) * 256 + ((guchar)outbuf[2] & 0x7f);
345   else
346     return ((guchar)outbuf[0] & 0x7f) * 256 + ((guchar)outbuf[1] & 0x7f);
347 }
348
349 static PangoGlyph
350 conv_16bit (CharCache  *cache G_GNUC_UNUSED,
351             GIConv      cd,
352             const char *input)
353 {
354   char outbuf[2];
355
356   const char *inptr = input;
357   size_t inbytesleft;
358   char *outptr = outbuf;
359   size_t outbytesleft = 2;
360
361   inbytesleft = g_utf8_next_char (input) - input;
362
363   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
364
365   if ((guchar)outbuf[0] < 128)
366     return outbuf[0];
367   else
368     return ((guchar)outbuf[0] & 0x7f) * 256 + ((guchar)outbuf[1] & 0x7f);
369 }
370
371 static PangoGlyph
372 conv_16bit_MSB_on (CharCache  *cache G_GNUC_UNUSED,
373                    GIConv      cd,
374                    const char *input)
375 {
376   char outbuf[2];
377
378   const char *inptr = input;
379   size_t inbytesleft;
380   char *outptr = outbuf;
381   size_t outbytesleft = 2;
382
383   inbytesleft = g_utf8_next_char (input) - input;
384
385   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
386
387   if ((guchar)outbuf[0] < 128)
388     return outbuf[0];
389   else
390     return (guchar)outbuf[0] * 256 + (guchar)outbuf[1];
391 }
392
393 static PangoGlyph
394 conv_gb18030_1 (CharCache  *cache G_GNUC_UNUSED,
395                 GIConv      cd,
396                 const char *input)
397 {
398   char outbuf[4];
399
400   const char *inptr = input;
401   size_t inbytesleft;
402   char *outptr = outbuf;
403   size_t outbytesleft = 4;
404
405
406   inbytesleft = g_utf8_next_char (input) - input;
407
408   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
409
410   if ((guchar)outbuf[0] < 128)
411     return outbuf[0];
412   else
413     return  12600 * ((guchar)outbuf[0] - 0x81) + 1260 * ((guchar)outbuf[1] - 0x30) + 10 * ((guchar)outbuf[2] - 0x81) + ((guchar)outbuf[3] - 0x30);
414 }
415
416 static PangoGlyph
417 conv_euctw (CharCache  *cache G_GNUC_UNUSED,
418             GIConv      cd,
419             const char *input)
420 {
421   char outbuf[4];
422
423   const char *inptr = input;
424   size_t inbytesleft;
425   char *outptr = outbuf;
426   size_t outbytesleft = 4;
427
428   inbytesleft = g_utf8_next_char (input) - input;
429
430   g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
431
432   /* The first two bytes determine which page of CNS to use; we
433    * get this information from tables-big.i, so ignore them
434    */
435   if ((guchar)outbuf[0] < 128)
436     return outbuf[0];
437   else
438     return ((guchar)outbuf[2] & 0x7f) * 256 + ((guchar)outbuf[3] & 0x7f);
439 }
440
441 static PangoGlyph
442 conv_ucs4 (CharCache  *cache G_GNUC_UNUSED,
443            GIConv      cd G_GNUC_UNUSED,
444            const char *input)
445 {
446   return g_utf8_get_char (input);
447 }
448
449 static void
450 swap_range (PangoGlyphString *glyphs, int start, int end)
451 {
452   int i, j;
453
454   for (i = start, j = end - 1; i < j; i++, j--)
455     {
456       PangoGlyphInfo glyph_info;
457       gint log_cluster;
458
459       glyph_info = glyphs->glyphs[i];
460       glyphs->glyphs[i] = glyphs->glyphs[j];
461       glyphs->glyphs[j] = glyph_info;
462
463       log_cluster = glyphs->log_clusters[i];
464       glyphs->log_clusters[i] = glyphs->log_clusters[j];
465       glyphs->log_clusters[j] = log_cluster;
466     }
467 }
468
469 static void
470 char_caches_free (GSList *caches)
471 {
472   GSList *tmp_list = caches;
473   while (tmp_list)
474     {
475       CharCachePointer *pointer = tmp_list->data;
476
477       pointer->cache->ref_count--;
478       if (pointer->cache->ref_count == 0)
479         char_cache_free (pointer->cache);
480       g_free (pointer);
481
482       tmp_list = tmp_list->next;
483     }
484   g_slist_free (caches);
485 }
486
487 static CharsetOrdering *
488 ordering_for_lang (PangoLanguage *lang)
489 {
490   int i;
491
492   for (i = 0; i < (int)G_N_ELEMENTS (charset_orderings) - 1; i++)
493     {
494       if (pango_language_matches (lang, charset_orderings[i].langs))
495         return &charset_orderings[i];
496     }
497
498   return &charset_orderings[i];
499 }
500
501 static CharCache *
502 get_char_cache (PangoFont     *font,
503                 PangoLanguage *lang)
504 {
505   GQuark cache_id = g_quark_from_string ("basic-char-cache");
506   CharCache *cache = NULL;
507   CharCachePointer *pointer;
508   CharsetOrdering *ordering;
509   GSList *caches;
510   GSList *tmp_list;
511
512   caches = g_object_get_qdata (G_OBJECT (font), cache_id);
513   tmp_list = caches;
514   while (tmp_list)
515     {
516       pointer = tmp_list->data;
517       if (pointer->lang == lang)
518         return pointer->cache;
519
520       tmp_list = tmp_list->next;
521     }
522
523   ordering = ordering_for_lang (lang);
524
525   tmp_list = caches;
526   while (tmp_list)
527     {
528       pointer = tmp_list->data;
529       if (pointer->cache->ordering == ordering)
530         {
531           cache = pointer->cache;
532           break;
533         }
534
535       tmp_list = tmp_list->next;
536     }
537
538   if (!cache)
539     cache = char_cache_new (ordering);
540   else
541     cache->ref_count++;
542
543   pointer = g_new (CharCachePointer, 1);
544   pointer->lang = lang;
545   pointer->cache = cache;
546
547   caches = g_slist_prepend (caches, pointer);
548
549   g_object_steal_qdata (G_OBJECT (font), cache_id);
550   g_object_set_qdata_full (G_OBJECT (font), cache_id,
551                            caches, (GDestroyNotify)char_caches_free);
552
553   return cache;
554 }
555
556 static void
557 basic_engine_shape (PangoEngineShape *engine G_GNUC_UNUSED,
558                     PangoFont        *font,
559                     const char       *text,
560                     gint              length,
561                     const PangoAnalysis *analysis,
562                     PangoGlyphString *glyphs)
563 {
564   int n_chars;
565   int i;
566   const char *p;
567
568   CharCache *cache;
569
570   g_return_if_fail (font != NULL);
571   g_return_if_fail (text != NULL);
572   g_return_if_fail (length >= 0);
573   g_return_if_fail (analysis != NULL);
574
575   cache = get_char_cache (font, analysis->language);
576
577   n_chars = g_utf8_strlen (text, length);
578   pango_glyph_string_set_size (glyphs, n_chars);
579
580   p = text;
581   for (i=0; i < n_chars; i++)
582     {
583       gunichar wc;
584       gunichar mirrored_ch;
585       PangoGlyph index;
586       char buf[6];
587       const char *input;
588
589       wc = g_utf8_get_char (p);
590
591       input = p;
592       if (analysis->level % 2)
593         if (pango_get_mirror_char (wc, &mirrored_ch))
594           {
595             wc = mirrored_ch;
596
597             g_unichar_to_utf8 (wc, buf);
598             input = buf;
599           }
600
601       if (wc == 0xa0)   /* non-break-space */
602         {
603           wc = 0x20;
604
605           g_unichar_to_utf8 (wc, buf);
606           input = buf;
607         }
608
609       if (pango_is_zero_width (wc))
610         {
611           set_glyph (font, glyphs, i, p - text, PANGO_GLYPH_EMPTY);
612         }
613       else
614         {
615           index = find_char (cache, font, wc, input);
616           if (index)
617             {
618               set_glyph (font, glyphs, i, p - text, index);
619
620               if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK)
621                 {
622                   if (i > 0)
623                     {
624                       PangoRectangle logical_rect, ink_rect;
625
626                       glyphs->glyphs[i].geometry.width = MAX (glyphs->glyphs[i-1].geometry.width,
627                                                               glyphs->glyphs[i].geometry.width);
628                       glyphs->glyphs[i-1].geometry.width = 0;
629                       glyphs->log_clusters[i] = glyphs->log_clusters[i-1];
630
631                       /* Some heuristics to try to guess how overstrike glyphs are
632                        * done and compensate
633                        */
634                       pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, &ink_rect, &logical_rect);
635                       if (logical_rect.width == 0 && ink_rect.x == 0)
636                         glyphs->glyphs[i].geometry.x_offset = (glyphs->glyphs[i].geometry.width - ink_rect.width) / 2;
637                     }
638                 }
639             }
640           else
641             set_glyph (font, glyphs, i, p - text, PANGO_GET_UNKNOWN_GLYPH (wc));
642         }
643
644       p = g_utf8_next_char (p);
645     }
646
647   /* Simple bidi support... may have separate modules later */
648
649   if (analysis->level % 2)
650     {
651       int start, end;
652
653       /* Swap all glyphs */
654       swap_range (glyphs, 0, n_chars);
655
656       /* Now reorder glyphs within each cluster back to LTR */
657       for (start=0; start<n_chars;)
658         {
659           end = start;
660           while (end < n_chars &&
661                  glyphs->log_clusters[end] == glyphs->log_clusters[start])
662             end++;
663
664           swap_range (glyphs, start, end);
665           start = end;
666         }
667     }
668 }
669
670 static PangoCoverageLevel
671 basic_engine_covers (PangoEngineShape *engine G_GNUC_UNUSED,
672                      PangoFont        *font,
673                      PangoLanguage    *lang,
674                      gunichar          wc)
675 {
676   CharCache *cache = get_char_cache (font, lang);
677   char buf[6];
678
679   g_unichar_to_utf8 (wc, buf);
680
681   return find_char (cache, font, wc, buf) ? PANGO_COVERAGE_EXACT : PANGO_COVERAGE_NONE;
682 }
683
684 static void
685 basic_engine_x_class_init (PangoEngineShapeClass *class)
686 {
687   class->covers = basic_engine_covers;
688   class->script_shape = basic_engine_shape;
689 }
690
691 PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineX, basic_engine_x,
692                                 basic_engine_x_class_init, NULL)
693
694 void
695 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
696 {
697   basic_engine_x_register_type (module);
698 }
699
700 void
701 PANGO_MODULE_ENTRY(exit) (void)
702 {
703 }
704
705 void
706 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
707                           int              *n_engines)
708 {
709   *engines = script_engines;
710   *n_engines = G_N_ELEMENTS (script_engines);
711 }
712
713 PangoEngine *
714 PANGO_MODULE_ENTRY(create) (const char *id)
715 {
716   if (!strcmp (id, SCRIPT_ENGINE_NAME))
717     return g_object_new (basic_engine_x_type, NULL);
718   else
719     return NULL;
720 }