2 * hangul-fc.c: Hangul shaper for FreeType based backends
4 * Copyright (C) 2002-2006 Changwoo Ryu
5 * Author: Changwoo Ryu <cwryu@debian.org>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
26 #include "pango-engine.h"
27 #include "pango-utils.h"
28 #include "pangofc-font.h"
30 #include "hangul-defs.h"
31 #include "tables-jamos.i"
33 /* No extra fields needed */
34 typedef PangoEngineShape HangulEngineFc;
35 typedef PangoEngineShapeClass HangulEngineFcClass ;
37 #define SCRIPT_ENGINE_NAME "HangulScriptEngineFc"
38 #define RENDER_TYPE PANGO_RENDER_TYPE_FC
40 static PangoEngineScriptInfo hangul_scripts[] = {
41 { PANGO_SCRIPT_HANGUL, "*" }
44 static PangoEngineInfo script_engines[] = {
47 PANGO_ENGINE_TYPE_SHAPE,
49 hangul_scripts, G_N_ELEMENTS(hangul_scripts)
54 set_glyph (PangoFont *font, PangoGlyphString *glyphs, int i, int offset, PangoGlyph glyph)
56 PangoRectangle logical_rect;
58 glyphs->glyphs[i].glyph = glyph;
59 glyphs->glyphs[i].geometry.x_offset = 0;
60 glyphs->glyphs[i].geometry.y_offset = 0;
61 glyphs->log_clusters[i] = offset;
63 pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
64 glyphs->glyphs[i].geometry.width = logical_rect.width;
67 /* Add a Hangul tone mark glyph in a glyph string.
68 * Non-spacing glyph works pretty much automatically.
69 * Spacing-glyph takes some care:
70 * 1. Make a room for a tone mark at the beginning(leftmost end) of a cluster
72 * 2. Adjust x_offset so that it is drawn to the left of a cluster.
73 * 3. Set the logical width to zero.
77 set_glyph_tone (PangoFont *font, PangoGlyphString *glyphs, int i,
78 int offset, PangoGlyph glyph)
80 PangoRectangle logical_rect, ink_rect;
81 PangoRectangle logical_rect_cluster;
83 glyphs->glyphs[i].glyph = glyph;
84 glyphs->glyphs[i].geometry.y_offset = 0;
85 glyphs->log_clusters[i] = offset;
87 pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph,
88 &ink_rect, &logical_rect);
90 /* tone mark is not the first in a glyph string. We have info. on the
91 * preceding glyphs in a glyph string
95 /* search for the beg. of the preceding cluster */
96 while (j >= 0 && glyphs->log_clusters[j] == glyphs->log_clusters[i - 1])
99 /* In .._extents_range(...,start,end,...), to my surprise start is
100 * inclusive but end is exclusive !!
102 pango_glyph_string_extents_range (glyphs, j + 1, i, font,
103 NULL, &logical_rect_cluster);
105 /* logical_rect_cluster.width is all the offset we need so that the
106 * inherent x_offset in the glyph (ink_rect.x) should be canceled out.
108 glyphs->glyphs[i].geometry.x_offset = - logical_rect_cluster.width
112 /* make an additional room for a tone mark if it has a spacing glyph
113 * because that's likely to be an indication that glyphs for other
114 * characters in the font are not designed for combining with tone marks.
116 if (logical_rect.width)
118 glyphs->glyphs[i].geometry.x_offset -= ink_rect.width;
119 glyphs->glyphs[j + 1].geometry.width += ink_rect.width;
120 glyphs->glyphs[j + 1].geometry.x_offset += ink_rect.width;
124 glyphs->glyphs[i].geometry.width = 0;
128 #define find_char(font,wc) \
129 pango_fc_font_get_glyph((PangoFcFont *)font, wc)
132 render_tone (PangoFont *font, gunichar tone, PangoGlyphString *glyphs,
133 int *n_glyphs, int cluster_offset)
137 index = find_char (font, tone);
138 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
141 set_glyph_tone (font, glyphs, *n_glyphs, cluster_offset, index);
145 /* fall back : HTONE1(0x302e) => middle-dot, HTONE2(0x302f) => colon */
146 index = find_char (font, tone == HTONE1 ? 0x00b7 : 0x003a);
149 set_glyph_tone (font, glyphs, *n_glyphs, cluster_offset, index);
152 set_glyph (font, glyphs, *n_glyphs, cluster_offset,
153 PANGO_GET_UNKNOWN_GLYPH (tone));
158 /* This is a fallback for when we get a tone mark not preceded
162 render_isolated_tone (PangoFont *font, gunichar tone, PangoGlyphString *glyphs,
163 int *n_glyphs, int cluster_offset)
165 #if 0 /* FIXME: what kind of hack is it? it draws dummy glyphs. */
166 /* Find a base character to render the mark on
168 int index = find_char (font, 0x25cc); /* DOTTED CIRCLE */
170 index = find_char (font, 0x25cb); /* WHITE CIRCLE, in KSC-5601 */
172 index = find_char (font, ' '); /* Space */
173 if (!index) /* Unknown glyph box with 0000 in it */
174 index = find_char (font, PANGO_GET_UNKNOWN_GLYPH (0));
176 /* Add the base character
178 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
179 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
185 render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
189 render_syllable (PangoFont *font, const char *str, int length,
190 PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset)
192 int n_prev_glyphs = *n_glyphs;
194 gunichar wc = 0, tone = 0, text[4];
195 int i, j, composed = 0;
198 /* Normalize it only when the entire sequence is equivalent to a
199 * precomposed syllable. It's usually better than prefix
200 * normalization both for poor-featured fonts and for smart fonts.
201 * I have seen no smart font which can render S+T as a syllable
205 if (length == 3 || length == 4)
208 text[0] = g_utf8_get_char(p);
209 p = g_utf8_next_char(p);
210 text[1] = g_utf8_get_char(p);
211 p = g_utf8_next_char(p);
212 text[2] = g_utf8_get_char(p);
214 if (length == 4 && !IS_M(g_utf8_get_char(g_utf8_next_char(p))))
215 goto lvt_out; /* draw the tone mark later */
217 if (IS_L_S(text[0]) && IS_V_S(text[1]) && IS_T_S(text[2]))
220 wc = S_FROM_LVT(text[0], text[1], text[2]);
221 str = g_utf8_next_char(p);
227 if (length == 2 || length == 3)
230 text[0] = g_utf8_get_char(p);
231 p = g_utf8_next_char(p);
232 text[1] = g_utf8_get_char(p);
234 if (length == 3 && !IS_M(g_utf8_get_char(g_utf8_next_char(p))))
235 goto lv_out; /* draw the tone mark later */
236 if (IS_L_S(text[0]) && IS_V_S(text[1]))
239 wc = S_FROM_LV(text[0], text[1]);
240 str = g_utf8_next_char(p);
242 else if (IS_S(text[0] && !S_HAS_T(text[0]) && IS_T_S(text[1])))
245 wc = text[0] + (text[1] - TBASE);
246 str = g_utf8_next_char(p);
255 index = find_char (font, wc);
256 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
258 set_glyph (font, glyphs, *n_glyphs, cluster_offset,
259 PANGO_GET_UNKNOWN_GLYPH (wc));
261 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
266 /* Render the remaining text as uncomposed forms as a fallback. */
267 for (i = 0; i < length; i++, str = g_utf8_next_char(str))
272 wc = g_utf8_get_char(str);
274 if (wc == LFILL || wc == VFILL)
287 text[0] = L_FROM_S(wc);
288 text[1] = V_FROM_S(wc);
291 text[2] = T_FROM_S(wc);
297 for (j = 0; j < composed; j++)
299 index = find_char (font, text[j]);
302 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
303 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
307 goto decompose_cancel;
313 /* The font doesn't have jamos. Cancel it. */
315 pango_glyph_string_set_size (glyphs, *n_glyphs);
318 index = find_char (font, wc);
321 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
322 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
328 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
329 set_glyph (font, glyphs, *n_glyphs, cluster_offset,
330 PANGO_GET_UNKNOWN_GLYPH (wc));
335 /* This font has no glyphs on the Hangul Jamo area! Find a
336 fallback from the Hangul Compatibility Jamo area. */
339 for (j = 0; j < 3 && (__jamo_to_ksc5601[jindex][j] != 0); j++)
341 wc = __jamo_to_ksc5601[jindex][j] - KSC_JAMOBASE + UNI_JAMOBASE;
342 index = (wc >= 0x3131) ? find_char (font, wc) : 0;
343 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
347 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
348 set_glyph (font, glyphs, *n_glyphs, cluster_offset,
349 PANGO_GET_UNKNOWN_GLYPH (text[i]));
354 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
358 if (n_prev_glyphs == *n_glyphs)
360 index = find_char (font, 0x3164); /* U+3164 HANGUL FILLER */
361 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
363 set_glyph (font, glyphs, *n_glyphs, cluster_offset,
364 PANGO_GET_UNKNOWN_GLYPH (index));
366 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
367 glyphs->log_clusters[*n_glyphs] = cluster_offset;
371 render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
375 render_basic (PangoFont *font, gunichar wc,
376 PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset)
380 if (wc == 0xa0) /* non-break-space */
383 pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
385 if (pango_is_zero_width (wc))
386 set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GLYPH_EMPTY);
389 index = find_char (font, wc);
391 set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
393 set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GET_UNKNOWN_GLYPH (wc));
399 hangul_engine_shape (PangoEngineShape *engine G_GNUC_UNUSED,
403 const PangoAnalysis *analysis G_GNUC_UNUSED,
404 PangoGlyphString *glyphs)
406 int n_chars = g_utf8_strlen (text, length);
409 const char *p, *start;
418 for (i = 0; i < n_chars; i++)
422 wc = g_utf8_get_char (p);
424 /* Check syllable boundaries. */
425 if (n_jamos && IS_BOUNDARY (prev, wc))
427 if (n_jamos == 1 && IS_S (prev))
428 /* common case which the most people use */
429 render_basic (font, prev, glyphs, &n_glyphs, start - text);
431 /* possibly complex composition */
432 render_syllable (font, start, n_jamos, glyphs,
433 &n_glyphs, start - text);
442 render_basic (font, wc, glyphs, &n_glyphs, start - text);
443 start = g_utf8_next_char (p);
445 else if (IS_M (wc) && !n_jamos)
447 /* Tone mark not following syllable */
448 render_isolated_tone (font, wc, glyphs, &n_glyphs, start - text);
449 start = g_utf8_next_char (p);
453 p = g_utf8_next_char (p);
456 if (n_jamos == 1 && IS_S (prev))
457 render_basic (font, prev, glyphs, &n_glyphs, start - text);
458 else if (n_jamos > 0)
459 render_syllable (font, start, n_jamos, glyphs, &n_glyphs,
464 hangul_engine_fc_class_init (PangoEngineShapeClass *class)
466 class->script_shape = hangul_engine_shape;
469 PANGO_ENGINE_SHAPE_DEFINE_TYPE (HangulEngineFc, hangul_engine_fc,
470 hangul_engine_fc_class_init, NULL)
473 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
475 hangul_engine_fc_register_type (module);
479 PANGO_MODULE_ENTRY(exit) (void)
484 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
487 *engines = script_engines;
488 *n_engines = G_N_ELEMENTS (script_engines);
492 PANGO_MODULE_ENTRY(create) (const char *id)
494 if (!strcmp (id, SCRIPT_ENGINE_NAME))
495 return g_object_new (hangul_engine_fc_type, NULL);