4 * Copyright (C) 1999 Red Hat Software
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.
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.
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.
25 #include "pango-engine.h"
26 #include "pango-utils.h"
28 #undef PANGO_DISABLE_DEPRECATED
31 /* No extra fields needed */
32 typedef PangoEngineShape BasicEngineX;
33 typedef PangoEngineShapeClass BasicEngineXClass ;
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;
42 typedef PangoGlyph (*ConvFunc) (CharCache *cache,
46 #define MAX_CHARSETS 32
48 #define SCRIPT_ENGINE_NAME "BasicScriptEngineX"
54 const char *x_charset;
58 struct _CharsetOrdering
61 char charsets[MAX_CHARSETS];
75 PangoXSubfont *subfonts;
82 CharsetOrdering *ordering;
83 MaskTable *mask_tables[256];
84 GIConv converters[MAX_CHARSETS];
85 PangoCoverage *coverage;
88 struct _CharCachePointer
94 static PangoGlyph conv_8bit (CharCache *cache,
97 static PangoGlyph conv_eucjp (CharCache *cache,
100 static PangoGlyph conv_16bit (CharCache *cache,
103 static PangoGlyph conv_ucs4 (CharCache *cache,
106 static PangoGlyph conv_16bit_MSB_on (CharCache *cache,
109 static PangoGlyph conv_gb18030_1 (CharCache *cache,
112 static PangoGlyph conv_euctw (CharCache *cache,
116 #include "tables-big.i"
118 static PangoEngineScriptInfo basic_scripts[] = {
119 { PANGO_SCRIPT_COMMON, "" },
122 static PangoEngineInfo script_engines[] = {
125 PANGO_ENGINE_TYPE_SHAPE,
127 basic_scripts, G_N_ELEMENTS(basic_scripts)
132 * X window system script engine portion
135 /* Structure of our cache:
137 * PangoFont => CharCachePointer ===\
139 * CharCachePointer ======> CharCache => CharsetOrdering
140 * | |======> MaskTable[0] => {subfonts,charset}[n_subfonts],
141 * | |======> MaskTable[1] => {subfonts,charset}[n_subfonts],
142 * | \======> MaskTable[...] => {subfonts,charset}[n_subfonts]
144 * CharCachePointer ======> CharCache => CharsetOrdering
145 * |======> MaskTable[0] => {subfonts,charset}[n_subfonts],
146 * |======> MaskTable[1] => {subfonts,charset}[n_subfonts],
147 * \======> MaskTable[...] => {subfonts,charset}[n_subfonts]
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.
154 char_cache_new (CharsetOrdering *ordering)
159 result = g_new0 (CharCache, 1);
161 result->ref_count = 1;
162 result->ordering = ordering;
163 for (i=0; i<MAX_CHARSETS; i++)
164 result->converters[i] = (GIConv)-1;
170 char_cache_free (CharCache *cache)
174 for (i=0; i<256; i++)
175 if (cache->mask_tables[i])
177 g_free (cache->mask_tables[i]->subfonts);
178 g_free (cache->mask_tables[i]->charsets);
180 g_free (cache->mask_tables[i]);
183 for (i=0; i<MAX_CHARSETS; i++)
184 if (cache->converters[i] != (GIConv)-1)
185 g_iconv_close (cache->converters[i]);
191 find_char (CharCache *cache, PangoFont *font, gunichar wc, const char *input)
194 MaskTable *mask_table;
201 case 0x2028: /* Line separator */
202 case 0x2029: /* Paragraph separator */
203 return PANGO_GET_UNKNOWN_GLYPH (wc);
207 if (wc >= G_N_ELEMENTS (char_masks))
210 mask_index = char_masks[wc];
212 if (cache->mask_tables[mask_index])
213 mask_table = cache->mask_tables[mask_index];
216 const char *charset_names[G_N_ELEMENTS(charsets)];
217 Charset *charsets_map[G_N_ELEMENTS(charsets)];
220 int *subfont_charsets;
222 mask_table = g_new (MaskTable, 1);
224 mask = char_mask_map[mask_index] | ENC_ISO_10646;
226 /* Find the character sets that are included in this mask
229 for (i=0; i<(int)G_N_ELEMENTS(charsets); i++)
231 int charset_index = cache->ordering->charsets[i];
233 if (mask & (1 << charset_index))
235 charset_names[n_charsets] = charsets[charset_index].x_charset;
236 charsets_map[n_charsets] = &charsets[charset_index];
242 mask_table->n_subfonts = pango_x_list_subfonts (font, (char**)(void*)charset_names, n_charsets, &mask_table->subfonts, &subfont_charsets);
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]];
248 g_free (subfont_charsets);
250 cache->mask_tables[mask_index] = mask_table;
253 for (i=0; i < mask_table->n_subfonts; i++)
259 charset = mask_table->charsets[i];
262 GIConv cd = cache->converters[charset->index];
264 if (charset->id && cd == (GIConv)-1)
266 cd = g_iconv_open (charset->id, "UTF-8");
267 if (cd == (GIConv)-1)
269 g_warning ("Could not load converter from %s to UTF-8", charset->id);
270 mask_table->charsets[i] = NULL;
274 cache->converters[charset->index] = cd;
277 index = (*charset->conv_func) (cache, cd, input);
278 glyph = PANGO_X_MAKE_GLYPH (mask_table->subfonts[i], index);
280 if (pango_x_has_glyph (font, glyph))
289 set_glyph (PangoFont *font, PangoGlyphString *glyphs, int i, int offset, PangoGlyph glyph)
291 PangoRectangle logical_rect;
293 glyphs->glyphs[i].glyph = glyph;
295 glyphs->glyphs[i].geometry.x_offset = 0;
296 glyphs->glyphs[i].geometry.y_offset = 0;
298 glyphs->log_clusters[i] = offset;
300 pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
301 glyphs->glyphs[i].geometry.width = logical_rect.width;
305 conv_8bit (CharCache *cache G_GNUC_UNUSED,
311 const char *inptr = input;
313 char *outptr = &outbuf;
314 size_t outbytesleft = 1;
316 inbytesleft = g_utf8_next_char (input) - input;
318 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
320 return (guchar)outbuf;
324 conv_eucjp (CharCache *cache G_GNUC_UNUSED,
330 const char *inptr = input;
332 char *outptr = outbuf;
333 size_t outbytesleft = 4;
335 inbytesleft = g_utf8_next_char (input) - input;
337 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
339 if ((guchar)outbuf[0] < 128)
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);
346 return ((guchar)outbuf[0] & 0x7f) * 256 + ((guchar)outbuf[1] & 0x7f);
350 conv_16bit (CharCache *cache G_GNUC_UNUSED,
356 const char *inptr = input;
358 char *outptr = outbuf;
359 size_t outbytesleft = 2;
361 inbytesleft = g_utf8_next_char (input) - input;
363 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
365 if ((guchar)outbuf[0] < 128)
368 return ((guchar)outbuf[0] & 0x7f) * 256 + ((guchar)outbuf[1] & 0x7f);
372 conv_16bit_MSB_on (CharCache *cache G_GNUC_UNUSED,
378 const char *inptr = input;
380 char *outptr = outbuf;
381 size_t outbytesleft = 2;
383 inbytesleft = g_utf8_next_char (input) - input;
385 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
387 if ((guchar)outbuf[0] < 128)
390 return (guchar)outbuf[0] * 256 + (guchar)outbuf[1];
394 conv_gb18030_1 (CharCache *cache G_GNUC_UNUSED,
400 const char *inptr = input;
402 char *outptr = outbuf;
403 size_t outbytesleft = 4;
406 inbytesleft = g_utf8_next_char (input) - input;
408 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
410 if ((guchar)outbuf[0] < 128)
413 return 12600 * ((guchar)outbuf[0] - 0x81) + 1260 * ((guchar)outbuf[1] - 0x30) + 10 * ((guchar)outbuf[2] - 0x81) + ((guchar)outbuf[3] - 0x30);
417 conv_euctw (CharCache *cache G_GNUC_UNUSED,
423 const char *inptr = input;
425 char *outptr = outbuf;
426 size_t outbytesleft = 4;
428 inbytesleft = g_utf8_next_char (input) - input;
430 g_iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
432 /* The first two bytes determine which page of CNS to use; we
433 * get this information from tables-big.i, so ignore them
435 if ((guchar)outbuf[0] < 128)
438 return ((guchar)outbuf[2] & 0x7f) * 256 + ((guchar)outbuf[3] & 0x7f);
442 conv_ucs4 (CharCache *cache G_GNUC_UNUSED,
443 GIConv cd G_GNUC_UNUSED,
446 return g_utf8_get_char (input);
450 swap_range (PangoGlyphString *glyphs, int start, int end)
454 for (i = start, j = end - 1; i < j; i++, j--)
456 PangoGlyphInfo glyph_info;
459 glyph_info = glyphs->glyphs[i];
460 glyphs->glyphs[i] = glyphs->glyphs[j];
461 glyphs->glyphs[j] = glyph_info;
463 log_cluster = glyphs->log_clusters[i];
464 glyphs->log_clusters[i] = glyphs->log_clusters[j];
465 glyphs->log_clusters[j] = log_cluster;
470 char_caches_free (GSList *caches)
472 GSList *tmp_list = caches;
475 CharCachePointer *pointer = tmp_list->data;
477 pointer->cache->ref_count--;
478 if (pointer->cache->ref_count == 0)
479 char_cache_free (pointer->cache);
482 tmp_list = tmp_list->next;
484 g_slist_free (caches);
487 static CharsetOrdering *
488 ordering_for_lang (PangoLanguage *lang)
492 for (i = 0; i < (int)G_N_ELEMENTS (charset_orderings) - 1; i++)
494 if (pango_language_matches (lang, charset_orderings[i].langs))
495 return &charset_orderings[i];
498 return &charset_orderings[i];
502 get_char_cache (PangoFont *font,
505 GQuark cache_id = g_quark_from_string ("basic-char-cache");
506 CharCache *cache = NULL;
507 CharCachePointer *pointer;
508 CharsetOrdering *ordering;
512 caches = g_object_get_qdata (G_OBJECT (font), cache_id);
516 pointer = tmp_list->data;
517 if (pointer->lang == lang)
518 return pointer->cache;
520 tmp_list = tmp_list->next;
523 ordering = ordering_for_lang (lang);
528 pointer = tmp_list->data;
529 if (pointer->cache->ordering == ordering)
531 cache = pointer->cache;
535 tmp_list = tmp_list->next;
539 cache = char_cache_new (ordering);
543 pointer = g_new (CharCachePointer, 1);
544 pointer->lang = lang;
545 pointer->cache = cache;
547 caches = g_slist_prepend (caches, pointer);
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);
557 basic_engine_shape (PangoEngineShape *engine G_GNUC_UNUSED,
561 const PangoAnalysis *analysis,
562 PangoGlyphString *glyphs)
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);
575 cache = get_char_cache (font, analysis->language);
577 n_chars = g_utf8_strlen (text, length);
578 pango_glyph_string_set_size (glyphs, n_chars);
581 for (i=0; i < n_chars; i++)
584 gunichar mirrored_ch;
589 wc = g_utf8_get_char (p);
592 if (analysis->level % 2)
593 if (pango_get_mirror_char (wc, &mirrored_ch))
597 g_unichar_to_utf8 (wc, buf);
601 if (wc == 0xa0) /* non-break-space */
605 g_unichar_to_utf8 (wc, buf);
609 if (pango_is_zero_width (wc))
611 set_glyph (font, glyphs, i, p - text, PANGO_GLYPH_EMPTY);
615 index = find_char (cache, font, wc, input);
618 set_glyph (font, glyphs, i, p - text, index);
620 if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK)
624 PangoRectangle logical_rect, ink_rect;
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];
631 /* Some heuristics to try to guess how overstrike glyphs are
632 * done and compensate
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;
641 set_glyph (font, glyphs, i, p - text, PANGO_GET_UNKNOWN_GLYPH (wc));
644 p = g_utf8_next_char (p);
647 /* Simple bidi support... may have separate modules later */
649 if (analysis->level % 2)
653 /* Swap all glyphs */
654 swap_range (glyphs, 0, n_chars);
656 /* Now reorder glyphs within each cluster back to LTR */
657 for (start=0; start<n_chars;)
660 while (end < n_chars &&
661 glyphs->log_clusters[end] == glyphs->log_clusters[start])
664 swap_range (glyphs, start, end);
670 static PangoCoverageLevel
671 basic_engine_covers (PangoEngineShape *engine G_GNUC_UNUSED,
676 CharCache *cache = get_char_cache (font, lang);
679 g_unichar_to_utf8 (wc, buf);
681 return find_char (cache, font, wc, buf) ? PANGO_COVERAGE_EXACT : PANGO_COVERAGE_NONE;
685 basic_engine_x_class_init (PangoEngineShapeClass *class)
687 class->covers = basic_engine_covers;
688 class->script_shape = basic_engine_shape;
691 PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineX, basic_engine_x,
692 basic_engine_x_class_init, NULL)
695 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
697 basic_engine_x_register_type (module);
701 PANGO_MODULE_ENTRY(exit) (void)
706 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
709 *engines = script_engines;
710 *n_engines = G_N_ELEMENTS (script_engines);
714 PANGO_MODULE_ENTRY(create) (const char *id)
716 if (!strcmp (id, SCRIPT_ENGINE_NAME))
717 return g_object_new (basic_engine_x_type, NULL);