Blame pango/pangocoretext-shape.c

Packit 0ec9dd
/* Pango
Packit 0ec9dd
 * pangocoretext-shape.c
Packit 0ec9dd
 *
Packit 0ec9dd
 * Copyright (C) 2005 Imendio AB
Packit 0ec9dd
 * Copyright (C) 2010  Kristian Rietveld  <kris@gtk.org>
Packit 0ec9dd
 * Copyright (C) 2012  Kristian Rietveld  <kris@lanedo.com>
Packit 0ec9dd
 *
Packit 0ec9dd
 * This library is free software; you can redistribute it and/or
Packit 0ec9dd
 * modify it under the terms of the GNU Library General Public
Packit 0ec9dd
 * License as published by the Free Software Foundation; either
Packit 0ec9dd
 * version 2 of the License, or (at your option) any later version.
Packit 0ec9dd
 *
Packit 0ec9dd
 * This library is distributed in the hope that it will be useful,
Packit 0ec9dd
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 0ec9dd
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Packit 0ec9dd
 * Library General Public License for more details.
Packit 0ec9dd
 *
Packit 0ec9dd
 * You should have received a copy of the GNU Library General Public
Packit 0ec9dd
 * License along with this library; if not, write to the
Packit 0ec9dd
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Packit 0ec9dd
 * Boston, MA 02111-1307, USA.
Packit 0ec9dd
 */
