diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index f442a4e2..b4a1784c 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -19,6 +19,7 @@ CFILES += \ unix/editablecombo.c \ unix/entry.c \ unix/fontbutton.c \ + unix/graphemes.c \ unix/group.c \ unix/label.c \ unix/main.c \ diff --git a/unix/drawtext.c b/unix/drawtext.c index 86932191..192e1327 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -173,34 +173,22 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri // note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure struct uiDrawTextLayout { char *s; - ptrdiff_t *charsToBytes; + PangoGlyphString *glyphString; PangoFont *defaultFont; double width; PangoAttrList *attrs; }; -static ptrdiff_t *computeCharsToBytes(const char *s) -{ - ptrdiff_t *charsToBytes; - glong i, charlen; - - // we INCLUDE the null terminator as a character in the string - // g_utf8_offset_to_pointer() doesn't stop on null terminator so this should work - charlen = g_utf8_strlen(s, -1) + 1; - charsToBytes = (ptrdiff_t *) uiAlloc(charlen * sizeof (ptrdiff_t), "ptrdiff_t[]"); - // TODO speed this up by not needing to scan the whole string again - for (i = 0; i < charlen; i++) - charsToBytes[i] = g_utf8_offset_to_pointer(s, i) - s; - return charsToBytes; -} - uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) { uiDrawTextLayout *layout; + PangoContext *context; layout = uiNew(uiDrawTextLayout); layout->s = g_strdup(text); - layout->charsToBytes = computeCharsToBytes(layout->s); + context = mkGenericPangoCairoContext(); + layout->glyphString = graphemes(layout->s, context); + g_object_unref(context); layout->defaultFont = defaultFont->f; g_object_ref(layout->defaultFont); // retain a copy uiDrawTextLayoutSetWidth(layout, width); @@ -212,7 +200,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) { pango_attr_list_unref(layout->attrs); g_object_unref(layout->defaultFont); - uiFree(layout->charsToBytes); + pango_glyph_string_free(layout->glyphString); g_free(layout->s); uiFree(layout); } @@ -279,8 +267,8 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar) { - attr->start_index = layout->charsToBytes[startChar]; - attr->end_index = layout->charsToBytes[endChar]; + attr->start_index = layout->glyphString->log_clusters[startChar]; + attr->end_index = layout->glyphString->log_clusters[endChar]; pango_attr_list_insert(layout->attrs, attr); // pango_attr_list_insert() takes attr; we don't free it } diff --git a/unix/graphemes.c b/unix/graphemes.c new file mode 100644 index 00000000..a4cf0cc5 --- /dev/null +++ b/unix/graphemes.c @@ -0,0 +1,41 @@ +// 25 may 2016 +#include "uipriv_unix.H" + +// TODO this breaks for non-BMP characters? + +struct gparam { + const char *text; + PangoGlyphString *glyphString; +}; + +static void runItem(gpointer it, gpointer data) +{ + PangoItem *item = (PangoItem *) it; + struct gparam *gparam = (struct gparam *) data; + + pango_shape(gparam->text + item->offset, + item->length, + &(item->analysis), + gparam->glyphString); + pango_item_free(item); +} + +PangoGlyphString *graphemes(const char *text, PangoContext *context) +{ + size_t len; + GList *list; + struct gparam gparam; + + len = strlen(text); + list = pango_itemize(context, + text, 0, len, + NULL, NULL); + + gparam.text = text; + gparam.glyphString = pango_glyph_string_new(); + + g_list_foreach(list, runItem, &gparam); + + g_list_free(list); + return gparam.glyphString; +} diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 67e48a85..f85874be 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -7,6 +7,7 @@ #include #include // see drawtext.c #include +#include #include "../ui.h" #include "../ui_unix.h" #include "../common/uipriv.h" @@ -42,5 +43,10 @@ extern void childSetMargined(struct child *c, int margined); // draw.c extern uiDrawContext *newContext(cairo_t *); extern void freeContext(uiDrawContext *); + +// drawtext.c extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); + +// graphemes.c +extern PangoGlyphString *graphemes(const char *text, PangoContext *context); diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 8ca7ad97..905d6597 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -28,7 +28,7 @@ CXXFILES += \ windows/events.cpp \ windows/fontbutton.cpp \ windows/fontdialog.cpp \ - windows/grapheme.cpp \ + windows/graphemes.cpp \ windows/group.cpp \ windows/init.cpp \ windows/label.cpp \ diff --git a/windows/grapheme.cpp b/windows/graphemes.cpp similarity index 100% rename from windows/grapheme.cpp rename to windows/graphemes.cpp diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index abf72943..3e6a74c1 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -140,7 +140,7 @@ extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); // sizing.cpp extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); -// grapheme.cpp +// graphemes.cpp extern size_t *graphemes(WCHAR *msg);