static void render_tone (PangoFont *font, gunichar tone, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { int index; index = find_char (font, tone); pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (index) { set_glyph_tone (font, glyphs, *n_glyphs, cluster_offset, index); } else { /* fall back : HTONE1(0x302e) => middle-dot, HTONE2(0x302f) => colon */ index = find_char (font, tone == HTONE1 ? 0x00b7 : 0x003a); if (index) { set_glyph_tone (font, glyphs, *n_glyphs, cluster_offset, index); } else set_glyph (font, glyphs, *n_glyphs, cluster_offset, get_unknown_glyph (font, tone)); } (*n_glyphs)++; }
/* This is a fallback for when we get a tone mark not preceded * by a syllable. */ static void render_isolated_tone (PangoFont *font, gunichar tone, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { #if 0 /* FIXME: what kind of hack is it? it draws dummy glyphs. */ /* Find a base character to render the mark on */ int index = find_char (font, 0x25cc); /* DOTTED CIRCLE */ if (!index) index = find_char (font, 0x25cb); /* WHITE CIRCLE, in KSC-5601 */ if (!index) index = find_char (font, ' '); /* Space */ if (!index) /* Unknown glyph box with 0000 in it */ index = find_char (font, PANGO_GET_UNKNOWN_GLYPH (0)); /* Add the base character */ pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; #endif /* And the tone mark */ render_tone(font, tone, glyphs, n_glyphs, cluster_offset); }
static void render_basic (PangoFont *font, gunichar wc, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { int index; if (wc == 0xa0) /* non-break-space */ wc = 0x20; pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (pango_is_zero_width (wc)) set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GLYPH_EMPTY); else { index = find_char (font, wc); if (index) set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); else set_glyph (font, glyphs, *n_glyphs, cluster_offset, get_unknown_glyph (font, wc)); } (*n_glyphs)++; }
/* This is a fallback for when we get a tone mark not preceded * by a syllable. */ static void render_isolated_tone (PangoFont *font, gunichar tone, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { /* Find a base character to render the mark on */ int index = find_char (font, 0x25cc); /* DOTTED CIRCLE */ if (!index) index = find_char (font, 0x25cb); /* WHITE CIRCLE, in KSC-5601 */ if (!index) index = find_char (font, ' '); /* Space */ if (!index) /* Unknown glyph box with 0000 in it */ index = find_char (font, get_unknown_glyph (font, 0)); /* Add the base character */ pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; /* And the tone mrak */ render_tone(font, tone, glyphs, n_glyphs, cluster_offset); }
bool set_o(char ar[][SIZE], unsigned int x, unsigned int y) { void* ar_p = ar; return set_glyph(ar_p, x, y, 'o'); }
static void render_syllable (PangoFont *font, gunichar *text, int length, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { int n_prev_glyphs = *n_glyphs; int index; gunichar wc, tone; int i, j, composed; if (IS_M (text[length - 1])) { tone = text[length - 1]; length--; } else tone = 0; if (length >= 3 && IS_L_S(text[0]) && IS_V_S(text[1]) && IS_T_S(text[2])) composed = 3; else if (length >= 2 && IS_L_S(text[0]) && IS_V_S(text[1])) composed = 2; else composed = 0; if (composed) { if (composed == 3) wc = S_FROM_LVT(text[0], text[1], text[2]); else wc = S_FROM_LV(text[0], text[1]); index = find_char (font, wc); pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) set_glyph (font, glyphs, *n_glyphs, cluster_offset, get_unknown_glyph (font, wc)); else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; text += composed; length -= composed; } /* Render the remaining text as uncomposed forms as a fallback. */ for (i = 0; i < length; i++) { int jindex; int oldlen; if (text[i] == LFILL || text[i] == VFILL) continue; index = find_char (font, text[i]); if (index) { pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; continue; } /* This font has no glyphs on the Hangul Jamo area! Find a fallback from the Hangul Compatibility Jamo area. */ jindex = text[i] - LBASE; oldlen = *n_glyphs; for (j = 0; j < 3 && (__jamo_to_ksc5601[jindex][j] != 0); j++) { wc = __jamo_to_ksc5601[jindex][j] - KSC_JAMOBASE + UNI_JAMOBASE; index = (wc >= 0x3131) ? find_char (font, wc) : 0; pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) { *n_glyphs = oldlen; pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, get_unknown_glyph (font, text[i])); (*n_glyphs)++; break; } else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; } } if (n_prev_glyphs == *n_glyphs) { index = find_char (font, 0x3164); pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) set_glyph (font, glyphs, *n_glyphs, cluster_offset, get_unknown_glyph (font, index)); else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); glyphs->log_clusters[*n_glyphs] = cluster_offset; (*n_glyphs)++; } if (tone) render_tone(font, tone, glyphs, n_glyphs, cluster_offset); }
unsigned char *gks_ft_get_bitmap(int *x, int *y, int *width, int *height, gks_state_list_t *gkss, const char *text, int length) { FT_Face face; /* font face */ FT_Vector pen; /* glyph position */ FT_BBox bb; /* bounding box */ FT_Vector bearing; /* individual glyph translation */ FT_UInt previous; /* previous glyph index */ FT_Vector spacing; /* amount of additional space between glyphs */ FT_ULong textheight; /* textheight in FreeType convention */ FT_Error error; /* error code */ FT_Matrix rotation; /* text rotation matrix */ FT_UInt size; /* number of pixels of the bitmap */ FT_String *file; /* concatenated font path */ const FT_String *font, *prefix; /* font file name and directory */ FT_UInt *unicode_string; /* unicode text string */ FT_Int halign, valign; /* alignment */ FT_Byte *mono_bitmap = NULL; /* target for rendered text */ FT_Int num_glyphs; /* number of glyphs */ FT_Vector align; FT_Bitmap ftbitmap; FT_UInt codepoint; int i, textfont, dx, dy, value, pos_x, pos_y; unsigned int j, k; double angle; const int windowwidth = *width; const int direction = (gkss->txp <= 3 && gkss->txp >= 0 ? gkss->txp : 0); const FT_Bool vertical = (direction == GKS_K_TEXT_PATH_DOWN || direction == GKS_K_TEXT_PATH_UP); const FT_String *suffix_type1 = ".afm"; if (!init) gks_ft_init(); if (gkss->txal[0] != GKS_K_TEXT_HALIGN_NORMAL) { halign = gkss->txal[0]; } else if (vertical) { halign = GKS_K_TEXT_HALIGN_CENTER; } else if (direction == GKS_K_TEXT_PATH_LEFT) { halign = GKS_K_TEXT_HALIGN_RIGHT; } else { halign = GKS_K_TEXT_HALIGN_LEFT; } valign = gkss->txal[1]; if (valign != GKS_K_TEXT_VALIGN_NORMAL) { valign = gkss->txal[1]; } else { valign = GKS_K_TEXT_VALIGN_BASE; } textfont = abs(gkss->txfont); if (textfont >= 101 && textfont <= 131) textfont -= 100; else if (textfont > 1 && textfont <= 32) textfont = map[textfont - 1]; else textfont = 9; textfont = textfont - 1; font = gks_font_list[textfont]; if (font_face_cache[textfont] == NULL) { prefix = gks_getenv("GKS_FONTPATH"); if (prefix == NULL) { prefix = gks_getenv("GRDIR"); if (prefix == NULL) prefix = GRDIR; } file = (FT_String *) malloc(strlen(prefix) + 7 + strlen(font) + 4 + 1); strcpy(file, prefix); #ifndef _WIN32 strcat(file, "/fonts/"); #else strcat(file, "\\FONTS\\"); #endif strcat(file, font); strcat(file, ".pfb"); error = FT_New_Face(library, file, 0, &face); if (error == FT_Err_Unknown_File_Format) { gks_perror("unknown file format: %s", file); return NULL; } else if (error) { gks_perror("could not open font file: %s", file); return NULL; } if (strcmp(FT_Get_X11_Font_Format(face), "Type 1") == 0) { strcpy(file, prefix); #ifndef _WIN32 strcat(file, "/fonts/"); #else strcat(file, "\\FONTS\\"); #endif strcat(file, font); strcat(file, suffix_type1); FT_Attach_File(face, file); } free(file); font_face_cache[textfont] = face; } else { face = font_face_cache[textfont]; } num_glyphs = length; unicode_string = (FT_UInt *) malloc(length * sizeof(FT_UInt) + 1); if (textfont + 1 == 13) { symbol_to_unicode((FT_Bytes)text, unicode_string, num_glyphs); } else { utf_to_unicode((FT_Bytes)text, unicode_string, &num_glyphs); } textheight = nint(gkss->chh * windowwidth * 64 / caps[textfont]); error = FT_Set_Char_Size(face, nint(textheight * gkss->chxp), textheight, 72, 72); if (error) gks_perror("cannot set text height"); if (gkss->chup[0] != 0.0 || gkss->chup[1] != 0.0) { angle = atan2f(gkss->chup[1], gkss->chup[0]) - M_PI / 2; rotation.xx = nint( cosf(angle) * 0x10000L); rotation.xy = nint(-sinf(angle) * 0x10000L); rotation.yx = nint( sinf(angle) * 0x10000L); rotation.yy = nint( cosf(angle) * 0x10000L); FT_Set_Transform(face, &rotation, NULL); } else { FT_Set_Transform(face, NULL, NULL); } spacing.x = spacing.y = 0; if (gkss->chsp != 0.0) { error = FT_Load_Glyph(face, FT_Get_Char_Index(face, ' '), vertical ? FT_LOAD_VERTICAL_LAYOUT : FT_LOAD_DEFAULT); if (!error) { spacing.x = nint(face->glyph->advance.x * gkss->chsp); spacing.y = nint(face->glyph->advance.y * gkss->chsp); } else { gks_perror("cannot apply character spacing"); } } bb.xMin = bb.yMin = LONG_MAX; bb.xMax = bb.yMax = LONG_MIN; pen.x = pen.y = 0; previous = 0; for (i = 0; i < num_glyphs; i++) { codepoint = unicode_string[direction == GKS_K_TEXT_PATH_LEFT ? (num_glyphs - 1 - i) : i]; error = set_glyph(face, codepoint, &previous, &pen, vertical, &rotation, &bearing, halign); if (error) continue; bb.xMin = ft_min(bb.xMin, pen.x + bearing.x); bb.xMax = ft_max(bb.xMax, pen.x + bearing.x + 64*face->glyph->bitmap.width); bb.yMin = ft_min(bb.yMin, pen.y + bearing.y - 64*face->glyph->bitmap.rows); bb.yMax = ft_max(bb.yMax, pen.y + bearing.y); if (direction == GKS_K_TEXT_PATH_DOWN) { pen.x -= face->glyph->advance.x + spacing.x; pen.y -= face->glyph->advance.y + spacing.y; } else { pen.x += face->glyph->advance.x + spacing.x; pen.y += face->glyph->advance.y + spacing.y; } } *width = (int)((bb.xMax - bb.xMin) / 64); *height = (int)((bb.yMax - bb.yMin) / 64); if (bb.xMax <= bb.xMin || bb.yMax <= bb.yMin) { gks_perror("invalid bitmap size"); free(unicode_string); return NULL; } size = *width * *height; mono_bitmap = (FT_Byte *) safe_realloc(mono_bitmap, size); memset(mono_bitmap, 0, size); pen.x = 0; pen.y = 0; previous = 0; for (i = 0; i < num_glyphs; i++) { bearing.x = bearing.y = 0; codepoint = unicode_string[direction == GKS_K_TEXT_PATH_LEFT ? (num_glyphs - 1 - i) : i]; error = set_glyph(face, codepoint, &previous, &pen, vertical, &rotation, &bearing, halign); if (error) continue; pos_x = ( pen.x + bearing.x - bb.xMin) / 64; pos_y = (-pen.y - bearing.y + bb.yMax) / 64; ftbitmap = face->glyph->bitmap; for (j = 0; j < (unsigned int) ftbitmap.rows; j++) { for (k = 0; k < (unsigned int) ftbitmap.width; k++) { dx = k + pos_x; dy = j + pos_y; value = mono_bitmap[dy * *width + dx]; value += ftbitmap.buffer[j * ftbitmap.pitch + k]; if (value > 255) { value = 255; } mono_bitmap[dy * *width + dx] = value; } } if (direction == GKS_K_TEXT_PATH_DOWN) { pen.x -= face->glyph->advance.x + spacing.x; pen.y -= face->glyph->advance.y + spacing.y; } else { pen.x += face->glyph->advance.x + spacing.x; pen.y += face->glyph->advance.y + spacing.y; } } free(unicode_string); /* Alignment */ if (direction == GKS_K_TEXT_PATH_DOWN) { pen.x += spacing.x; pen.y += spacing.y; } else { pen.x -= spacing.x; pen.y -= spacing.y; } align.x = align.y = 0; if (valign != GKS_K_TEXT_VALIGN_BASE) { align.y = nint(gkss->chh * windowwidth * 64); FT_Vector_Transform(&align, &rotation); if (valign == GKS_K_TEXT_VALIGN_HALF) { align.x = nint(0.5 * align.x); align.y = nint(0.5 * align.y); } else if (valign == GKS_K_TEXT_VALIGN_TOP) { align.x = nint(1.2 * align.x); align.y = nint(1.2 * align.y); } else if (valign == GKS_K_TEXT_VALIGN_BOTTOM) { align.x = nint(-0.2 * align.x); align.y = nint(-0.2 * align.y); } } if (!vertical && halign != GKS_K_TEXT_HALIGN_LEFT) { FT_Vector right; right.x = face->glyph->metrics.width + face->glyph->metrics.horiBearingX; right.y = 0; if (right.x != 0) { FT_Vector_Transform(&right, &rotation); } pen.x += right.x - face->glyph->advance.x; pen.y += right.y - face->glyph->advance.y; if (halign == GKS_K_TEXT_HALIGN_CENTER) { align.x += pen.x / 2; align.y += pen.y / 2; } else if (halign == GKS_K_TEXT_HALIGN_RIGHT) { align.x += pen.x; align.y += pen.y; } } *x += (bb.xMin - align.x) / 64; *y += (bb.yMin - align.y) / 64; return mono_bitmap; }
static void render_syllable (PangoFont *font, const char *str, int length, PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset) { int n_prev_glyphs = *n_glyphs; int index; gunichar wc = 0, tone = 0, text[4]; int i, j, composed = 0; const char *p; /* Normalize it only when the entire sequence is equivalent to a * precomposed syllable. It's usually better than prefix * normalization both for poor-featured fonts and for smart fonts. * I have seen no smart font which can render S+T as a syllable * form. */ if (length == 3 || length == 4) { p = str; text[0] = g_utf8_get_char(p); p = g_utf8_next_char(p); text[1] = g_utf8_get_char(p); p = g_utf8_next_char(p); text[2] = g_utf8_get_char(p); if (length == 4 && !IS_M(g_utf8_get_char(g_utf8_next_char(p)))) goto lvt_out; /* draw the tone mark later */ if (IS_L_S(text[0]) && IS_V_S(text[1]) && IS_T_S(text[2])) { composed = 3; wc = S_FROM_LVT(text[0], text[1], text[2]); str = g_utf8_next_char(p); goto normalize_out; } } lvt_out: if (length == 2 || length == 3) { p = str; text[0] = g_utf8_get_char(p); p = g_utf8_next_char(p); text[1] = g_utf8_get_char(p); if (length == 3 && !IS_M(g_utf8_get_char(g_utf8_next_char(p)))) goto lv_out; /* draw the tone mark later */ if (IS_L_S(text[0]) && IS_V_S(text[1])) { composed = 2; wc = S_FROM_LV(text[0], text[1]); str = g_utf8_next_char(p); } else if (IS_S(text[0] && !S_HAS_T(text[0]) && IS_T_S(text[1]))) { composed = 2; wc = text[0] + (text[1] - TBASE); str = g_utf8_next_char(p); goto normalize_out; } } lv_out: normalize_out: if (composed) { index = find_char (font, wc); pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GET_UNKNOWN_GLYPH (wc)); else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; length -= composed; } /* Render the remaining text as uncomposed forms as a fallback. */ for (i = 0; i < length; i++, str = g_utf8_next_char(str)) { int jindex; int oldlen; wc = g_utf8_get_char(str); if (wc == LFILL || wc == VFILL) continue; if (IS_M(wc)) { tone = wc; break; } if (IS_S(wc)) { oldlen = *n_glyphs; text[0] = L_FROM_S(wc); text[1] = V_FROM_S(wc); if (S_HAS_T(wc)) { text[2] = T_FROM_S(wc); composed = 3; } else composed = 2; for (j = 0; j < composed; j++) { index = find_char (font, text[j]); if (index) { pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; } else goto decompose_cancel; } continue; decompose_cancel: /* The font doesn't have jamos. Cancel it. */ *n_glyphs = oldlen; pango_glyph_string_set_size (glyphs, *n_glyphs); } index = find_char (font, wc); if (index) { pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; continue; } else if (IS_S(wc)) { pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GET_UNKNOWN_GLYPH (wc)); (*n_glyphs)++; continue; } /* This font has no glyphs on the Hangul Jamo area! Find a fallback from the Hangul Compatibility Jamo area. */ jindex = wc - LBASE; oldlen = *n_glyphs; for (j = 0; j < 3 && (__jamo_to_ksc5601[jindex][j] != 0); j++) { wc = __jamo_to_ksc5601[jindex][j] - KSC_JAMOBASE + UNI_JAMOBASE; index = (wc >= 0x3131) ? find_char (font, wc) : 0; pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) { *n_glyphs = oldlen; pango_glyph_string_set_size (glyphs, *n_glyphs + 1); set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GET_UNKNOWN_GLYPH (text[i])); (*n_glyphs)++; break; } else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); (*n_glyphs)++; } } if (n_prev_glyphs == *n_glyphs) { index = find_char (font, 0x3164); /* U+3164 HANGUL FILLER */ pango_glyph_string_set_size (glyphs, *n_glyphs + 1); if (!index) set_glyph (font, glyphs, *n_glyphs, cluster_offset, PANGO_GET_UNKNOWN_GLYPH (index)); else set_glyph (font, glyphs, *n_glyphs, cluster_offset, index); glyphs->log_clusters[*n_glyphs] = cluster_offset; (*n_glyphs)++; } if (tone) render_tone(font, tone, glyphs, n_glyphs, cluster_offset); }
static void basic_engine_shape (PangoEngineShape *engine, PangoFont *font, const char *text, gint length, PangoAnalysis *analysis, PangoGlyphString *glyphs) { 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); #ifdef HAVE_USP10_H if (have_uniscribe && !text_is_simple (text, length) && uniscribe_shape (font, text, length, analysis, glyphs)) return; #endif 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 (pango_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 basic_engine_shape (PangoEngineShape *engine, PangoFont *font, const char *text, gint length, const PangoAnalysis *analysis, PangoGlyphString *glyphs) { const char *p; char *copy; CTLineRef line; CFStringRef cstr; CFDictionaryRef attributes; CFAttributedStringRef attstr; PangoCoreTextFont *cfont = PANGO_CORE_TEXT_FONT (font); PangoCoverage *coverage; CFArrayRef runs; CTRunRef run; CTRunStatus run_status; CFIndex i, glyph_count; const CGGlyph *cgglyphs; CFTypeRef keys[] = { (CFTypeRef) kCTFontAttributeName }; CFTypeRef values[] = { pango_core_text_font_get_ctfont (cfont) }; attributes = CFDictionaryCreate (kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); copy = g_strndup (text, length + 1); copy[length] = 0; cstr = CFStringCreateWithCString (kCFAllocatorDefault, copy, kCFStringEncodingUTF8); g_free (copy); attstr = CFAttributedStringCreate (kCFAllocatorDefault, cstr, attributes); line = CTLineCreateWithAttributedString (attstr); runs = CTLineGetGlyphRuns (line); /* Since Pango divides things into runs already, we assume there is * only a single run in this line. */ run = CFArrayGetValueAtIndex (runs, 0); run_status = CTRunGetStatus (run); glyph_count = CTRunGetGlyphCount (run); cgglyphs = CTRunGetGlyphsPtr (run); p = text; pango_glyph_string_set_size (glyphs, glyph_count); coverage = pango_font_get_coverage (PANGO_FONT (cfont), analysis->language); for (i = 0; i < glyph_count; i++) { CFIndex real_i, prev_i; gunichar wc; gunichar mirrored_ch; wc = g_utf8_get_char (p); if (analysis->level % 2) if (pango_get_mirror_char (wc, &mirrored_ch)) wc = mirrored_ch; if (run_status & kCTRunStatusRightToLeft) { real_i = glyph_count - i - 1; prev_i = real_i + 1; } else { real_i = i; prev_i = real_i - 1; } if (wc == 0xa0) /* non-break-space */ wc = 0x20; if (pango_is_zero_width (wc)) { set_glyph (font, glyphs, real_i, p - text, PANGO_GLYPH_EMPTY); } else { PangoCoverageLevel result; result = pango_coverage_get (coverage, wc); if (result != PANGO_COVERAGE_NONE) { set_glyph (font, glyphs, real_i, p - text, cgglyphs[real_i]); if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK) { if (i > 0) { PangoRectangle logical_rect, ink_rect; glyphs->glyphs[real_i].geometry.width = MAX (glyphs->glyphs[prev_i].geometry.width, glyphs->glyphs[prev_i].geometry.width); glyphs->glyphs[prev_i].geometry.width = 0; glyphs->log_clusters[real_i] = glyphs->log_clusters[prev_i]; /* Some heuristics to try to guess how overstrike glyphs are * done and compensate */ pango_font_get_glyph_extents (font, glyphs->glyphs[real_i].glyph, &ink_rect, &logical_rect); if (logical_rect.width == 0 && ink_rect.x == 0) glyphs->glyphs[real_i].geometry.x_offset = (glyphs->glyphs[real_i].geometry.width - ink_rect.width) / 2; } } } else { set_glyph (font, glyphs, real_i, p - text, PANGO_GET_UNKNOWN_GLYPH (wc)); } } p = g_utf8_next_char (p); } CFRelease (line); CFRelease (attstr); CFRelease (cstr); CFRelease (attributes); pango_coverage_unref (coverage); }