diff options
author | Chookij Vanatham <chookij@src.gnome.org> | 2002-01-16 22:56:35 +0000 |
---|---|---|
committer | Chookij Vanatham <chookij@src.gnome.org> | 2002-01-16 22:56:35 +0000 |
commit | 807e62c50179007e9cc455ac34033a45c9a15247 (patch) | |
tree | 4853c5b1d560bb92904f572b2386f351108ef222 | |
parent | 774888236caa9a767d2c65c0b4e4d69233d52eba (diff) | |
download | pango-807e62c50179007e9cc455ac34033a45c9a15247.tar.gz |
bug#: 68350 pango module for xft and freetype2.
-rw-r--r-- | modules/hebrew/hebrew-fc.c | 262 | ||||
-rw-r--r-- | modules/hebrew/hebrew-ft2.c | 271 | ||||
-rw-r--r-- | modules/hebrew/hebrew-shaper.c | 439 | ||||
-rw-r--r-- | modules/hebrew/hebrew-shaper.h | 28 | ||||
-rw-r--r-- | modules/hebrew/hebrew-xft.c | 262 |
5 files changed, 1262 insertions, 0 deletions
diff --git a/modules/hebrew/hebrew-fc.c b/modules/hebrew/hebrew-fc.c new file mode 100644 index 00000000..365d34fb --- /dev/null +++ b/modules/hebrew/hebrew-fc.c @@ -0,0 +1,262 @@ +/* Pango + * hebrew-xft.h: + * + * Copyright (C) 2000 Red Hat Software + * Author: Owen Taylor <otaylor@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include "pangoxft.h" +#include "pango-engine.h" +#include "pango-utils.h" +#include "hebrew-shaper.h" + +#define MAX_CLUSTER_CHRS 20 + +static PangoEngineRange hebrew_ranges[] = { + /* Language characters */ + { 0x0591, 0x05f4, "*" }, /* Hebrew */ +}; + +static PangoEngineInfo script_engines[] = { + { + "HebrewScriptEngineXft", + PANGO_ENGINE_TYPE_SHAPE, + PANGO_RENDER_TYPE_XFT, + hebrew_ranges, G_N_ELEMENTS(hebrew_ranges) + } +}; + +static guint +get_glyph_num (FT_Face face, PangoFont *font, gunichar wc) +{ + int index = FT_Get_Char_Index (face, wc); + + if (index && index <= face->num_glyphs) + return index; + else + return 0; +} + +static void +get_cluster_glyphs(FT_Face face, + PangoFont *font, + gunichar cluster[], + gint cluster_size, + /* output */ + gint glyph_num[], + PangoGlyph glyph[], + gint widths[], + PangoRectangle ink_rects[]) +{ + int i; + for (i=0; i<cluster_size; i++) + { + PangoRectangle logical_rect; + glyph_num[i] = get_glyph_num(face, font, cluster[i]); + glyph[i] = glyph_num[i]; + + pango_font_get_glyph_extents (font, + glyph[i], &ink_rects[i], &logical_rect); + + /* Assign the base char width to the last character in the cluster */ + if (i==0) + { + widths[i] = 0; + widths[cluster_size-1] = logical_rect.width; + } + else if (i < cluster_size-1) + widths[i] = 0; + } +} + +static void +add_glyph (PangoGlyphString *glyphs, + gint cluster_start, + PangoGlyph glyph, + gboolean is_combining, + gint width, + gint x_offset, + gint y_offset + ) +{ + gint index = glyphs->num_glyphs; + + pango_glyph_string_set_size (glyphs, index + 1); + + glyphs->glyphs[index].glyph = glyph; + glyphs->glyphs[index].attr.is_cluster_start = is_combining ? 0 : 1; + + glyphs->log_clusters[index] = cluster_start; + + glyphs->glyphs[index].geometry.x_offset = x_offset; + glyphs->glyphs[index].geometry.y_offset = y_offset; + glyphs->glyphs[index].geometry.width = width; +} + +static void +add_cluster(PangoFont *font, + PangoGlyphString *glyphs, + int cluster_size, + int cluster_start, + int glyph_num[], + PangoGlyph glyph[], + int width[], + int x_offset[], + int y_offset[]) +{ + int i; + + for (i=0; i<cluster_size; i++) + { + add_glyph (glyphs, cluster_start, glyph[i], + i == 0 ? FALSE : TRUE, width[i], x_offset[i], y_offset[i]); + } +} + + +static void +hebrew_engine_shape (PangoFont *font, + const char *text, + gint length, + PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + const char *p; + const char *log_cluster; + gunichar cluster[MAX_CLUSTER_CHRS]; + gint cluster_size; + gint glyph_num[MAX_CLUSTER_CHRS]; + gint glyph_width[MAX_CLUSTER_CHRS], x_offset[MAX_CLUSTER_CHRS], y_offset[MAX_CLUSTER_CHRS]; + PangoRectangle ink_rects[MAX_CLUSTER_CHRS]; + PangoGlyph glyph[MAX_CLUSTER_CHRS]; + FT_Face face; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + g_return_if_fail (length >= 0); + g_return_if_fail (analysis != NULL); + + pango_glyph_string_set_size (glyphs, 0); + face = pango_xft_font_get_face (font); + g_assert (face); + + p = text; + while (p < text + length) + { + log_cluster = p; + p = hebrew_shaper_get_next_cluster (p, text + length - p, + /* output */ + cluster, &cluster_size); + get_cluster_glyphs(face, + font, + cluster, + cluster_size, + /* output */ + glyph_num, + glyph, + glyph_width, + ink_rects); + + /* Kern the glyphs! */ + hebrew_shaper_get_cluster_kerning(cluster, + cluster_size, + /* Input and output */ + ink_rects, + glyph_width, + /* output */ + x_offset, + y_offset); + + add_cluster(font, + glyphs, + cluster_size, + log_cluster - text, + glyph_num, + glyph, + glyph_width, + x_offset, + y_offset); + + } + + if (analysis->level % 2) + hebrew_shaper_bidi_reorder(glyphs); +} + +static PangoCoverage * +hebrew_engine_get_coverage (PangoFont *font, + PangoLanguage *lang) +{ + return pango_font_get_coverage (font, lang); +} + +static PangoEngine * +hebrew_engine_xft_new () +{ + PangoEngineShape *result; + + result = g_new (PangoEngineShape, 1); + + result->engine.id = PANGO_RENDER_TYPE_XFT; + result->engine.type = PANGO_ENGINE_TYPE_SHAPE; + result->engine.length = sizeof (result); + result->script_shape = hebrew_engine_shape; + result->get_coverage = hebrew_engine_get_coverage; + + return (PangoEngine *)result; +} + +/* The following three functions provide the public module API for + * Pango. If we are compiling it is a module, then we name the + * entry points script_engine_list, etc. But if we are compiling + * it for inclusion directly in Pango, then we need them to + * to have distinct names for this module, so we prepend + * _pango_hebrew_xft_ + */ +#ifdef XFT_MODULE_PREFIX +#define MODULE_ENTRY(func) _pango_hebrew_xft_##func +#else +#define MODULE_ENTRY(func) func +#endif + +/* List the engines contained within this module + */ +void +MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, gint *n_engines) +{ + *engines = script_engines; + *n_engines = G_N_ELEMENTS (script_engines); +} + +/* Load a particular engine given the ID for the engine + */ +PangoEngine * +MODULE_ENTRY(script_engine_load) (const char *id) +{ + if (!strcmp (id, "HebrewScriptEngineXft")) + return hebrew_engine_xft_new (); + else + return NULL; +} + +void +MODULE_ENTRY(script_engine_unload) (PangoEngine *engine) +{ +} diff --git a/modules/hebrew/hebrew-ft2.c b/modules/hebrew/hebrew-ft2.c new file mode 100644 index 00000000..35e949b0 --- /dev/null +++ b/modules/hebrew/hebrew-ft2.c @@ -0,0 +1,271 @@ +/* Pango + * hebrew-ft2.c: + * + * Copyright (C) 1999 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <string.h> + +#include "pango-layout.h" +#include "pango-engine.h" +#include "pangoft2.h" +#include "pango-utils.h" +#include "hebrew-shaper.h" + +#define SCRIPT_ENGINE_NAME "HebrewScriptEngineFT2" +#define MAX_CLUSTER_CHRS 20 + +static PangoEngineRange hebrew_ranges[] = { + /* Hebrew */ + { 0x0591, 0x05f4, "*" }, +}; + +static PangoEngineInfo script_engines[] = { + { + SCRIPT_ENGINE_NAME, + PANGO_ENGINE_TYPE_SHAPE, + PANGO_RENDER_TYPE_FT2, + hebrew_ranges, G_N_ELEMENTS(hebrew_ranges) + } +}; + +static gint n_script_engines = G_N_ELEMENTS (script_engines); + +/* + * FT2 system script engine portion + */ + +static PangoGlyph +get_glyph (PangoFont *font, + gunichar wc) +{ + FT_Face face; + FT_UInt index; + + face = pango_ft2_font_get_face (font); + index = FT_Get_Char_Index (face, wc); + if (index && index <= face->num_glyphs) + return index; + + return 0; +} + +/* This should be extended to support various encodings... */ +gint get_glyph_num(PangoFont *font, + gunichar uch) +{ + return uch; +} + +static void +get_cluster_glyphs(PangoFont *font, + gunichar cluster[], + gint cluster_size, + /* output */ + gint glyph_num[], + PangoGlyph glyph[], + gint widths[], + PangoRectangle ink_rects[]) +{ + int i; + for (i=0; i<cluster_size; i++) + { + PangoRectangle logical_rect; + glyph_num[i] = get_glyph_num(font, cluster[i]); + glyph[i] = get_glyph(font, glyph_num[i]); + + pango_font_get_glyph_extents (font, + glyph[i], &ink_rects[i], &logical_rect); + + /* Assign the base char width to the last character in the cluster */ + if (i==0) + { + widths[i] = 0; + widths[cluster_size-1] = logical_rect.width; + } + else if (i < cluster_size-1) + widths[i] = 0; + } +} + +static void +add_glyph (PangoGlyphString *glyphs, + gint cluster_start, + PangoGlyph glyph, + gboolean is_combining, + gint width, + gint x_offset, + gint y_offset + ) +{ + gint index = glyphs->num_glyphs; + + pango_glyph_string_set_size (glyphs, index + 1); + + glyphs->glyphs[index].glyph = glyph; + glyphs->glyphs[index].attr.is_cluster_start = is_combining ? 0 : 1; + + glyphs->log_clusters[index] = cluster_start; + + glyphs->glyphs[index].geometry.x_offset = x_offset; + glyphs->glyphs[index].geometry.y_offset = y_offset; + glyphs->glyphs[index].geometry.width = width; +} + +static void +add_cluster(PangoFont *font, + PangoGlyphString *glyphs, + int cluster_size, + int cluster_start, + int glyph_num[], + PangoGlyph glyph[], + int width[], + int x_offset[], + int y_offset[]) +{ + int i; + + for (i=0; i<cluster_size; i++) + { + add_glyph (glyphs, cluster_start, glyph[i], + i == 0 ? FALSE : TRUE, width[i], x_offset[i], y_offset[i]); + } +} + + +static void +hebrew_engine_shape (PangoFont *font, + const char *text, + gint length, + PangoAnalysis *analysis, + /* output */ + PangoGlyphString *glyphs) +{ + const char *p; + const char *log_cluster; + gunichar cluster[MAX_CLUSTER_CHRS]; + gint cluster_size; + gint glyph_num[MAX_CLUSTER_CHRS]; + gint glyph_width[MAX_CLUSTER_CHRS], x_offset[MAX_CLUSTER_CHRS], y_offset[MAX_CLUSTER_CHRS]; + PangoRectangle ink_rects[MAX_CLUSTER_CHRS]; + PangoGlyph glyph[MAX_CLUSTER_CHRS]; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + g_return_if_fail (length >= 0); + g_return_if_fail (analysis != NULL); + + pango_glyph_string_set_size (glyphs, 0); + + p = text; + while (p < text + length) + { + log_cluster = p; + p = hebrew_shaper_get_next_cluster (p, text + length - p, + /* output */ + cluster, &cluster_size); + get_cluster_glyphs(font, + cluster, + cluster_size, + /* output */ + glyph_num, + glyph, + glyph_width, + ink_rects); + + /* Kern the glyphs! */ + hebrew_shaper_get_cluster_kerning(cluster, + cluster_size, + /* Input and output */ + ink_rects, + glyph_width, + /* output */ + x_offset, + y_offset); + + add_cluster(font, + glyphs, + cluster_size, + log_cluster - text, + glyph_num, + glyph, + glyph_width, + x_offset, + y_offset); + + } + + if (analysis->level % 2) + hebrew_shaper_bidi_reorder(glyphs); +} + +static PangoCoverage * +hebrew_engine_get_coverage (PangoFont *font, + PangoLanguage *lang) +{ + return pango_font_get_coverage (font, lang); +} + +static PangoEngine * +hebrew_engine_ft2_new (void) +{ + PangoEngineShape *result; + + result = g_new (PangoEngineShape, 1); + + result->engine.id = SCRIPT_ENGINE_NAME; + result->engine.type = PANGO_ENGINE_TYPE_SHAPE; + result->engine.length = sizeof (result); + result->script_shape = hebrew_engine_shape; + result->get_coverage = hebrew_engine_get_coverage; + + return (PangoEngine *)result; +} + +/* The following three functions provide the public module API for + * Pango + */ +#ifdef FT2_MODULE_PREFIX +#define MODULE_ENTRY(func) _pango_hebrew_ft2_##func +#else +#define MODULE_ENTRY(func) func +#endif + +void +MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, + gint *n_engines) +{ + *engines = script_engines; + *n_engines = n_script_engines; +} + +PangoEngine * +MODULE_ENTRY(script_engine_load) (const char *id) +{ + if (!strcmp (id, SCRIPT_ENGINE_NAME)) + return hebrew_engine_ft2_new (); + else + return NULL; +} + +void +MODULE_ENTRY(script_engine_unload) (PangoEngine *engine) +{ +} + diff --git a/modules/hebrew/hebrew-shaper.c b/modules/hebrew/hebrew-shaper.c new file mode 100644 index 00000000..8915bf80 --- /dev/null +++ b/modules/hebrew/hebrew-shaper.c @@ -0,0 +1,439 @@ +/* Pango + * hebrew-shaper.c: + * + * Copyright (c) 2001 by Sun Microsystems, Inc. + * Author: Chookij Vanatham <Chookij.Vanatham@Eng.Sun.COM> + * + * Hebrew points positioning improvements 2001 + * Author: Dov Grobgeld <dov@imagic.weizmann.ac.il> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include "pango-engine.h" + +#define ucs2iso8859_8(wc) (unsigned int)((unsigned int)(wc) - 0x0590 + 0x10) +#define iso8859_8_2uni(c) ((gunichar)(c) - 0x10 + 0x0590) + +#define MAX_CLUSTER_CHRS 256 + +/* Define Hebrew character classes */ +#define _ND 0 +#define _SP 1 +#define _NS (1<<1) +#define _DA (1<<2) /* only for dagesh... */ + +#define NoDefine _ND +#define SpacingLetter _SP +#define NonSpacingPunc _NS + +/* Define Hebrew character types */ +#define __ND 0 +#define __SP 1 +#define __NS 2 +#define __DA 3 + +/* Unicode definitions needed in logics below... */ +#define UNI_ALEF 0x05D0 +#define UNI_BET 0x05D1 +#define UNI_GIMMEL 0x05d2 +#define UNI_DALED 0x05D3 +#define UNI_KAF 0x05DB +#define UNI_VAV 0x05D5 +#define UNI_YOD 0x05D9 +#define UNI_RESH 0x05E8 +#define UNI_LAMED 0x05DC +#define UNI_SHIN 0x05E9 +#define UNI_FINAL_PE 0x05E3 +#define UNI_PE 0x05E4 +#define UNI_TAV 0x05EA +#define UNI_SHIN_DOT 0x05C1 +#define UNI_SIN_DOT 0x05C2 +#define UNI_MAPIQ 0x05BC +#define UNI_SHEVA 0x05B0 +#define UNI_QAMATS 0x05B8 +#define UNI_HOLAM 0x05B9 +#define UNI_QUBUTS 0x05BB + + +/*====================================================================== +// In the tables below all Hebrew characters are categorized to +// one of the following four classes: +// +// non used entries Not defined (ND) +// accents, points Non spacing (NS) +// punctuation and characters Spacing characters (SP) +// dagesh "Dagesh" (DA) +//----------------------------------------------------------------------*/ +static const gint char_class_table[128] = { + /* 0, 1, 2, 3, 4, 5, 6, 7 */ + + /*00*/ _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND, + _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND, + + /*10*/ _ND, _NS, _NS, _NS, _NS, _NS, _NS, _NS, + _NS, _NS, _NS, _NS, _NS, _NS, _NS, _NS, + /*20*/ _NS, _NS, _ND, _NS, _NS, _NS, _NS, _NS, + _NS, _NS, _NS, _NS, _NS, _NS, _NS, _NS, + /*30*/ _NS, _NS, _NS, _NS, _NS, _NS, _NS, _NS, + _NS, _NS, _ND, _NS, _DA, _NS, _SP, _NS, + /*40*/ _SP, _NS, _NS, _SP, _NS, _ND, _ND, _ND, + _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND, + /*50*/ _SP, _SP, _SP, _SP, _SP, _SP, _SP, _SP, + _SP, _SP, _SP, _SP, _SP, _SP, _SP, _SP, + /*60*/ _SP, _SP, _SP, _SP, _SP, _SP, _SP, _SP, + _SP, _SP, _SP, _ND, _ND, _ND, _ND, _ND, + /*70*/ _SP, _SP, _SP, _SP, _SP, _ND, _ND, _ND, + _ND, _ND, _ND, _ND, _ND, _ND, _ND, _ND, +}; + +static const gint char_type_table[128] = { + /* 0, 1, 2, 3, 4, 5, 6, 7 */ + + /*00*/ __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND, + __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND, + + /*10*/ __ND, __NS, __NS, __NS, __NS, __NS, __NS, __NS, + __NS, __NS, __NS, __NS, __NS, __NS, __NS, __NS, + /*20*/ __NS, __NS, __ND, __NS, __NS, __NS, __NS, __NS, + __NS, __NS, __NS, __NS, __NS, __NS, __NS, __NS, + /*30*/ __NS, __NS, __NS, __NS, __NS, __NS, __NS, __NS, + __NS, __NS, __ND, __NS, __DA, __NS, __SP, __NS, + /*40*/ __SP, __NS, __NS, __SP, __NS, __ND, __ND, __ND, + __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND, + /*50*/ __SP, __SP, __SP, __SP, __SP, __SP, __SP, __SP, + __SP, __SP, __SP, __SP, __SP, __SP, __SP, __SP, + /*60*/ __SP, __SP, __SP, __SP, __SP, __SP, __SP, __SP, + __SP, __SP, __SP, __ND, __ND, __ND, __ND, __ND, + /*70*/ __SP, __SP, __SP, __SP, __SP, __ND, __ND, __ND, + __ND, __ND, __ND, __ND, __ND, __ND, __ND, __ND, +}; + +/*====================================================================== +// The following table answers the question whether two characters +// are composible or not. The decision is made by looking at the +// char_type_table values for the first character in a cluster +// vs a following charactrer. The only three combinations that +// are composible in Hebrew according to the table are: +// +// 1. a spacing character followed by non-spacing character +// 2. a spacing character followed by a dagesh. +// 3. a dagesh followed by a non-spacing character. +// +// Note that a spacing character may be followed by several non-spacing +// accents, as the decision is always made on the base character of +// a combination. +//----------------------------------------------------------------------*/ +static const gboolean compose_table[4][4] = { + /* Cn */ /* 0, 1, 2, 3, */ +/* Cn-1 00 */ { FALSE, FALSE, FALSE, FALSE }, + /* 10 */ { FALSE, FALSE, TRUE, TRUE }, + /* 20 */ { FALSE, FALSE, FALSE, FALSE }, + /* 30 */ { FALSE, FALSE, TRUE, FALSE }, +}; + +/* ISO 8859_8 Hebrew Font Layout. Does not include any accents. + */ +static const gint iso_8859_8_shape_table[128] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/* Unicode Hebrew Font Layout + */ +static const gint Unicode_shape_table[128] = { + /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* cantillation marks followed by accents */ + /* 10 */ 0x0000, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, + 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + /* 20 */ 0x05A0, 0x05A1, 0x0000, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, + 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + /* 30 */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + /* 40 */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + + /* Aleph-Tav, Yiddish ligatures, and punctuation */ + /* 50 */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + /* 60 */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 70 */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +#define is_char_class(wc, mask) (char_class_table[ucs2iso8859_8 ((wc))] & (mask)) +#define is_composible(cur_wc, nxt_wc) (compose_table[char_type_table[ucs2iso8859_8 (cur_wc)]]\ + [char_type_table[ucs2iso8859_8 (nxt_wc)]]) + + + +const char * +hebrew_shaper_get_next_cluster(const char *text, + gint length, + gunichar *cluster, + gint *num_chrs) +{ + const char *p; + gint n_chars = 0; + + p = text; + + while (p < text + length && n_chars < MAX_CLUSTER_CHRS) + { + gunichar current = g_utf8_get_char (p); + + if (n_chars == 0 || + is_composible ((gunichar)(cluster[0]), current) ) + { + cluster[n_chars++] = current; + p = g_utf8_next_char (p); + if (n_chars == 1 && + is_char_class(cluster[0], ~(NoDefine|SpacingLetter)) ) + break; + } + else + break; + } + + *num_chrs = n_chars; + return p; +} + +void +hebrew_shaper_get_cluster_kerning(gunichar *cluster, + gint cluster_length, + PangoRectangle ink_rect[], + + /* input and output */ + gint width[], + gint x_offset[], + gint y_offset[]) +{ + int i; + int base_ink_x_offset, base_ink_width, base_ink_height; + gunichar base_char = cluster[0]; + + x_offset[0] = 0; + y_offset[0] = 0; + + if (cluster_length == 1) + return; + + base_ink_x_offset = ink_rect[0].x; + base_ink_width = ink_rect[0].width; + base_ink_height = ink_rect[0].height; + + /* Do heuristics */ + for (i=1; i<cluster_length; i++) + { + int gl = cluster[i]; + x_offset[i] = 0; + y_offset[i] = 0; + + /* Check if it is a point */ + if (gl < 0x5B0 || gl >= 0x05D0) + continue; + + /* Center dot of VAV */ + if (gl == UNI_MAPIQ && base_char == UNI_VAV) + { + x_offset[i] = base_ink_x_offset - ink_rect[i].x; + + /* If VAV is a vertical bar without a roof, then we + need to make room for the dot by increasing the + cluster width. But how can I check if that is the + case?? + */ + /* This is wild, but it does the job of differentiating + between two M$ fonts... Base the decision on the + aspect ratio of the vav... + */ + if (base_ink_height > base_ink_width * 3.5) + { + int j; + double space = 0.7; + double kern = 0.5; + + for (j=0; j<i; j++) + { + x_offset[j] += ink_rect[0].width*(1+space-kern); + } + + width[cluster_length-1] += ink_rect[i].width*(1+space-kern); + x_offset[i] -= ink_rect[i].width*(kern); + } + } + + /* Dot over SHIN */ + else if (gl == UNI_SHIN_DOT && base_char == UNI_SHIN) + { + x_offset[i] = base_ink_x_offset + base_ink_width + - ink_rect[i].x - ink_rect[i].width; + } + + /* Dot over SIN */ + else if (gl == UNI_SIN_DOT && base_char == UNI_SHIN) + { + x_offset[i] = base_ink_x_offset - ink_rect[i].x; + } + + /* VOWEL DOT above to any other character than + SHIN or VAV should stick out a bit to the left. */ + else if ((gl == UNI_SIN_DOT || gl == UNI_HOLAM) + && base_char != UNI_SHIN && base_char != UNI_VAV) + { + x_offset[i] = base_ink_x_offset -ink_rect[i].x - ink_rect[i].width * 3/ 2; + } + + /* VOWELS under resh or vav are right aligned */ + else if ((base_char == UNI_VAV + || base_char == UNI_RESH + || base_char == UNI_YOD + || base_char == UNI_DALED + ) + && ((gl >= UNI_SHEVA && gl <= UNI_QAMATS) || + gl == UNI_QUBUTS)) + { + x_offset[i] = base_ink_x_offset + base_ink_width + - ink_rect[i].x - ink_rect[i].width; + } + + /* MAPIQ in PE or FINAL PE */ + else if (gl == UNI_MAPIQ + && (base_char == UNI_PE || base_char == UNI_FINAL_PE)) + { + x_offset[i]= base_ink_x_offset - ink_rect[i].x + + base_ink_width * 2/3 - ink_rect[i].width/2; + + /* Another option is to offset the MAPIQ in y... + glyphs->glyphs[cluster_start_idx+i].geometry.y_offset + -= base_ink_height/5; */ + } + + /* MAPIQ in SHIN should be moved a bit to the right */ + else if (gl == UNI_MAPIQ + && base_char == UNI_SHIN) + { + x_offset[i]= base_ink_x_offset - ink_rect[i].x + + base_ink_width * 3/5 - ink_rect[i].width/2; + } + + /* MAPIQ in YUD is right aligned */ + else if (gl == UNI_MAPIQ + && base_char == UNI_YOD) + { + x_offset[i]= base_ink_x_offset - ink_rect[i].x; + } + + /* VOWEL DOT next to any other character */ + else if ((gl == UNI_SIN_DOT || gl == UNI_HOLAM) + && (base_char != UNI_VAV)) + { + x_offset[i] = base_ink_x_offset -ink_rect[i].x; + } + + /* Move nikud of taf a bit ... */ + else if (base_char == UNI_TAV && gl == UNI_MAPIQ) + { + x_offset[i] = base_ink_x_offset - ink_rect[i].x + + base_ink_width * 5/8 - ink_rect[i].width/2; + } + + /* Move center dot of characters with a right stem and no + left stem. */ + else if (gl == UNI_MAPIQ && + (base_char == UNI_BET + || base_char == UNI_DALED + || base_char == UNI_KAF + || base_char == UNI_GIMMEL + )) + { + x_offset[i] = base_ink_x_offset - ink_rect[i].x + + base_ink_width * 3/8 - ink_rect[i].width/2; + } + + /* Center by default */ + else + { + x_offset[i] = base_ink_x_offset - ink_rect[i].x + + base_ink_width/2 - ink_rect[i].width/2; + } + } + +} + +void +hebrew_shaper_swap_range (PangoGlyphString *glyphs, + int start, + int end) +{ + int i, j; + + for (i = start, j = end - 1; i < j; i++, j--) + { + PangoGlyphInfo glyph_info; + gint log_cluster; + + glyph_info = glyphs->glyphs[i]; + glyphs->glyphs[i] = glyphs->glyphs[j]; + glyphs->glyphs[j] = glyph_info; + + log_cluster = glyphs->log_clusters[i]; + glyphs->log_clusters[i] = glyphs->log_clusters[j]; + glyphs->log_clusters[j] = log_cluster; + } +} + +void +hebrew_shaper_bidi_reorder(PangoGlyphString *glyphs) +{ + int start, end; + + /* Swap all glyphs */ + hebrew_shaper_swap_range (glyphs, 0, glyphs->num_glyphs); + + /* Now reorder glyphs within each cluster back to LTR */ + for (start = 0; start < glyphs->num_glyphs;) + { + end = start; + while (end < glyphs->num_glyphs && + glyphs->log_clusters[end] == glyphs->log_clusters[start]) + end++; + + hebrew_shaper_swap_range (glyphs, start, end); + start = end; + } +} diff --git a/modules/hebrew/hebrew-shaper.h b/modules/hebrew/hebrew-shaper.h new file mode 100644 index 00000000..6b590013 --- /dev/null +++ b/modules/hebrew/hebrew-shaper.h @@ -0,0 +1,28 @@ +#ifndef HEBREW_SHAPER_H +#define HEBREW_SHAPER_H + +char * +hebrew_shaper_get_next_cluster(const char *text, + gint length, + gunichar *cluster, + gint *num_chrs); + +void +hebrew_shaper_get_cluster_kerning(gunichar *cluster, + gint cluster_length, + PangoRectangle ink_rect[], + + /* input and output */ + gint width[], + gint x_offset[], + gint y_offset[]); + +void +hebrew_shaper_swap_range (PangoGlyphString *glyphs, + int start, + int end); + +void +hebrew_shaper_bidi_reorder(PangoGlyphString *glyphs); + +#endif diff --git a/modules/hebrew/hebrew-xft.c b/modules/hebrew/hebrew-xft.c new file mode 100644 index 00000000..365d34fb --- /dev/null +++ b/modules/hebrew/hebrew-xft.c @@ -0,0 +1,262 @@ +/* Pango + * hebrew-xft.h: + * + * Copyright (C) 2000 Red Hat Software + * Author: Owen Taylor <otaylor@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include "pangoxft.h" +#include "pango-engine.h" +#include "pango-utils.h" +#include "hebrew-shaper.h" + +#define MAX_CLUSTER_CHRS 20 + +static PangoEngineRange hebrew_ranges[] = { + /* Language characters */ + { 0x0591, 0x05f4, "*" }, /* Hebrew */ +}; + +static PangoEngineInfo script_engines[] = { + { + "HebrewScriptEngineXft", + PANGO_ENGINE_TYPE_SHAPE, + PANGO_RENDER_TYPE_XFT, + hebrew_ranges, G_N_ELEMENTS(hebrew_ranges) + } +}; + +static guint +get_glyph_num (FT_Face face, PangoFont *font, gunichar wc) +{ + int index = FT_Get_Char_Index (face, wc); + + if (index && index <= face->num_glyphs) + return index; + else + return 0; +} + +static void +get_cluster_glyphs(FT_Face face, + PangoFont *font, + gunichar cluster[], + gint cluster_size, + /* output */ + gint glyph_num[], + PangoGlyph glyph[], + gint widths[], + PangoRectangle ink_rects[]) +{ + int i; + for (i=0; i<cluster_size; i++) + { + PangoRectangle logical_rect; + glyph_num[i] = get_glyph_num(face, font, cluster[i]); + glyph[i] = glyph_num[i]; + + pango_font_get_glyph_extents (font, + glyph[i], &ink_rects[i], &logical_rect); + + /* Assign the base char width to the last character in the cluster */ + if (i==0) + { + widths[i] = 0; + widths[cluster_size-1] = logical_rect.width; + } + else if (i < cluster_size-1) + widths[i] = 0; + } +} + +static void +add_glyph (PangoGlyphString *glyphs, + gint cluster_start, + PangoGlyph glyph, + gboolean is_combining, + gint width, + gint x_offset, + gint y_offset + ) +{ + gint index = glyphs->num_glyphs; + + pango_glyph_string_set_size (glyphs, index + 1); + + glyphs->glyphs[index].glyph = glyph; + glyphs->glyphs[index].attr.is_cluster_start = is_combining ? 0 : 1; + + glyphs->log_clusters[index] = cluster_start; + + glyphs->glyphs[index].geometry.x_offset = x_offset; + glyphs->glyphs[index].geometry.y_offset = y_offset; + glyphs->glyphs[index].geometry.width = width; +} + +static void +add_cluster(PangoFont *font, + PangoGlyphString *glyphs, + int cluster_size, + int cluster_start, + int glyph_num[], + PangoGlyph glyph[], + int width[], + int x_offset[], + int y_offset[]) +{ + int i; + + for (i=0; i<cluster_size; i++) + { + add_glyph (glyphs, cluster_start, glyph[i], + i == 0 ? FALSE : TRUE, width[i], x_offset[i], y_offset[i]); + } +} + + +static void +hebrew_engine_shape (PangoFont *font, + const char *text, + gint length, + PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + const char *p; + const char *log_cluster; + gunichar cluster[MAX_CLUSTER_CHRS]; + gint cluster_size; + gint glyph_num[MAX_CLUSTER_CHRS]; + gint glyph_width[MAX_CLUSTER_CHRS], x_offset[MAX_CLUSTER_CHRS], y_offset[MAX_CLUSTER_CHRS]; + PangoRectangle ink_rects[MAX_CLUSTER_CHRS]; + PangoGlyph glyph[MAX_CLUSTER_CHRS]; + FT_Face face; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + g_return_if_fail (length >= 0); + g_return_if_fail (analysis != NULL); + + pango_glyph_string_set_size (glyphs, 0); + face = pango_xft_font_get_face (font); + g_assert (face); + + p = text; + while (p < text + length) + { + log_cluster = p; + p = hebrew_shaper_get_next_cluster (p, text + length - p, + /* output */ + cluster, &cluster_size); + get_cluster_glyphs(face, + font, + cluster, + cluster_size, + /* output */ + glyph_num, + glyph, + glyph_width, + ink_rects); + + /* Kern the glyphs! */ + hebrew_shaper_get_cluster_kerning(cluster, + cluster_size, + /* Input and output */ + ink_rects, + glyph_width, + /* output */ + x_offset, + y_offset); + + add_cluster(font, + glyphs, + cluster_size, + log_cluster - text, + glyph_num, + glyph, + glyph_width, + x_offset, + y_offset); + + } + + if (analysis->level % 2) + hebrew_shaper_bidi_reorder(glyphs); +} + +static PangoCoverage * +hebrew_engine_get_coverage (PangoFont *font, + PangoLanguage *lang) +{ + return pango_font_get_coverage (font, lang); +} + +static PangoEngine * +hebrew_engine_xft_new () +{ + PangoEngineShape *result; + + result = g_new (PangoEngineShape, 1); + + result->engine.id = PANGO_RENDER_TYPE_XFT; + result->engine.type = PANGO_ENGINE_TYPE_SHAPE; + result->engine.length = sizeof (result); + result->script_shape = hebrew_engine_shape; + result->get_coverage = hebrew_engine_get_coverage; + + return (PangoEngine *)result; +} + +/* The following three functions provide the public module API for + * Pango. If we are compiling it is a module, then we name the + * entry points script_engine_list, etc. But if we are compiling + * it for inclusion directly in Pango, then we need them to + * to have distinct names for this module, so we prepend + * _pango_hebrew_xft_ + */ +#ifdef XFT_MODULE_PREFIX +#define MODULE_ENTRY(func) _pango_hebrew_xft_##func +#else +#define MODULE_ENTRY(func) func +#endif + +/* List the engines contained within this module + */ +void +MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, gint *n_engines) +{ + *engines = script_engines; + *n_engines = G_N_ELEMENTS (script_engines); +} + +/* Load a particular engine given the ID for the engine + */ +PangoEngine * +MODULE_ENTRY(script_engine_load) (const char *id) +{ + if (!strcmp (id, "HebrewScriptEngineXft")) + return hebrew_engine_xft_new (); + else + return NULL; +} + +void +MODULE_ENTRY(script_engine_unload) (PangoEngine *engine) +{ +} |