|
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 |
}
|