Packit 0ec9dd
Packit 0ec9dd
#include "config.h"
Packit 0ec9dd
#include <glib.h>
Packit 0ec9dd
#include <string.h>
Packit 0ec9dd
#include <Carbon/Carbon.h>
Packit 0ec9dd
#include "pango-utils.h"
Packit 0ec9dd
#include "pangocoretext-private.h"
Packit 0ec9dd
#include "pango-impl-utils.h"
Packit 0ec9dd
Packit 0ec9dd
#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < 1060
Packit 0ec9dd
CF_INLINE Boolean CFStringIsSurrogateHighCharacter(UniChar character) {
Packit 0ec9dd
    return ((character >= 0xD800UL) && (character <= 0xDBFFUL) ? true : false);
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
CF_INLINE Boolean CFStringIsSurrogateLowCharacter(UniChar character) {
Packit 0ec9dd
    return ((character >= 0xDC00UL) && (character <= 0xDFFFUL) ? true : false);
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
CF_INLINE UTF32Char CFStringGetLongCharacterForSurrogatePair(UniChar surrogateHigh, UniChar surrogateLow) {
Packit 0ec9dd
    return ((surrogateHigh - 0xD800UL) << 10) + (surrogateLow - 0xDC00UL) + 0x0010000UL;
Packit 0ec9dd
}
Packit 0ec9dd
#endif
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
set_glyph (PangoFont        *font,
Packit 0ec9dd
	   PangoGlyphString *glyphs,
Packit 0ec9dd
	   int               i,
Packit 0ec9dd
	   int               offset,
Packit 0ec9dd
	   PangoGlyph        glyph)
Packit 0ec9dd
{
Packit 0ec9dd
  PangoRectangle logical_rect;
Packit 0ec9dd
Packit 0ec9dd
  glyphs->glyphs[i].glyph = glyph;
Packit 0ec9dd
Packit 0ec9dd
  glyphs->glyphs[i].geometry.x_offset = 0;
Packit 0ec9dd
  glyphs->glyphs[i].geometry.y_offset = 0;
Packit 0ec9dd
Packit 0ec9dd
  glyphs->log_clusters[i] = offset;
Packit 0ec9dd
  pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
Packit 0ec9dd
  glyphs->glyphs[i].geometry.width = logical_rect.width;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
Packit 0ec9dd
/* The "RunIterator" helps us to iterate over the array of runs that is obtained from
Packit 0ec9dd
 * the CoreText type setter. Even though Pango considers the string that is passed to
Packit 0ec9dd
 * the shape function a single run, CoreText might consider it to consist out of
Packit 0ec9dd
 * multiple runs. Because of this, we have an interface around the CoreText array of
Packit 0ec9dd
 * runs that works like iterating a single array, which makes our job in the shape
Packit 0ec9dd
 * function easier.
Packit 0ec9dd
 */
Packit 0ec9dd
Packit 0ec9dd
struct RunIterator
Packit 0ec9dd
{
Packit 0ec9dd
  CTLineRef line;
Packit 0ec9dd
  CFStringRef cstr;
Packit 0ec9dd
  CFArrayRef runs;
Packit 0ec9dd
  CFIndex glyph_count;
Packit 0ec9dd
Packit 0ec9dd
  CFIndex total_ct_i;
Packit 0ec9dd
  CFIndex ct_i;
Packit 0ec9dd
Packit 0ec9dd
  CFIndex *chr_idx_lut;
Packit 0ec9dd
Packit 0ec9dd
  int current_run_number;
Packit 0ec9dd
  CTRunRef current_run;
Packit 0ec9dd
  CFIndex *current_indices;
Packit 0ec9dd
  const CGGlyph *current_cgglyphs;
Packit 0ec9dd
  CGGlyph *current_cgglyphs_buffer;
Packit 0ec9dd
  CTRunStatus current_run_status;
Packit 0ec9dd
};
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
run_iterator_free_current_run (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  iter->current_run_number = -1;
Packit 0ec9dd
  iter->current_run = NULL;
Packit 0ec9dd
  iter->current_cgglyphs = NULL;
Packit 0ec9dd
  if (iter->current_cgglyphs_buffer)
Packit 0ec9dd
    free (iter->current_cgglyphs_buffer);
Packit 0ec9dd
  iter->current_cgglyphs_buffer = NULL;
Packit 0ec9dd
  if (iter->current_indices)
Packit 0ec9dd
    free (iter->current_indices);
Packit 0ec9dd
  iter->current_indices = NULL;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
run_iterator_set_current_run (struct RunIterator *iter,
Packit 0ec9dd
                              const int           run_number)
Packit 0ec9dd
{
Packit 0ec9dd
  CFIndex ct_glyph_count;
Packit 0ec9dd
Packit 0ec9dd
  run_iterator_free_current_run (iter);
Packit 0ec9dd
Packit 0ec9dd
  iter->current_run_number = run_number;
Packit 0ec9dd
  iter->current_run = CFArrayGetValueAtIndex (iter->runs, run_number);
Packit 0ec9dd
  ct_glyph_count = CTRunGetGlyphCount (iter->current_run);
Packit 0ec9dd
Packit 0ec9dd
  iter->current_run_status = CTRunGetStatus (iter->current_run);
Packit 0ec9dd
  iter->current_cgglyphs = CTRunGetGlyphsPtr (iter->current_run);
Packit 0ec9dd
  if (!iter->current_cgglyphs)
Packit 0ec9dd
    {
Packit 0ec9dd
      iter->current_cgglyphs_buffer = (CGGlyph *)malloc (sizeof (CGGlyph) * ct_glyph_count);
Packit 0ec9dd
      CTRunGetGlyphs (iter->current_run, CFRangeMake (0, ct_glyph_count),
Packit 0ec9dd
                      iter->current_cgglyphs_buffer);
Packit 0ec9dd
      iter->current_cgglyphs = iter->current_cgglyphs_buffer;
Packit 0ec9dd
    }
Packit 0ec9dd
Packit 0ec9dd
  iter->current_indices = malloc (sizeof (CFIndex) * ct_glyph_count);
Packit 0ec9dd
  CTRunGetStringIndices (iter->current_run, CFRangeMake (0, ct_glyph_count),
Packit 0ec9dd
                         iter->current_indices);
Packit 0ec9dd
Packit 0ec9dd
  iter->ct_i = 0;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static CFIndex
Packit 0ec9dd
run_iterator_get_glyph_count (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  CFIndex accumulator = 0;
Packit 0ec9dd
  CFIndex i;
Packit 0ec9dd
Packit 0ec9dd
  for (i = 0; i < CFArrayGetCount (iter->runs); i++)
Packit 0ec9dd
    accumulator += CTRunGetGlyphCount (CFArrayGetValueAtIndex (iter->runs, i));
Packit 0ec9dd
Packit 0ec9dd
  return accumulator;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
/* This function generates a lookup table to match string indices of glyphs to
Packit 0ec9dd
 * actual unicode character indices. This also takes unicode characters into
Packit 0ec9dd
 * account that are encoded using 2 UTF16 code points in CFStrings. We use the
Packit 0ec9dd
 * unicode character index to match up with the unicode characters in the UTF8
Packit 0ec9dd
 * string provided by Pango.
Packit 0ec9dd
 */
Packit 0ec9dd
static CFIndex *
Packit 0ec9dd
run_iterator_get_chr_idx_lut (CFStringRef cstr)
Packit 0ec9dd
{
Packit 0ec9dd
  CFIndex cstr_length = CFStringGetLength (cstr);
Packit 0ec9dd
  CFIndex *chr_idx_lut = malloc (sizeof (CFIndex) * cstr_length);
Packit 0ec9dd
  CFIndex i;
Packit 0ec9dd
  CFIndex current_value = 0;
Packit 0ec9dd
Packit 0ec9dd
  for (i = 0; i < cstr_length; i++)
Packit 0ec9dd
    {
Packit 0ec9dd
      chr_idx_lut[i] = current_value;
Packit 0ec9dd
Packit 0ec9dd
      if (CFStringIsSurrogateHighCharacter (CFStringGetCharacterAtIndex (cstr, i)) &&
Packit 0ec9dd
          i + 1 < cstr_length &&
Packit 0ec9dd
          CFStringIsSurrogateLowCharacter (CFStringGetCharacterAtIndex (cstr, i + 1)))
Packit 0ec9dd
        continue;
Packit 0ec9dd
Packit 0ec9dd
      current_value++;
Packit 0ec9dd
    }
Packit 0ec9dd
Packit 0ec9dd
  return chr_idx_lut;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
/* These functions are commented out to silence the compiler, but
Packit 0ec9dd
 * kept around because they might be of use when fixing the more
Packit 0ec9dd
 * intricate issues noted in the comment in the function
Packit 0ec9dd
 * pangocoretext_shape() below.
Packit 0ec9dd
 */
Packit 0ec9dd
#if 0
Packit 0ec9dd
static gboolean
Packit 0ec9dd
run_iterator_is_rtl (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  /* Assume run status is equal for all runs? */
Packit 0ec9dd
  CTRunStatus run_status = CTRunGetStatus (CFArrayGetValueAtIndex (iter->runs, 0));
Packit 0ec9dd
Packit 0ec9dd
  return run_status & kCTRunStatusRightToLeft;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static gboolean
Packit 0ec9dd
run_iterator_run_is_non_monotonic (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  CTRunStatus run_status = CTRunGetStatus (iter->current_run);
Packit 0ec9dd
Packit 0ec9dd
  return run_status & kCTRunStatusNonMonotonic;
Packit 0ec9dd
}
Packit 0ec9dd
#endif
Packit 0ec9dd
Packit 0ec9dd
static gunichar
Packit 0ec9dd
run_iterator_get_character (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  UniChar ch = CFStringGetCharacterAtIndex (iter->cstr, iter->current_indices[iter->ct_i]);
Packit 0ec9dd
Packit 0ec9dd
  if (CFStringIsSurrogateHighCharacter (ch) &&
Packit 0ec9dd
      iter->current_indices[iter->ct_i] + 1 < CFStringGetLength (iter->cstr))
Packit 0ec9dd
    {
Packit 0ec9dd
      UniChar ch2 = CFStringGetCharacterAtIndex (iter->cstr, iter->current_indices[iter->ct_i]+1);
Packit 0ec9dd
Packit 0ec9dd
      if (CFStringIsSurrogateLowCharacter (ch2))
Packit 0ec9dd
        return CFStringGetLongCharacterForSurrogatePair (ch, ch2);
Packit 0ec9dd
    }
Packit 0ec9dd
Packit 0ec9dd
  return ch;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static CGGlyph
Packit 0ec9dd
run_iterator_get_cgglyph (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  return iter->current_cgglyphs[iter->ct_i];
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static CFIndex
Packit 0ec9dd
run_iterator_get_index (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  return iter->chr_idx_lut[iter->current_indices[iter->ct_i]];
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static gboolean
Packit 0ec9dd
run_iterator_create (struct RunIterator *iter,
Packit 0ec9dd
                     const char         *text,
Packit 0ec9dd
                     const gint          length,
Packit 0ec9dd
                     CTFontRef           ctfont)
Packit 0ec9dd
{
Packit 0ec9dd
  char *copy;
Packit 0ec9dd
  CFDictionaryRef attributes;
Packit 0ec9dd
  CFAttributedStringRef attstr;
Packit 0ec9dd
Packit 0ec9dd
  CFTypeRef keys[] = {
Packit 0ec9dd
      (CFTypeRef) kCTFontAttributeName
Packit 0ec9dd
  };
Packit 0ec9dd
Packit 0ec9dd
  CFTypeRef values[] = {
Packit 0ec9dd
      ctfont
Packit 0ec9dd
  };
Packit 0ec9dd
Packit 0ec9dd
  /* Initialize RunIterator structure */
Packit 0ec9dd
  iter->current_run_number = -1;
Packit 0ec9dd
  iter->current_run = NULL;
Packit 0ec9dd
  iter->current_indices = NULL;
Packit 0ec9dd
  iter->chr_idx_lut = NULL;
Packit 0ec9dd
  iter->current_cgglyphs = NULL;
Packit 0ec9dd
  iter->current_cgglyphs_buffer = NULL;
Packit 0ec9dd
Packit 0ec9dd
  /* Create CTLine */
Packit 0ec9dd
  attributes = CFDictionaryCreate (kCFAllocatorDefault,
Packit 0ec9dd
                                   (const void **)keys,
Packit 0ec9dd
                                   (const void **)values,
Packit 0ec9dd
                                   1,
Packit 0ec9dd
                                   &kCFCopyStringDictionaryKeyCallBacks,
Packit 0ec9dd
                                   &kCFTypeDictionaryValueCallBacks);
Packit 0ec9dd
Packit 0ec9dd
  copy = g_strndup (text, length + 1);
Packit 0ec9dd
  copy[length] = 0;
Packit 0ec9dd
Packit 0ec9dd
  iter->cstr = CFStringCreateWithCString (kCFAllocatorDefault, copy,
Packit 0ec9dd
                                          kCFStringEncodingUTF8);
Packit 0ec9dd
  g_free (copy);
Packit 0ec9dd
Packit 0ec9dd
  if (!iter->cstr)
Packit 0ec9dd
    /* Creating a CFString can fail if the input string does not
Packit 0ec9dd
     * adhere to the specified encoding (i.e. it contains invalid UTF8).
Packit 0ec9dd
     */
Packit 0ec9dd
    return FALSE;
Packit 0ec9dd
Packit 0ec9dd
  attstr = CFAttributedStringCreate (kCFAllocatorDefault,
Packit 0ec9dd
                                     iter->cstr,
Packit 0ec9dd
                                     attributes);
Packit 0ec9dd
Packit 0ec9dd
  iter->line = CTLineCreateWithAttributedString (attstr);
Packit 0ec9dd
  iter->runs = CTLineGetGlyphRuns (iter->line);
Packit 0ec9dd
Packit 0ec9dd
  CFRelease (attstr);
Packit 0ec9dd
  CFRelease (attributes);
Packit 0ec9dd
Packit 0ec9dd
  iter->chr_idx_lut = run_iterator_get_chr_idx_lut (iter->cstr);
Packit 0ec9dd
Packit 0ec9dd
  iter->total_ct_i = 0;
Packit 0ec9dd
  iter->glyph_count = run_iterator_get_glyph_count (iter);
Packit 0ec9dd
Packit 0ec9dd
  /* If CoreText did not render any glyphs for this string (can happen,
Packit 0ec9dd
   * e.g. a run solely consisting of a BOM), glyph_count will be zero and
Packit 0ec9dd
   * we immediately set the iterator variable to indicate end of glyph list.
Packit 0ec9dd
   */
Packit 0ec9dd
  if (iter->glyph_count > 0)
Packit 0ec9dd
    run_iterator_set_current_run (iter, 0);
Packit 0ec9dd
  else
Packit 0ec9dd
    iter->total_ct_i = -1;
Packit 0ec9dd
Packit 0ec9dd
  return TRUE;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
run_iterator_free (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  run_iterator_free_current_run (iter);
Packit 0ec9dd
Packit 0ec9dd
  free (iter->chr_idx_lut);
Packit 0ec9dd
Packit 0ec9dd
  CFRelease (iter->line);
Packit 0ec9dd
  CFRelease (iter->cstr);
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static gboolean
Packit 0ec9dd
run_iterator_at_end (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  if (iter->total_ct_i == -1)
Packit 0ec9dd
    return TRUE;
Packit 0ec9dd
Packit 0ec9dd
  return FALSE;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
run_iterator_advance (struct RunIterator *iter)
Packit 0ec9dd
{
Packit 0ec9dd
  if (iter->total_ct_i >= iter->glyph_count - 1)
Packit 0ec9dd
    {
Packit 0ec9dd
      run_iterator_free_current_run (iter);
Packit 0ec9dd
      iter->ct_i = iter->total_ct_i = -1;
Packit 0ec9dd
    }
Packit 0ec9dd
  else
Packit 0ec9dd
    {
Packit 0ec9dd
      iter->total_ct_i++;
Packit 0ec9dd
      iter->ct_i++;
Packit 0ec9dd
Packit 0ec9dd
      if (iter->total_ct_i < iter->glyph_count &&
Packit 0ec9dd
          iter->ct_i >= CTRunGetGlyphCount (iter->current_run))
Packit 0ec9dd
        {
Packit 0ec9dd
          iter->current_run_number++;
Packit 0ec9dd
          run_iterator_set_current_run (iter, iter->current_run_number);
Packit 0ec9dd
        }
Packit 0ec9dd
    }
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
Packit 0ec9dd
Packit 0ec9dd
struct GlyphInfo
Packit 0ec9dd
{
Packit 0ec9dd
  CFIndex index;
Packit 0ec9dd
  CGGlyph cgglyph;
Packit 0ec9dd
  gunichar wc;
Packit 0ec9dd
};
Packit 0ec9dd
Packit 0ec9dd
static gint
Packit 0ec9dd
glyph_info_compare_func (gconstpointer a, gconstpointer b)
Packit 0ec9dd
{
Packit 0ec9dd
  const struct GlyphInfo *gi_a = a;
Packit 0ec9dd
  const struct GlyphInfo *gi_b = b;
Packit 0ec9dd
Packit 0ec9dd
  if (gi_a->index < gi_b->index)
Packit 0ec9dd
    return -1;
Packit 0ec9dd
  else if (gi_a->index > gi_b->index)
Packit 0ec9dd
    return 1;
Packit 0ec9dd
  /* else */
Packit 0ec9dd
  return 0;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static void
Packit 0ec9dd
glyph_info_free (gpointer data, gpointer user_data)
Packit 0ec9dd
{
Packit 0ec9dd
  g_slice_free (struct GlyphInfo, data);
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
static GSList *
Packit 0ec9dd
create_core_text_glyph_list (const char *text,
Packit 0ec9dd
                             gint        length,
Packit 0ec9dd
                             CTFontRef   ctfont)
Packit 0ec9dd
{
Packit 0ec9dd
  GSList *glyph_list = NULL;
Packit 0ec9dd
  struct RunIterator riter;
Packit 0ec9dd
Packit 0ec9dd
  if (!run_iterator_create (&riter, text, length, ctfont))
Packit 0ec9dd
    return NULL;
Packit 0ec9dd
Packit 0ec9dd
  while (!run_iterator_at_end (&riter))
Packit 0ec9dd
    {
Packit 0ec9dd
      struct GlyphInfo *gi;
Packit 0ec9dd
Packit 0ec9dd
      gi = g_slice_new (struct GlyphInfo);
Packit 0ec9dd
      gi->index = run_iterator_get_index (&riter);
Packit 0ec9dd
      gi->cgglyph = run_iterator_get_cgglyph (&riter);
Packit 0ec9dd
      gi->wc = run_iterator_get_character (&riter);
Packit 0ec9dd
Packit 0ec9dd
      glyph_list = g_slist_prepend (glyph_list, gi);
Packit 0ec9dd
Packit 0ec9dd
      run_iterator_advance (&riter);
Packit 0ec9dd
    }
Packit 0ec9dd
Packit 0ec9dd
  glyph_list = g_slist_sort (glyph_list, glyph_info_compare_func);
Packit 0ec9dd
Packit 0ec9dd
  run_iterator_free (&riter);
Packit 0ec9dd
Packit 0ec9dd
  return glyph_list;
Packit 0ec9dd
}
Packit 0ec9dd
Packit 0ec9dd
Packit 0ec9dd
void
Packit 0ec9dd
_pango_core_text_shape (PangoFont           *font,
Packit 0ec9dd
			const char          *text,
Packit 0ec9dd
			gint                 length,
Packit 0ec9dd
			const PangoAnalysis *analysis,
Packit 0ec9dd
			PangoGlyphString    *glyphs,
Packit 0ec9dd
			const char          *paragraph_text G_GNUC_UNUSED,
Packit 0ec9dd
			unsigned int         paragraph_length G_GNUC_UNUSED)
Packit 0ec9dd
{
Packit 0ec9dd
  const char *p;
Packit 0ec9dd
  gulong n_chars, gs_i, gs_prev_i;
Packit 0ec9dd
  PangoCoreTextFont *cfont = PANGO_CORE_TEXT_FONT (font);
Packit 0ec9dd
  PangoCoverage *coverage;
Packit 0ec9dd
  GSList *glyph_list;
Packit 0ec9dd
  GSList *glyph_iter;
Packit 0ec9dd
Packit 0ec9dd
  /* We first fully iterate over the glyph sequence generated by CoreText and
Packit 0ec9dd
   * store this into a list, which is sorted after the iteration. We make a pass
Packit 0ec9dd
   * over the sorted linked list to build up the PangoGlyphString.
Packit 0ec9dd
   *
Packit 0ec9dd
   * We have to do this in order to properly handle a bunch of characteristics of the
Packit 0ec9dd
   * glyph sequence generated by the CoreText typesetter:
Packit 0ec9dd
   *   # E.g. zero-width spaces do not end up in the CoreText glyph sequence. We have
Packit 0ec9dd
   *     to manually account for the gap in the character indices.
Packit 0ec9dd
   *   # Sometimes, CoreText generates two glyph for the same character index. We
Packit 0ec9dd
   *     currently handle this "properly" as in we do not crash or corrupt memory,
Packit 0ec9dd
   *     but that's about it.
Packit 0ec9dd
   *   # Due to mismatches in size, the CoreText glyph sequence can either be longer or
Packit 0ec9dd
   *     shorter than the PangoGlyphString. Note that the size of the PangoGlyphString
Packit 0ec9dd
   *     should match the number of characters in "text".
Packit 0ec9dd
   *
Packit 0ec9dd
   * If performance becomes a problem, it is certainly possible to use a faster code
Packit 0ec9dd
   * that only does a single iteration over the string for "simple cases". Simple cases
Packit 0ec9dd
   * could include these that only consist out of one run (simple Latin text), which
Packit 0ec9dd
   * don't have gaps in the glyph sequence and which are monotonically
Packit 0ec9dd
   * increasing/decreasing.
Packit 0ec9dd
   *
Packit 0ec9dd
   * FIXME items for future fixing:
Packit 0ec9dd
   *   # We currently don't bother about LTR, Pango core appears to fix this up for us.
Packit 0ec9dd
   *     (Even when we cared warnings were generated that strings were in the wrong
Packit 0ec9dd
   *     order, this should be investigated).
Packit 0ec9dd
   *   # When CoreText generates two glyphs for one character, only one is stored.
Packit 0ec9dd
   *     This breaks the example strings for e.g. Georgian and Gothic.
Packit 0ec9dd
   */
Packit 0ec9dd
Packit 0ec9dd
  glyph_list = create_core_text_glyph_list (text, length,
Packit 0ec9dd
                                            pango_core_text_font_get_ctfont (cfont));
Packit 0ec9dd
  if (!glyph_list)
Packit 0ec9dd
    return;
Packit 0ec9dd
Packit 0ec9dd
  /* Set up for translation of the glyph list to a PangoGlyphString. */
Packit 0ec9dd
  n_chars = pango_utf8_strlen (text, length);
Packit 0ec9dd
  pango_glyph_string_set_size (glyphs, n_chars);
Packit 0ec9dd
Packit 0ec9dd
  glyph_iter = glyph_list;
Packit 0ec9dd
Packit 0ec9dd
  coverage = pango_font_get_coverage (PANGO_FONT (cfont),
Packit 0ec9dd
                                      analysis->language);
Packit 0ec9dd
Packit 0ec9dd
  /* gs_i is the index into the Pango glyph string. gi is the iterator into
Packit 0ec9dd
   * the (CoreText) glyph list, gi->index is the index into the CFString.
Packit 0ec9dd
   * In matching, we want gs_i and gi->index to match up.
Packit 0ec9dd
   */
Packit 0ec9dd
  for (gs_prev_i = -1, gs_i = 0, p = text; gs_i < n_chars;
Packit 0ec9dd
       gs_prev_i = gs_i, gs_i++, p = g_utf8_next_char (p))
Packit 0ec9dd
    {
Packit 0ec9dd
      struct GlyphInfo *gi = glyph_iter != NULL ? glyph_iter->data : NULL;
Packit 0ec9dd
Packit 0ec9dd
      if (gi == NULL || gi->index > gs_i)
Packit 0ec9dd
        {
Packit 0ec9dd
          /* The glyph string is behind, insert an empty glyph to catch
Packit 0ec9dd
           * up with the CoreText glyph list. This occurs for instance when
Packit 0ec9dd
           * CoreText inserts a ligature that covers two characters.
Packit 0ec9dd
           */
Packit 0ec9dd
          set_glyph (font, glyphs, gs_i, p - text, PANGO_GLYPH_EMPTY);
Packit 0ec9dd
          continue;
Packit 0ec9dd
        }
Packit 0ec9dd
      else if (gi->index < gs_i)
Packit 0ec9dd
        {
Packit 0ec9dd
          /* The CoreText glyph list is behind, fast forward the iterator
Packit 0ec9dd
           * to catch up. This can happen when CoreText emits two glyphs
Packit 0ec9dd
           * for once character, which is (as noted in the FIXME) above
Packit 0ec9dd
           * not handled by us yet.
Packit 0ec9dd
           */
Packit 0ec9dd
          while (gi && gi->index < gs_i)
Packit 0ec9dd
            {
Packit 0ec9dd
              glyph_iter = g_slist_next (glyph_iter);
Packit 0ec9dd
              if (glyph_iter)
Packit 0ec9dd
                gi = glyph_iter->data;
Packit 0ec9dd
              else
Packit 0ec9dd
                gi = NULL;
Packit 0ec9dd
            }
Packit 0ec9dd
        }
Packit 0ec9dd
Packit 0ec9dd
      if (gi != NULL && gi->index == gs_i)
Packit 0ec9dd
        {
Packit 0ec9dd
          gunichar mirrored_ch;
Packit 0ec9dd
          PangoCoverageLevel result;
Packit 0ec9dd
Packit 0ec9dd
          if (analysis->level % 2)
Packit 0ec9dd
            if (g_unichar_get_mirror_char (gi->wc, &mirrored_ch))
Packit 0ec9dd
              gi->wc = mirrored_ch;
Packit 0ec9dd
Packit 0ec9dd
          if (gi->wc == 0xa0)	/* non-break-space */
Packit 0ec9dd
            gi->wc = 0x20;
Packit 0ec9dd
Packit 0ec9dd
          result = pango_coverage_get (coverage, gi->wc);
Packit 0ec9dd
Packit 0ec9dd
          if (result != PANGO_COVERAGE_NONE)
Packit 0ec9dd
            {
Packit 0ec9dd
              set_glyph (font, glyphs, gs_i, p - text, gi->cgglyph);
Packit 0ec9dd
Packit 0ec9dd
              if (g_unichar_type (gi->wc) == G_UNICODE_NON_SPACING_MARK)
Packit 0ec9dd
                {
Packit 0ec9dd
                  if (gi->index > 0)
Packit 0ec9dd
                    {
Packit 0ec9dd
                      PangoRectangle logical_rect, ink_rect;
Packit 0ec9dd
Packit 0ec9dd
                      glyphs->glyphs[gs_i].geometry.width = MAX (glyphs->glyphs[gs_prev_i].geometry.width,
Packit 0ec9dd
                                                                 glyphs->glyphs[gs_i].geometry.width);
Packit 0ec9dd
                      glyphs->glyphs[gs_prev_i].geometry.width = 0;
Packit 0ec9dd
                      glyphs->log_clusters[gs_i] = glyphs->log_clusters[gs_prev_i];
Packit 0ec9dd
Packit 0ec9dd
                      /* Some heuristics to try to guess how overstrike glyphs are
Packit 0ec9dd
                       * done and compensate
Packit 0ec9dd
                       */
Packit 0ec9dd
                      pango_font_get_glyph_extents (font, glyphs->glyphs[gs_i].glyph, &ink_rect, &logical_rect);
Packit 0ec9dd
                      if (logical_rect.width == 0 && ink_rect.x == 0)
Packit 0ec9dd
                        glyphs->glyphs[gs_i].geometry.x_offset = (glyphs->glyphs[gs_i].geometry.width - ink_rect.width) / 2;
Packit 0ec9dd
                    }
Packit 0ec9dd
                }
Packit 0ec9dd
            }
Packit 0ec9dd
          else
Packit 0ec9dd
            set_glyph (font, glyphs, gs_i, p - text, PANGO_GET_UNKNOWN_GLYPH (gi->wc));
Packit 0ec9dd
Packit 0ec9dd
          glyph_iter = g_slist_next (glyph_iter);
Packit 0ec9dd
        }
Packit 0ec9dd
    }
Packit 0ec9dd
Packit 0ec9dd
  pango_coverage_unref (coverage);
Packit 0ec9dd
  g_slist_foreach (glyph_list, glyph_info_free, NULL);
Packit 0ec9dd
  g_slist_free (glyph_list);
Packit 0ec9dd
Packit 0ec9dd
  if (analysis->level & 1)
Packit 0ec9dd
    pango_glyph_string_reverse_range (glyphs, 0, glyphs->num_glyphs);
Packit 0ec9dd
}