diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2014-07-27 18:11:16 -0400 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2015-04-04 16:13:07 -0700 |
commit | 14c11dd96e1547aaede1bb05a0243f5b0b53186e (patch) | |
tree | d9af93737a52e46e8f9b27eefc86918aed30e4a6 /pango | |
parent | cfe9ce995d7e12a3e3f64c923123337d5dba25b0 (diff) | |
download | pango-14c11dd96e1547aaede1bb05a0243f5b0b53186e.tar.gz |
Move shapers from modules/basic/ into pango/
Note wired up yet. Doesn't build.
Diffstat (limited to 'pango')
-rw-r--r-- | pango/Makefile.am | 11 | ||||
-rw-r--r-- | pango/pangocoretext-shape.c | 546 | ||||
-rw-r--r-- | pango/pangofc-shape.c | 483 | ||||
-rw-r--r-- | pango/pangowin32-shape.c | 877 |
4 files changed, 1913 insertions, 4 deletions
diff --git a/pango/Makefile.am b/pango/Makefile.am index 3e22ae86..d1561e23 100644 --- a/pango/Makefile.am +++ b/pango/Makefile.am @@ -183,6 +183,7 @@ pangoft2_public_sources = \ pangofc-font.c \ pangofc-fontmap.c \ pangofc-decoder.c \ + pangofc-shape.c \ pangoft2.c libpangoft2_1_0_la_LDFLAGS = $(LIBRARY_LIBTOOL_OPTIONS) @@ -321,8 +322,8 @@ pangocairo-win32-res.o: pangocairo.rc $(AM_V_GEN) $(WINDRES) $< $@ -libpangocairo_1_0_la_LIBADD += libpangowin32-$(PANGO_API_VERSION).la $(WIN32_LIBS) -libpangocairo_1_0_la_DEPENDENCIES += libpangowin32-$(PANGO_API_VERSION).la +libpangocairo_1_0_la_LIBADD += libpangowin32-$(PANGO_API_VERSION).la -lgdi32 +libpangocairo_1_0_la_DEPENDENCIES += libpangowin32-$(PANGO_API_VERSION).la libpangocairo_1_0_la_SOURCES += pangocairo-win32font.c pangocairo-win32fontmap.c pangocairo-win32.h endif @@ -347,6 +348,7 @@ libpangocairo_1_0_la_SOURCES += \ pangocoretext.c \ pangocoretext-private.h \ pangocoretext-fontmap.c \ + pangocoretext-shape.c \ pangocairo-coretext.h \ pangocairo-coretextfont.c \ pangocairo-coretextfont.h \ @@ -391,7 +393,7 @@ libpangowin32_1_0_la_LIBADD = \ libpango-$(PANGO_API_VERSION).la \ $(INCLUDED_WIN32_MODULES) \ $(GLIB_LIBS) \ - $(WIN32_LIBS) + -lgdi32 -lusp10 libpangowin32_1_0_la_DEPENDENCIES = \ libpango-$(PANGO_API_VERSION).la \ $(INCLUDED_WIN32_MODULES) @@ -402,7 +404,8 @@ libpangowin32_1_0_la_SOURCES = \ pangowin32.c \ pangowin32-private.h \ pangowin32-fontcache.c \ - pangowin32-fontmap.c + pangowin32-fontmap.c \ + pangowin32-shape.c if PLATFORM_WIN32 libpangowin32_1_0_la_LDFLAGS += -export-symbols $(srcdir)/pangowin32.def -Wl,pangowin32-win32-res.o diff --git a/pango/pangocoretext-shape.c b/pango/pangocoretext-shape.c new file mode 100644 index 00000000..ce6a62a5 --- /dev/null +++ b/pango/pangocoretext-shape.c @@ -0,0 +1,546 @@ +/* Pango + * basic-coretext.c + * + * Copyright (C) 2005 Imendio AB + * Copyright (C) 2010 Kristian Rietveld <kris@gtk.org> + * Copyright (C) 2012 Kristian Rietveld <kris@lanedo.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 "config.h" +#include <glib.h> +#include <string.h> +#include <Carbon/Carbon.h> +#include "pango-engine.h" +#include "pango-utils.h" +#include "pango-fontmap.h" +#include "pangocoretext.h" +#include "pango-impl-utils.h" + +/* No extra fields needed */ +typedef PangoEngineShape BasicEngineCoreText; +typedef PangoEngineShapeClass BasicEngineCoreTextClass ; + +#define SCRIPT_ENGINE_NAME "BasicScriptEngineCoreText" +#define RENDER_TYPE PANGO_RENDER_TYPE_CORE_TEXT + +static PangoEngineScriptInfo basic_scripts[] = { + { PANGO_SCRIPT_COMMON, "" } +}; + +static PangoEngineInfo script_engines[] = { + { + SCRIPT_ENGINE_NAME, + PANGO_ENGINE_TYPE_SHAPE, + RENDER_TYPE, + basic_scripts, G_N_ELEMENTS(basic_scripts) + } +}; + +static void +set_glyph (PangoFont *font, + PangoGlyphString *glyphs, + int i, + int offset, + PangoGlyph glyph) +{ + PangoRectangle logical_rect; + + glyphs->glyphs[i].glyph = glyph; + + glyphs->glyphs[i].geometry.x_offset = 0; + glyphs->glyphs[i].geometry.y_offset = 0; + + glyphs->log_clusters[i] = offset; + pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect); + glyphs->glyphs[i].geometry.width = logical_rect.width; +} + + +/* The "RunIterator" helps us to iterate over the array of runs that is obtained from + * the CoreText type setter. Even though Pango considers the string that is passed to + * the shaping engine a single run, CoreText might consider it to consist out of + * multiple runs. Because of this, we have an interface around the CoreText array of + * runs that works like iterating a single array, which makes our job in the shaping + * engine function easier. + */ + +struct RunIterator +{ + CTLineRef line; + CFStringRef cstr; + CFArrayRef runs; + CFIndex glyph_count; + + CFIndex total_ct_i; + CFIndex ct_i; + + int current_run_number; + CTRunRef current_run; + CFIndex *current_indices; + const CGGlyph *current_cgglyphs; + CGGlyph *current_cgglyphs_buffer; + CTRunStatus current_run_status; +}; + +static void +run_iterator_free_current_run (struct RunIterator *iter) +{ + iter->current_run_number = -1; + iter->current_run = NULL; + iter->current_cgglyphs = NULL; + if (iter->current_cgglyphs_buffer) + free (iter->current_cgglyphs_buffer); + iter->current_cgglyphs_buffer = NULL; + if (iter->current_indices) + free (iter->current_indices); + iter->current_indices = NULL; +} + +static void +run_iterator_set_current_run (struct RunIterator *iter, + const int run_number) +{ + CFIndex ct_glyph_count; + + run_iterator_free_current_run (iter); + + iter->current_run_number = run_number; + iter->current_run = CFArrayGetValueAtIndex (iter->runs, run_number); + ct_glyph_count = CTRunGetGlyphCount (iter->current_run); + + iter->current_run_status = CTRunGetStatus (iter->current_run); + iter->current_cgglyphs = CTRunGetGlyphsPtr (iter->current_run); + if (!iter->current_cgglyphs) + { + iter->current_cgglyphs_buffer = (CGGlyph *)malloc (sizeof (CGGlyph) * ct_glyph_count); + CTRunGetGlyphs (iter->current_run, CFRangeMake (0, ct_glyph_count), + iter->current_cgglyphs_buffer); + iter->current_cgglyphs = iter->current_cgglyphs_buffer; + } + + iter->current_indices = malloc (sizeof (CFIndex) * ct_glyph_count); + CTRunGetStringIndices (iter->current_run, CFRangeMake (0, ct_glyph_count), + iter->current_indices); + + iter->ct_i = 0; +} + +static CFIndex +run_iterator_get_glyph_count (struct RunIterator *iter) +{ + CFIndex accumulator = 0; + CFIndex i; + + for (i = 0; i < CFArrayGetCount (iter->runs); i++) + accumulator += CTRunGetGlyphCount (CFArrayGetValueAtIndex (iter->runs, i)); + + return accumulator; +} + +/* These functions are commented out to silence the compiler, but + * kept around because they might be of use when fixing the more + * intricate issues noted in the comment in the function + * basic_engine_shape() below. + */ +#if 0 +static gboolean +run_iterator_is_rtl (struct RunIterator *iter) +{ + /* Assume run status is equal for all runs? */ + CTRunStatus run_status = CTRunGetStatus (CFArrayGetValueAtIndex (iter->runs, 0)); + + return run_status & kCTRunStatusRightToLeft; +} + +static gboolean +run_iterator_run_is_non_monotonic (struct RunIterator *iter) +{ + CTRunStatus run_status = CTRunGetStatus (iter->current_run); + + return run_status & kCTRunStatusNonMonotonic; +} +#endif + +static gunichar +run_iterator_get_character (struct RunIterator *iter) +{ + return CFStringGetCharacterAtIndex (iter->cstr, iter->current_indices[iter->ct_i]); +} + +static CGGlyph +run_iterator_get_cgglyph (struct RunIterator *iter) +{ + return iter->current_cgglyphs[iter->ct_i]; +} + +static CFIndex +run_iterator_get_index (struct RunIterator *iter) +{ + return iter->current_indices[iter->ct_i]; +} + +static gboolean +run_iterator_create (struct RunIterator *iter, + const char *text, + const gint length, + CTFontRef ctfont) +{ + char *copy; + CFDictionaryRef attributes; + CFAttributedStringRef attstr; + + CFTypeRef keys[] = { + (CFTypeRef) kCTFontAttributeName + }; + + CFTypeRef values[] = { + ctfont + }; + + /* Initialize RunIterator structure */ + iter->current_run_number = -1; + iter->current_run = NULL; + iter->current_indices = NULL; + iter->current_cgglyphs = NULL; + iter->current_cgglyphs_buffer = NULL; + + /* Create CTLine */ + attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **)keys, + (const void **)values, + 1, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + copy = g_strndup (text, length + 1); + copy[length] = 0; + + iter->cstr = CFStringCreateWithCString (kCFAllocatorDefault, copy, + kCFStringEncodingUTF8); + g_free (copy); + + if (!iter->cstr) + /* Creating a CFString can fail if the input string does not + * adhere to the specified encoding (i.e. it contains invalid UTF8). + */ + return FALSE; + + attstr = CFAttributedStringCreate (kCFAllocatorDefault, + iter->cstr, + attributes); + + iter->line = CTLineCreateWithAttributedString (attstr); + iter->runs = CTLineGetGlyphRuns (iter->line); + + CFRelease (attstr); + CFRelease (attributes); + + iter->total_ct_i = 0; + iter->glyph_count = run_iterator_get_glyph_count (iter); + + /* If CoreText did not render any glyphs for this string (can happen, + * e.g. a run solely consisting of a BOM), glyph_count will be zero and + * we immediately set the iterator variable to indicate end of glyph list. + */ + if (iter->glyph_count > 0) + run_iterator_set_current_run (iter, 0); + else + iter->total_ct_i = -1; + + return TRUE; +} + +static void +run_iterator_free (struct RunIterator *iter) +{ + run_iterator_free_current_run (iter); + + CFRelease (iter->line); + CFRelease (iter->cstr); +} + +static gboolean +run_iterator_at_end (struct RunIterator *iter) +{ + if (iter->total_ct_i == -1) + return TRUE; + + return FALSE; +} + +static void +run_iterator_advance (struct RunIterator *iter) +{ + if (iter->total_ct_i >= iter->glyph_count - 1) + { + run_iterator_free_current_run (iter); + iter->ct_i = iter->total_ct_i = -1; + } + else + { + iter->total_ct_i++; + iter->ct_i++; + + if (iter->total_ct_i < iter->glyph_count && + iter->ct_i >= CTRunGetGlyphCount (iter->current_run)) + { + iter->current_run_number++; + run_iterator_set_current_run (iter, iter->current_run_number); + } + } +} + + + +struct GlyphInfo +{ + CFIndex index; + CGGlyph cgglyph; + gunichar wc; +}; + +static gint +glyph_info_compare_func (gconstpointer a, gconstpointer b) +{ + const struct GlyphInfo *gi_a = a; + const struct GlyphInfo *gi_b = b; + + if (gi_a->index < gi_b->index) + return -1; + else if (gi_a->index > gi_b->index) + return 1; + /* else */ + return 0; +} + +static void +glyph_info_free (gpointer data, gpointer user_data) +{ + g_slice_free (struct GlyphInfo, data); +} + +static GSList * +create_core_text_glyph_list (const char *text, + gint length, + CTFontRef ctfont) +{ + GSList *glyph_list = NULL; + struct RunIterator riter; + + if (!run_iterator_create (&riter, text, length, ctfont)) + return NULL; + + while (!run_iterator_at_end (&riter)) + { + struct GlyphInfo *gi; + + gi = g_slice_new (struct GlyphInfo); + gi->index = run_iterator_get_index (&riter); + gi->cgglyph = run_iterator_get_cgglyph (&riter); + gi->wc = run_iterator_get_character (&riter); + + glyph_list = g_slist_prepend (glyph_list, gi); + + run_iterator_advance (&riter); + } + + glyph_list = g_slist_sort (glyph_list, glyph_info_compare_func); + + run_iterator_free (&riter); + + return glyph_list; +} + + +static void +basic_engine_shape (PangoEngineShape *engine, + PangoFont *font, + const char *text, + gint length, + const PangoAnalysis *analysis, + PangoGlyphString *glyphs, + const char *paragraph_text G_GNUC_UNUSED, + unsigned int paragraph_length G_GNUC_UNUSED) +{ + const char *p; + gulong n_chars, gs_i, gs_prev_i; + PangoCoreTextFont *cfont = PANGO_CORE_TEXT_FONT (font); + PangoCoverage *coverage; + GSList *glyph_list; + GSList *glyph_iter; + + /* We first fully iterate over the glyph sequence generated by CoreText and + * store this into a list, which is sorted after the iteration. We make a pass + * over the sorted linked list to build up the PangoGlyphString. + * + * We have to do this in order to properly handle a bunch of characteristics of the + * glyph sequence generated by the CoreText typesetter: + * # E.g. zero-width spaces do not end up in the CoreText glyph sequence. We have + * to manually account for the gap in the character indices. + * # Sometimes, CoreText generates two glyph for the same character index. We + * currently handle this "properly" as in we do not crash or corrupt memory, + * but that's about it. + * # Due to mismatches in size, the CoreText glyph sequence can either be longer or + * shorter than the PangoGlyphString. Note that the size of the PangoGlyphString + * should match the number of characters in "text". + * + * If performance becomes a problem, it is certainly possible to use a faster code + * that only does a single iteration over the string for "simple cases". Simple cases + * could include these that only consist out of one run (simple Latin text), which + * don't have gaps in the glyph sequence and which are monotonically + * increasing/decreasing. + * + * FIXME items for future fixing: + * # CoreText strings are UTF16, and the indices *often* refer to characters, + * but not *always*. Notable exception is when a character is encoded using + * two UTF16 code points. This are two characters in a CFString. At this point + * advancing a single character in the CFString and advancing a single character + * using g_utf8_next_char in the const char string goes out of sync. + * # We currently don't bother about LTR, Pango core appears to fix this up for us. + * (Even when we cared warnings were generated that strings were in the wrong + * order, this should be investigated). + * # When CoreText generates two glyphs for one character, only one is stored. + * This breaks the example strings for e.g. Georgian and Gothic. + */ + + glyph_list = create_core_text_glyph_list (text, length, + pango_core_text_font_get_ctfont (cfont)); + if (!glyph_list) + return; + + /* Translate the glyph list to a PangoGlyphString */ + n_chars = g_utf8_strlen (text, length); + pango_glyph_string_set_size (glyphs, n_chars); + + glyph_iter = glyph_list; + + coverage = pango_font_get_coverage (PANGO_FONT (cfont), + analysis->language); + + for (gs_prev_i = -1, gs_i = 0, p = text; gs_i < n_chars; + gs_prev_i = gs_i, gs_i++, p = g_utf8_next_char (p)) + { + struct GlyphInfo *gi = glyph_iter != NULL ? glyph_iter->data : NULL; + + if (gi == NULL || gi->index > gs_i) + { + /* gs_i is behind, insert empty glyph */ + set_glyph (font, glyphs, gs_i, p - text, PANGO_GLYPH_EMPTY); + continue; + } + else if (gi->index < gs_i) + { + while (gi && gi->index < gs_i) + { + glyph_iter = g_slist_next (glyph_iter); + if (glyph_iter) + gi = glyph_iter->data; + else + gi = NULL; + } + } + + if (gi != NULL && gi->index == gs_i) + { + gunichar mirrored_ch; + PangoCoverageLevel result; + + if (analysis->level % 2) + if (g_unichar_get_mirror_char (gi->wc, &mirrored_ch)) + gi->wc = mirrored_ch; + + if (gi->wc == 0xa0) /* non-break-space */ + gi->wc = 0x20; + + result = pango_coverage_get (coverage, gi->wc); + + if (result != PANGO_COVERAGE_NONE) + { + set_glyph (font, glyphs, gs_i, p - text, gi->cgglyph); + + if (g_unichar_type (gi->wc) == G_UNICODE_NON_SPACING_MARK) + { + if (gi->index > 0) + { + PangoRectangle logical_rect, ink_rect; + + glyphs->glyphs[gs_i].geometry.width = MAX (glyphs->glyphs[gs_prev_i].geometry.width, + glyphs->glyphs[gs_i].geometry.width); + glyphs->glyphs[gs_prev_i].geometry.width = 0; + glyphs->log_clusters[gs_i] = glyphs->log_clusters[gs_prev_i]; + + /* Some heuristics to try to guess how overstrike glyphs are + * done and compensate + */ + pango_font_get_glyph_extents (font, glyphs->glyphs[gs_i].glyph, &ink_rect, &logical_rect); + if (logical_rect.width == 0 && ink_rect.x == 0) + glyphs->glyphs[gs_i].geometry.x_offset = (glyphs->glyphs[gs_i].geometry.width - ink_rect.width) / 2; + } + } + } + else + set_glyph (font, glyphs, gs_i, p - text, PANGO_GET_UNKNOWN_GLYPH (gi->wc)); + + glyph_iter = g_slist_next (glyph_iter); + } + } + + pango_coverage_unref (coverage); + g_slist_foreach (glyph_list, glyph_info_free, NULL); + g_slist_free (glyph_list); + + if (analysis->level & 1) + pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs); +} + +static void +basic_engine_core_text_class_init (PangoEngineShapeClass *class) +{ + class->script_shape = basic_engine_shape; +} + +PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineCoreText, basic_engine_core_text, + basic_engine_core_text_class_init, NULL); + +void +PANGO_MODULE_ENTRY(init) (GTypeModule *module) +{ + basic_engine_core_text_register_type (module); +} + +void +PANGO_MODULE_ENTRY(exit) (void) +{ +} + +void +PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines, + int *n_engines) +{ + *engines = script_engines; + *n_engines = G_N_ELEMENTS (script_engines); +} + +PangoEngine * +PANGO_MODULE_ENTRY(create) (const char *id) +{ + if (!strcmp (id, SCRIPT_ENGINE_NAME)) + return g_object_new (basic_engine_core_text_type, NULL); + else + return NULL; +} diff --git a/pango/pangofc-shape.c b/pango/pangofc-shape.c new file mode 100644 index 00000000..d77dacef --- /dev/null +++ b/pango/pangofc-shape.c @@ -0,0 +1,483 @@ +/* Pango + * basic-fc.c: Basic shaper for FreeType-based backends + * + * Copyright (C) 2000, 2007, 2009 Red Hat Software + * Authors: + * Owen Taylor <otaylor@redhat.com> + * Behdad Esfahbod <behdad@behdad.org> + * + * 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 "config.h" +#include <string.h> + +#define PANGO_ENABLE_BACKEND 1 /* XXX */ + +#include "pango-engine.h" +#include "pango-utils.h" +#include "pangofc-fontmap.h" +#include "pangofc-font.h" +#include <hb-ft.h> +#include <hb-glib.h> + +#define PANGO_UNITS_26_6(d) ((d) << 4) + + +/* No extra fields needed */ +typedef PangoEngineShape BasicEngineFc; +typedef PangoEngineShapeClass BasicEngineFcClass; + +#define SCRIPT_ENGINE_NAME "BasicScriptEngineFc" +#define RENDER_TYPE PANGO_RENDER_TYPE_FC + +static PangoEngineScriptInfo basic_scripts[] = { + { PANGO_SCRIPT_COMMON, "" } +}; + +static PangoEngineInfo script_engines[] = { + { + SCRIPT_ENGINE_NAME, + PANGO_ENGINE_TYPE_SHAPE, + RENDER_TYPE, + basic_scripts, G_N_ELEMENTS(basic_scripts) + } +}; + + +/* cache a single hb_buffer_t */ +static hb_buffer_t *cached_buffer = NULL; /* MT-safe */ +G_LOCK_DEFINE_STATIC (cached_buffer); + +static hb_buffer_t * +create_buffer (void) +{ + hb_buffer_t *buffer; + + buffer = hb_buffer_create (); + hb_buffer_set_unicode_funcs (buffer, hb_glib_get_unicode_funcs ()); + + return buffer; +} + +static hb_buffer_t * +acquire_buffer (gboolean *free_buffer) +{ + hb_buffer_t *buffer; + + if (G_LIKELY (G_TRYLOCK (cached_buffer))) + { + if (G_UNLIKELY (!cached_buffer)) + cached_buffer = create_buffer (); + + buffer = cached_buffer; + *free_buffer = FALSE; + } + else + { + buffer = create_buffer (); + *free_buffer = TRUE; + } + + return buffer; +} + +static void +release_buffer (hb_buffer_t *buffer, gboolean free_buffer) +{ + if (G_LIKELY (!free_buffer)) + { + hb_buffer_reset (buffer); + G_UNLOCK (cached_buffer); + } + else + hb_buffer_destroy (buffer); +} + +typedef struct _PangoFcHbContext { + FT_Face ft_face; + PangoFcFont *fc_font; + gboolean vertical; + int improper_sign; +} PangoFcHbContext; + +static hb_bool_t +pango_fc_hb_font_get_glyph (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + PangoFcFont *fc_font = context->fc_font; + + *glyph = pango_fc_font_get_glyph (fc_font, unicode); + if (G_LIKELY (*glyph)) + return TRUE; + + *glyph = PANGO_GET_UNKNOWN_GLYPH (unicode); + + /* We draw our own invalid-Unicode shape, so prevent HarfBuzz + * from using REPLACEMENT CHARACTER. */ + if (unicode > 0x10FFFF) + return TRUE; + + return FALSE; +} + +static hb_bool_t +pango_fc_hb_font_get_glyph_contour_point (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data G_GNUC_UNUSED) +{ + return FALSE; +#if 0 + FT_Face ft_face = (FT_Face) font_data; + int load_flags = FT_LOAD_DEFAULT; + + /* TODO: load_flags, embolden, etc */ + + if (HB_UNLIKELY (FT_Load_Glyph (ft_face, glyph, load_flags))) + return FALSE; + + if (HB_UNLIKELY (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return FALSE; + + if (HB_UNLIKELY (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return FALSE; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return TRUE; +#endif +} + +static hb_position_t +pango_fc_hb_font_get_glyph_advance (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + PangoFcFont *fc_font = context->fc_font; + PangoRectangle logical; + + pango_font_get_glyph_extents ((PangoFont *) fc_font, glyph, NULL, &logical); + + return logical.width; +} + +static hb_bool_t +pango_fc_hb_font_get_glyph_extents (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + PangoFcFont *fc_font = context->fc_font; + PangoRectangle ink; + + pango_font_get_glyph_extents ((PangoFont *) fc_font, glyph, &ink, NULL); + + if (G_LIKELY (!context->vertical)) { + extents->x_bearing = ink.x; + extents->y_bearing = ink.y; + extents->width = ink.width; + extents->height = ink.height; + } else { + /* XXX */ + extents->x_bearing = ink.x; + extents->y_bearing = ink.y; + extents->width = ink.height; + extents->height = ink.width; + } + + return TRUE; +} + +static hb_bool_t +pango_fc_hb_font_get_glyph_h_origin (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + FT_Face ft_face = context->ft_face; + int load_flags = FT_LOAD_DEFAULT; + + if (!context->vertical) return TRUE; + + if (FT_Load_Glyph (ft_face, glyph, load_flags)) + return FALSE; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = PANGO_UNITS_26_6 (ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX); + *y = PANGO_UNITS_26_6 (ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY)); + + /* XXX */ + *x = -*x; + *y = *y; + + return TRUE; +} + +static hb_bool_t +pango_fc_hb_font_get_glyph_v_origin (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + FT_Face ft_face = context->ft_face; + int load_flags = FT_LOAD_DEFAULT; + + if (context->vertical) return TRUE; + + if (FT_Load_Glyph (ft_face, glyph, load_flags)) + return FALSE; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = PANGO_UNITS_26_6 (ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX); + *y = PANGO_UNITS_26_6 (ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY)); + + /* XXX */ + + return TRUE; +} + + +static hb_position_t +pango_fc_hb_font_get_h_kerning (hb_font_t *font, void *font_data, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph, + void *user_data G_GNUC_UNUSED) +{ + PangoFcHbContext *context = (PangoFcHbContext *) font_data; + FT_Face ft_face = context->ft_face; + FT_Vector kerning; + + if (FT_Get_Kerning (ft_face, left_glyph, right_glyph, FT_KERNING_DEFAULT, &kerning)) + return 0; + + return PANGO_UNITS_26_6 (kerning.x); +} + +static hb_font_funcs_t * +pango_fc_get_hb_font_funcs (void) +{ + static hb_font_funcs_t *funcs; + + if (G_UNLIKELY (!funcs)) { + funcs = hb_font_funcs_create (); + hb_font_funcs_set_glyph_func (funcs, pango_fc_hb_font_get_glyph, NULL, NULL); + hb_font_funcs_set_glyph_h_advance_func (funcs, pango_fc_hb_font_get_glyph_advance, NULL, NULL); + hb_font_funcs_set_glyph_v_advance_func (funcs, pango_fc_hb_font_get_glyph_advance, NULL, NULL); + hb_font_funcs_set_glyph_h_origin_func (funcs, pango_fc_hb_font_get_glyph_h_origin, NULL, NULL); + hb_font_funcs_set_glyph_v_origin_func (funcs, pango_fc_hb_font_get_glyph_v_origin, NULL, NULL); + hb_font_funcs_set_glyph_h_kerning_func (funcs, pango_fc_hb_font_get_h_kerning, NULL, NULL); + /* Don't need v_kerning. */ + hb_font_funcs_set_glyph_extents_func (funcs, pango_fc_hb_font_get_glyph_extents, NULL, NULL); + hb_font_funcs_set_glyph_contour_point_func (funcs, pango_fc_hb_font_get_glyph_contour_point, NULL, NULL); + /* Don't need glyph_name / glyph_from_name */ + } + + return funcs; +} + + + + +static void +basic_engine_shape (PangoEngineShape *engine G_GNUC_UNUSED, + PangoFont *font, + const char *item_text, + unsigned int item_length, + const PangoAnalysis *analysis, + PangoGlyphString *glyphs, + const char *paragraph_text, + unsigned int paragraph_length) +{ + PangoFcHbContext context; + PangoFcFont *fc_font; + FT_Face ft_face; + hb_face_t *hb_face; + hb_font_t *hb_font; + hb_buffer_t *hb_buffer; + hb_direction_t hb_direction; + gboolean free_buffer; + gboolean is_hinted; + hb_glyph_info_t *hb_glyph; + hb_glyph_position_t *hb_position; + int last_cluster; + guint i, num_glyphs; + unsigned int item_offset = item_text - paragraph_text; + hb_feature_t features[8]; + unsigned int num_features = 0; + + g_return_if_fail (font != NULL); + g_return_if_fail (analysis != NULL); + + fc_font = PANGO_FC_FONT (font); + ft_face = pango_fc_font_lock_face (fc_font); + if (!ft_face) + return; + + /* TODO: Cache hb_font? */ + context.ft_face = ft_face; + context.fc_font = fc_font; + context.vertical = PANGO_GRAVITY_IS_VERTICAL (analysis->gravity); + context.improper_sign = PANGO_GRAVITY_IS_IMPROPER (analysis->gravity) ? -1 : +1; + hb_face = hb_ft_face_create_cached (ft_face); + hb_font = hb_font_create (hb_face); + hb_font_set_funcs (hb_font, + pango_fc_get_hb_font_funcs (), + &context, + NULL); + hb_font_set_scale (hb_font, + /* XXX CTM */ + context.improper_sign * + (((guint64) ft_face->size->metrics.x_scale * ft_face->units_per_EM) >> 12), + context.improper_sign * + -(((guint64) ft_face->size->metrics.y_scale * ft_face->units_per_EM) >> 12)); + is_hinted = fc_font->is_hinted; + hb_font_set_ppem (hb_font, + is_hinted ? ft_face->size->metrics.x_ppem : 0, + is_hinted ? ft_face->size->metrics.y_ppem : 0); + + hb_buffer = acquire_buffer (&free_buffer); + + hb_direction = PANGO_GRAVITY_IS_VERTICAL (analysis->gravity) ? HB_DIRECTION_TTB : HB_DIRECTION_LTR; + if (analysis->level % 2) + hb_direction = HB_DIRECTION_REVERSE (hb_direction); + if (PANGO_GRAVITY_IS_IMPROPER (analysis->gravity)) + hb_direction = HB_DIRECTION_REVERSE (hb_direction); + + /* setup buffer */ + + hb_buffer_set_direction (hb_buffer, hb_direction); + hb_buffer_set_script (hb_buffer, hb_glib_script_to_script (analysis->script)); + hb_buffer_set_language (hb_buffer, hb_language_from_string (pango_language_to_string (analysis->language), -1)); + hb_buffer_set_flags (hb_buffer, + (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) | + (item_offset + item_length == paragraph_length ? HB_BUFFER_FLAG_EOT : 0)); + + hb_buffer_add_utf8 (hb_buffer, paragraph_text, paragraph_length, item_offset, item_length); + + /* Setup features from fontconfig pattern. */ + if (fc_font->font_pattern) + { + char *s; + while (num_features < G_N_ELEMENTS (features) && + FcResultMatch == FcPatternGetString (fc_font->font_pattern, + PANGO_FC_FONT_FEATURES, + num_features, + (FcChar8 **) &s)) + { + gboolean ret = hb_feature_from_string (s, -1, &features[num_features]); + features[num_features].start = 0; + features[num_features].end = (unsigned int) -1; + if (ret) + num_features++; + } + } + + hb_shape (hb_font, hb_buffer, features, num_features); + + if (PANGO_GRAVITY_IS_IMPROPER (analysis->gravity)) + hb_buffer_reverse (hb_buffer); + + /* buffer output */ + num_glyphs = hb_buffer_get_length (hb_buffer); + hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL); + pango_glyph_string_set_size (glyphs, num_glyphs); + last_cluster = -1; + for (i = 0; i < num_glyphs; i++) + { + glyphs->glyphs[i].glyph = hb_glyph->codepoint; + glyphs->log_clusters[i] = hb_glyph->cluster - item_offset; + glyphs->glyphs[i].attr.is_cluster_start = glyphs->log_clusters[i] != last_cluster; + hb_glyph++; + last_cluster = glyphs->log_clusters[i]; + } + + hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL); + if (context.vertical) + for (i = 0; i < num_glyphs; i++) + { + unsigned int advance = hb_position->y_advance; + if (is_hinted) + advance = PANGO_UNITS_ROUND (advance); + glyphs->glyphs[i].geometry.width = advance; + /* XXX */ + glyphs->glyphs[i].geometry.x_offset = hb_position->y_offset; + glyphs->glyphs[i].geometry.y_offset = -hb_position->x_offset; + hb_position++; + } + else /* horizontal */ + for (i = 0; i < num_glyphs; i++) + { + unsigned int advance = hb_position->x_advance; + if (is_hinted) + advance = PANGO_UNITS_ROUND (advance); + glyphs->glyphs[i].geometry.width = advance; + glyphs->glyphs[i].geometry.x_offset = hb_position->x_offset; + glyphs->glyphs[i].geometry.y_offset = hb_position->y_offset; + hb_position++; + } + + release_buffer (hb_buffer, free_buffer); + hb_font_destroy (hb_font); + hb_face_destroy (hb_face); + pango_fc_font_unlock_face (fc_font); +} + +static void +basic_engine_fc_class_init (PangoEngineShapeClass *class) +{ + class->script_shape = basic_engine_shape; +} + +PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineFc, basic_engine_fc, + basic_engine_fc_class_init, NULL) + +void +PANGO_MODULE_ENTRY(init) (GTypeModule *module) +{ + basic_engine_fc_register_type (module); +} + +void +PANGO_MODULE_ENTRY(exit) (void) +{ +} + +void +PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines, + int *n_engines) +{ + *engines = script_engines; + *n_engines = G_N_ELEMENTS (script_engines); +} + +PangoEngine * +PANGO_MODULE_ENTRY(create) (const char *id) +{ + if (!strcmp (id, SCRIPT_ENGINE_NAME)) + return g_object_new (basic_engine_fc_type, NULL); + else + return NULL; +} diff --git a/pango/pangowin32-shape.c b/pango/pangowin32-shape.c new file mode 100644 index 00000000..e9d020da --- /dev/null +++ b/pango/pangowin32-shape.c @@ -0,0 +1,877 @@ +/* Pango + * basic-win32.c: + * + * Copyright (C) 1999 Red Hat Software + * Copyright (C) 2001 Alexander Larsson + * + * 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 "config.h" + +#define BASIC_WIN32_DEBUGGING + +#include <math.h> +#include <stdlib.h> + +#include <glib.h> + +#include "pangowin32.h" + +extern HFONT _pango_win32_font_get_hfont (PangoFont *font); + +#include "pango-engine.h" +#include "pango-utils.h" + +/* No extra fields needed */ +typedef PangoEngineShape BasicEngineWin32; +typedef PangoEngineShapeClass BasicEngineWin32Class ; + +#define SCRIPT_ENGINE_NAME "BasicScriptEngineWin32" + +static gboolean pango_win32_debug = FALSE; + +#include <usp10.h> + +static HDC hdc; + +#ifdef BASIC_WIN32_DEBUGGING +static const SCRIPT_PROPERTIES **scripts; +static int nscripts; +#endif + +static PangoEngineScriptInfo uniscribe_scripts[] = { + /* We claim to cover everything ;-) */ + { PANGO_SCRIPT_COMMON, "" }, +}; + +static PangoEngineScriptInfo basic_scripts[] = { + /* Those characters that can be rendered legibly without Uniscribe. + * I am not certain this list is correct. + */ + { PANGO_SCRIPT_ARMENIAN, "*" }, + { PANGO_SCRIPT_BOPOMOFO, "*" }, + { PANGO_SCRIPT_CHEROKEE, "*" }, + { PANGO_SCRIPT_COPTIC, "*" }, + { PANGO_SCRIPT_CYRILLIC, "*" }, + { PANGO_SCRIPT_DESERET, "*" }, + { PANGO_SCRIPT_ETHIOPIC, "*" }, + { PANGO_SCRIPT_GEORGIAN, "*" }, + { PANGO_SCRIPT_GOTHIC, "*" }, + { PANGO_SCRIPT_GREEK, "*" }, + { PANGO_SCRIPT_HAN, "*" }, + { PANGO_SCRIPT_HANGUL, "*" }, + { PANGO_SCRIPT_HIRAGANA, "*" }, + { PANGO_SCRIPT_KATAKANA, "*" }, + { PANGO_SCRIPT_LATIN, "*" }, + { PANGO_SCRIPT_OGHAM, "*" }, + { PANGO_SCRIPT_OLD_ITALIC, "*" }, + { PANGO_SCRIPT_RUNIC, "*" }, + { PANGO_SCRIPT_THAI, "*" }, + { PANGO_SCRIPT_CANADIAN_ABORIGINAL, "*" }, + { PANGO_SCRIPT_YI, "*" }, + { PANGO_SCRIPT_BRAILLE, "*" }, + { PANGO_SCRIPT_CYPRIOT, "*" }, + { PANGO_SCRIPT_LIMBU, "*" }, + { PANGO_SCRIPT_OSMANYA, "*" }, + { PANGO_SCRIPT_SHAVIAN, "*" }, + { PANGO_SCRIPT_LINEAR_B, "*" }, + { PANGO_SCRIPT_UGARITIC, "*" }, + + /* Claim to handle everything as a fallback */ + { PANGO_SCRIPT_COMMON, "" } +}; + +static PangoEngineInfo script_engines[] = { + { + SCRIPT_ENGINE_NAME, + PANGO_ENGINE_TYPE_SHAPE, + PANGO_RENDER_TYPE_WIN32, + NULL, 0 + } +}; + +static PangoGlyph +find_char (PangoFont *font, + gunichar wc) +{ + return pango_win32_font_get_glyph_index (font, wc); +} + +static void +set_glyph (PangoFont *font, + PangoGlyphString *glyphs, + int i, + int offset, + PangoGlyph glyph) +{ + PangoRectangle logical_rect; + + glyphs->glyphs[i].glyph = glyph; + + glyphs->glyphs[i].geometry.x_offset = 0; + glyphs->glyphs[i].geometry.y_offset = 0; + + glyphs->log_clusters[i] = offset; + + pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect); + glyphs->glyphs[i].geometry.width = logical_rect.width; +} + +static void +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; + } +} + +#ifdef BASIC_WIN32_DEBUGGING + +static char * +lang_name (int lang) +{ + LCID lcid = MAKELCID (lang, SORT_DEFAULT); + static char retval[10]; + + if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, retval, G_N_ELEMENTS (retval))) + sprintf (retval, "%#02x", lang); + + return retval; +} + +#endif /* BASIC_WIN32_DEBUGGING */ + +static WORD +make_langid (PangoLanguage *lang) +{ +#define CASE(t,p,s) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_##p##_##s) +#define CASEN(t,p) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_NEUTRAL) + + /* Languages that most probably don't affect Uniscribe have been + * left out. Uniscribe is documented to use + * SCRIPT_CONTROL::uDefaultLanguage only to select digit shapes, so + * just leave languages with own digits. + */ + + CASEN ("ar", ARABIC); + CASEN ("hy", ARMENIAN); + CASEN ("as", ASSAMESE); + CASEN ("az", AZERI); + CASEN ("bn", BENGALI); + CASE ("zh-tw", CHINESE, TRADITIONAL); + CASE ("zh-cn", CHINESE, SIMPLIFIED); + CASE ("zh-hk", CHINESE, HONGKONG); + CASE ("zh-sg", CHINESE, SINGAPORE); + CASE ("zh-mo", CHINESE, MACAU); + CASEN ("dib", DIVEHI); + CASEN ("fa", FARSI); + CASEN ("ka", GEORGIAN); + CASEN ("gu", GUJARATI); + CASEN ("he", HEBREW); + CASEN ("hi", HINDI); + CASEN ("ja", JAPANESE); + CASEN ("kn", KANNADA); + CASE ("ks-in", KASHMIRI, INDIA); + CASEN ("ks", KASHMIRI); + CASEN ("kk", KAZAK); + CASEN ("kok", KONKANI); + CASEN ("ko", KOREAN); + CASEN ("ky", KYRGYZ); + CASEN ("ml", MALAYALAM); + CASEN ("mni", MANIPURI); + CASEN ("mr", MARATHI); + CASEN ("mn", MONGOLIAN); + CASE ("ne-in", NEPALI, INDIA); + CASEN ("ne", NEPALI); + CASEN ("or", ORIYA); + CASEN ("pa", PUNJABI); + CASEN ("sa", SANSKRIT); + CASEN ("sd", SINDHI); + CASEN ("syr", SYRIAC); + CASEN ("ta", TAMIL); + CASEN ("tt", TATAR); + CASEN ("te", TELUGU); + CASEN ("th", THAI); + CASE ("ur-pk", URDU, PAKISTAN); + CASE ("ur-in", URDU, INDIA); + CASEN ("ur", URDU); + CASEN ("uz", UZBEK); + +#undef CASE +#undef CASEN + + return MAKELANGID (LANG_NEUTRAL, SUBLANG_NEUTRAL); +} + +#ifdef BASIC_WIN32_DEBUGGING + +static void +dump_glyphs_and_log_clusters (gboolean rtl, + int itemlen, + int charix0, + WORD *log_clusters, + WORD *iglyphs, + int nglyphs) +{ + if (pango_win32_debug) + { + int j, k, nclusters, clusterix, charix, ng; + + g_print (" ScriptShape: nglyphs=%d: ", nglyphs); + + for (j = 0; j < nglyphs; j++) + g_print ("%d%s", iglyphs[j], (j < nglyphs-1) ? "," : ""); + g_print ("\n"); + + g_print (" log_clusters: "); + for (j = 0; j < itemlen; j++) + g_print ("%d ", log_clusters[j]); + g_print ("\n"); + nclusters = 0; + for (j = 0; j < itemlen; j++) + { + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + nclusters++; + } + g_print (" %d clusters:\n", nclusters); + + /* If RTL, first char is the last in the run, otherwise the + * first. + */ + clusterix = 0; + if (rtl) + { + int firstglyphix = 0; + for (j = itemlen - 1, charix = charix0 + j; j >= 0; j--, charix--) + { + if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1]) + g_print (" Cluster %d: chars %d--", + clusterix, charix); + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + { + ng = log_clusters[j] - firstglyphix + 1; + g_print ("%d: %d glyphs: ", + charix, ng); + for (k = firstglyphix; k <= log_clusters[j]; k++) + { + g_print ("%d", iglyphs[k]); + if (k < log_clusters[j]) + g_print (","); + } + firstglyphix = log_clusters[j] + 1; + clusterix++; + g_print ("\n"); + } + } + } + else + { + for (j = 0, charix = charix0; j < itemlen; j++, charix++) + { + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + g_print (" Cluster %d: wchar_t %d--", + clusterix, charix); + if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1]) + { + int klim = ((j == itemlen-1) ? nglyphs : log_clusters[j+1]); + ng = klim - log_clusters[j]; + g_print ("%d: %d glyphs: ", + charix, ng); + for (k = log_clusters[j]; k < klim; k++) + { + g_print ("%d", iglyphs[k]); + if (k != klim - 1) + g_print (","); + } + clusterix++; + g_print ("\n"); + } + } + } + } +} + +#endif /* BASIC_WIN32_DEBUGGING */ + +static void +set_up_pango_log_clusters (wchar_t *wtext, + gboolean rtl, + int itemlen, + WORD *usp_log_clusters, + int nglyphs, + gint *pango_log_clusters, + int char_offset) +{ + int j, k; + int first_char_in_cluster; + + if (rtl) + { + /* RTL. Walk Uniscribe log_clusters array backwards, build Pango + * log_clusters array forwards. + */ + int glyph0 = 0; + first_char_in_cluster = itemlen - 1; + for (j = itemlen - 1; j >= 0; j--) + { + if (j < itemlen - 1 && usp_log_clusters[j+1] != usp_log_clusters[j]) + { + /* Cluster starts */ + first_char_in_cluster = j; + } + if (j == 0) + { + /* First char, cluster ends */ + for (k = glyph0; k < nglyphs; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + else if (usp_log_clusters[j-1] == usp_log_clusters[j]) + { + /* Cluster continues */ + first_char_in_cluster = j-1; + } + else + { + /* Cluster ends */ + for (k = glyph0; k <= usp_log_clusters[j]; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + glyph0 = usp_log_clusters[j] + 1; + } + } + } + else + { + /* LTR. Walk Uniscribe log_clusters array forwards, build Pango + * log_clusters array also forwards. + */ + first_char_in_cluster = 0; + for (j = 0; j < itemlen; j++) + { + if (j > 0 && usp_log_clusters[j-1] != usp_log_clusters[j]) + { + /* Cluster starts */ + first_char_in_cluster = j; + } + if (j == itemlen - 1) + { + /* Last char, cluster ends */ + for (k = usp_log_clusters[j]; k < nglyphs; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + else if (usp_log_clusters[j] == usp_log_clusters[j+1]) + { + /* Cluster continues */ + } + else + { + /* Cluster ends */ + for (k = usp_log_clusters[j]; k < usp_log_clusters[j+1]; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + } + } +} + +static void +convert_log_clusters_to_byte_offsets (const char *text, + gint length, + PangoGlyphString *glyphs, + gint utf16_len) +{ + const char *p; + int charix, glyphix; + int *byte_offset = g_new (int, utf16_len); + + p = text; + charix = 0; + while (p < text + length) + { + byte_offset[charix] = p - text; + charix++; + if (g_utf8_get_char (p) > 0xFFFF) + { + byte_offset[charix] = p - text; + charix++; + } + p = g_utf8_next_char (p); + } + g_assert (charix <= utf16_len); + + /* Convert utf16 indexes in the log_clusters array to byte offsets. + */ + for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) + { + g_assert (glyphs->log_clusters[glyphix] < utf16_len); + glyphs->log_clusters[glyphix] = byte_offset[glyphs->log_clusters[glyphix]]; + } + + g_free (byte_offset); +} + +static gboolean +itemize_shape_and_place (PangoFont *font, + HDC hdc, + wchar_t *wtext, + int wlen, + const PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + int i; + int item, nitems, item_step; + int itemlen, glyphix, nglyphs; + SCRIPT_CONTROL control; + SCRIPT_STATE state; + SCRIPT_ITEM items[100]; + double scale = pango_win32_font_get_metrics_factor (font); + HFONT hfont = _pango_win32_font_get_hfont (font); + static GHashTable *script_cache_hash = NULL; + + if (!script_cache_hash) + script_cache_hash = g_hash_table_new (g_int64_hash, g_int64_equal); + + memset (&control, 0, sizeof (control)); + memset (&state, 0, sizeof (state)); + + control.uDefaultLanguage = make_langid (analysis->language); + state.uBidiLevel = analysis->level; + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print (G_STRLOC ": ScriptItemize: uDefaultLanguage:%04x uBidiLevel:%d\n", + control.uDefaultLanguage, state.uBidiLevel); +#endif + if (ScriptItemize (wtext, wlen, G_N_ELEMENTS (items), &control, NULL, + items, &nitems)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print ("ScriptItemize failed\n"); +#endif + return FALSE; + } + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print ("%d items:\n", nitems); +#endif + + if (analysis->level % 2) + { + item = nitems - 1; + item_step = -1; + } + else + { + item = 0; + item_step = 1; + } + + for (i = 0; i < nitems; i++, item += item_step) + { + WORD iglyphs[1000]; + WORD log_clusters[1000]; + SCRIPT_VISATTR visattrs[1000]; + int advances[1000]; + GOFFSET offsets[1000]; + ABC abc; + gint32 script = items[item].a.eScript; + int ng; + int char_offset; + SCRIPT_CACHE *script_cache; + gint64 font_and_script_key; + + memset (advances, 0, sizeof (advances)); + memset (offsets, 0, sizeof (offsets)); + memset (&abc, 0, sizeof (abc)); + + /* Note that itemlen is number of wchar_t's i.e. surrogate pairs + * count as two! + */ + itemlen = items[item+1].iCharPos - items[item].iCharPos; + char_offset = items[item].iCharPos; + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print (" Item %d: iCharPos=%d eScript=%d (%s) %s%s%s%s%s%s%s wchar_t %d--%d (%d)\n", + item, items[item].iCharPos, script, + lang_name (scripts[script]->langid), + scripts[script]->fComplex ? "complex" : "simple", + items[item].a.fRTL ? " fRTL" : "", + items[item].a.fLayoutRTL ? " fLayoutRTL" : "", + items[item].a.fLinkBefore ? " fLinkBefore" : "", + items[item].a.fLinkAfter ? " fLinkAfter" : "", + items[item].a.fLogicalOrder ? " fLogicalOrder" : "", + items[item].a.fNoGlyphIndex ? " fNoGlyphIndex" : "", + items[item].iCharPos, items[item+1].iCharPos-1, itemlen); +#endif + /* Create a hash key based on hfont and script engine */ + font_and_script_key = (((gint64) ((gint32) hfont)) << 32) | script; + + /* Get the script cache for this hfont and script */ + script_cache = g_hash_table_lookup (script_cache_hash, &font_and_script_key); + if (!script_cache) + { + gint64 *key_n; + SCRIPT_CACHE *new_script_cache; + + key_n = g_new (gint64, 1); + *key_n = font_and_script_key; + + new_script_cache = g_new0 (SCRIPT_CACHE, 1); + script_cache = new_script_cache; + + /* Insert the new value */ + g_hash_table_insert (script_cache_hash, key_n, new_script_cache); + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print (" New SCRIPT_CACHE for font %p and script %d\n", hfont, script); +#endif + } + + items[item].a.fRTL = analysis->level % 2; + if (ScriptShape (hdc, script_cache, + wtext + items[item].iCharPos, itemlen, + G_N_ELEMENTS (iglyphs), + &items[item].a, + iglyphs, + log_clusters, + visattrs, + &nglyphs)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print ("pango-basic-win32: ScriptShape failed\n"); +#endif + return FALSE; + } + +#ifdef BASIC_WIN32_DEBUGGING + dump_glyphs_and_log_clusters (items[item].a.fRTL, itemlen, + items[item].iCharPos, log_clusters, + iglyphs, nglyphs); +#endif + + ng = glyphs->num_glyphs; + pango_glyph_string_set_size (glyphs, ng + nglyphs); + + set_up_pango_log_clusters (wtext + items[item].iCharPos, + items[item].a.fRTL, itemlen, log_clusters, + nglyphs, glyphs->log_clusters + ng, + char_offset); + + if (ScriptPlace (hdc, script_cache, iglyphs, nglyphs, + visattrs, &items[item].a, + advances, offsets, &abc)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print ("pango-basic-win32: ScriptPlace failed\n"); +#endif + return FALSE; + } + + for (glyphix = 0; glyphix < nglyphs; glyphix++) + { + if (iglyphs[glyphix] != 0) + { + glyphs->glyphs[ng+glyphix].glyph = iglyphs[glyphix]; + glyphs->glyphs[ng+glyphix].geometry.width = floor (0.5 + scale * advances[glyphix]); + glyphs->glyphs[ng+glyphix].geometry.x_offset = floor (0.5 + scale * offsets[glyphix].du); + glyphs->glyphs[ng+glyphix].geometry.y_offset = -floor (0.5 + scale * offsets[glyphix].dv); + } + else + { + PangoRectangle logical_rect; + /* Should pass actual char that was not found to + * PANGO_GET_UNKNOWN_GLYPH(), but a bit hard to + * find out that at this point, so cheat and use 0. + */ + PangoGlyph unk = PANGO_GET_UNKNOWN_GLYPH (0); + + glyphs->glyphs[ng+glyphix].glyph = unk; + pango_font_get_glyph_extents (font, unk, NULL, &logical_rect); + glyphs->glyphs[ng+glyphix].geometry.width = logical_rect.width; + glyphs->glyphs[ng+glyphix].geometry.x_offset = 0; + glyphs->glyphs[ng+glyphix].geometry.y_offset = 0; + } + } + } + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + { + g_print (" Pango log_clusters (level:%d), char index:", analysis->level); + for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) + g_print ("%d ", glyphs->log_clusters[glyphix]); + g_print ("\n"); + } +#endif + + return TRUE; +} + +static gboolean +uniscribe_shape (PangoFont *font, + const char *text, + gint length, + const PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + wchar_t *wtext; + long wlen; + gboolean retval = TRUE; + + if (!pango_win32_font_select_font (font, hdc)) + return FALSE; + + wtext = g_utf8_to_utf16 (text, length, NULL, &wlen, NULL); + if (wtext == NULL) + retval = FALSE; + + if (retval) + { + retval = itemize_shape_and_place (font, hdc, wtext, wlen, analysis, glyphs); + } + + if (retval) + { + convert_log_clusters_to_byte_offsets (text, length, glyphs, wlen); +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + { + int glyphix; + + g_print (" Pango log_clusters, byte offsets:"); + for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) + g_print ("%d ", glyphs->log_clusters[glyphix]); + g_print ("\n"); + } +#endif + } + + pango_win32_font_done_font (font); + + g_free (wtext); + + return retval && glyphs->num_glyphs > 0; +} + +static gboolean +text_is_simple (const char *text, + gint length) +{ + gboolean retval; + wchar_t *wtext; + long wlen; + + wtext = (wchar_t *) g_utf8_to_utf16 (text, length, NULL, &wlen, NULL); + if (wtext == NULL) + return TRUE; + + retval = (ScriptIsComplex (wtext, wlen, SIC_COMPLEX) == S_FALSE); + + g_free (wtext); + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + g_print ("text_is_simple: %.*s (%ld wchar_t): %s\n", + MIN (length, 10), text, wlen, retval ? "YES" : "NO"); +#endif + + return retval; +} + +static void +basic_engine_shape (PangoEngineShape *engine, + PangoFont *font, + const char *text, + unsigned int length, + const PangoAnalysis *analysis, + PangoGlyphString *glyphs, + const char *paragraph_text G_GNUC_UNUSED, + unsigned int paragraph_length G_GNUC_UNUSED) +{ + int n_chars; + int i; + const char *p; + + g_return_if_fail (font != NULL); + g_return_if_fail (text != NULL); + g_return_if_fail (length >= 0); + g_return_if_fail (analysis != NULL); + + if (!text_is_simple (text, length) && + uniscribe_shape (font, text, length, analysis, glyphs)) + return; + + n_chars = g_utf8_strlen (text, length); + + pango_glyph_string_set_size (glyphs, n_chars); + + p = text; + for (i = 0; i < n_chars; i++) + { + gunichar wc; + gunichar mirrored_ch; + PangoGlyph index; + + wc = g_utf8_get_char (p); + + if (analysis->level % 2) + if (g_unichar_get_mirror_char (wc, &mirrored_ch)) + wc = mirrored_ch; + + if (wc == 0xa0) /* non-break-space */ + wc = 0x20; + + if (pango_is_zero_width (wc)) + { + set_glyph (font, glyphs, i, p - text, PANGO_GLYPH_EMPTY); + } + else + { + index = find_char (font, wc); + if (index) + { + set_glyph (font, glyphs, i, p - text, index); + + if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK) + { + if (i > 0) + { + PangoRectangle logical_rect, ink_rect; + + glyphs->glyphs[i].geometry.width = MAX (glyphs->glyphs[i-1].geometry.width, + glyphs->glyphs[i].geometry.width); + glyphs->glyphs[i-1].geometry.width = 0; + glyphs->log_clusters[i] = glyphs->log_clusters[i-1]; + + /* Some heuristics to try to guess how overstrike glyphs are + * done and compensate + */ + /* FIXME: (alex) Is this double call to get_glyph_extents really necessary? */ + pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, &ink_rect, &logical_rect); + if (logical_rect.width == 0 && ink_rect.x == 0) + glyphs->glyphs[i].geometry.x_offset = (glyphs->glyphs[i].geometry.width - ink_rect.width) / 2; + } + } + } + else + set_glyph (font, glyphs, i, p - text, PANGO_GET_UNKNOWN_GLYPH (wc)); + } + + p = g_utf8_next_char (p); + } + + /* Simple bidi support... may have separate modules later */ + + if (analysis->level % 2) + { + int start, end; + + /* Swap all glyphs */ + swap_range (glyphs, 0, n_chars); + + /* Now reorder glyphs within each cluster back to LTR */ + for (start = 0; start < n_chars;) + { + end = start; + while (end < n_chars && + glyphs->log_clusters[end] == glyphs->log_clusters[start]) + end++; + + swap_range (glyphs, start, end); + start = end; + } + } +} + +static void +init_uniscribe (void) +{ +#ifdef BASIC_WIN32_DEBUGGING + ScriptGetProperties (&scripts, &nscripts); +#endif + hdc = pango_win32_get_dc (); +} + +static void +basic_engine_win32_class_init (PangoEngineShapeClass *class) +{ + class->script_shape = basic_engine_shape; +} + +PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineWin32, basic_engine_win32, + basic_engine_win32_class_init, NULL); + +void +PANGO_MODULE_ENTRY(init) (GTypeModule *module) +{ + init_uniscribe (); + + if (pango_win32_get_debug_flag ()) + pango_win32_debug = TRUE; + + basic_engine_win32_register_type (module); +} + +void +PANGO_MODULE_ENTRY(exit) (void) +{ +} + +void +PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines, + int *n_engines) +{ + init_uniscribe (); + + script_engines[0].scripts = basic_scripts; + script_engines[0].n_scripts = G_N_ELEMENTS (basic_scripts); + + /* This is stupid, we rewrite the previous two lines. Not + * going to touch it now. */ + script_engines[0].scripts = uniscribe_scripts; + script_engines[0].n_scripts = G_N_ELEMENTS (uniscribe_scripts); + + *engines = script_engines; + *n_engines = G_N_ELEMENTS (script_engines); +} + +PangoEngine * +PANGO_MODULE_ENTRY(create) (const char *id) +{ + if (!strcmp (id, SCRIPT_ENGINE_NAME)) + return g_object_new (basic_engine_win32_type, NULL); + else + return NULL; +} |